Using GUI:
-`convert.py`
+`converter.py`
Using CLI:
-`convert.py C:\PlaylistDirectory C:\TargetConvertedDirectory`
+`converter.py C:\PlaylistDirectory C:\TargetConvertedDirectory`
-`convert.py --profile aiff --jobs 4 --trim --converter C:\ffmpeg\bin\ffmpeg.exe C:\PlaylistDirectory C:\TargetDirectory`
+`converter.py --profile aiff --jobs 4 --trim --converter C:\ffmpeg\bin\ffmpeg.exe C:\PlaylistDirectory C:\TargetDirectory`
-`convert.py -p flac -j 8 -t -c C:\ffmpeg\bin\ffmpeg.exe C:\PlaylistDirectory C:\TargetDirectory`
+`converter.py -p flac -j 8 -t -c C:\ffmpeg\bin\ffmpeg.exe C:\PlaylistDirectory C:\TargetDirectory`
### Supported library playlist extensions
- **m3u**
- **xspf**
-### Supported extension profiles
+### Supported extension profiles (--profile option)
- **mp3** (default)
- **aiff**
- **m3u**
-### Options
+### Additional options
-- **converter**: Path to music file converter. For example `C:\ffmpeg\bin\ffmpeg.exe`.
-- **trim**: Trim track numbers from file names.
-- **jobs**: Number of parallel tasks to run (Multithreading).
+- **converter**: Path to music file converter. For example `C:\ffmpeg\bin\ffmpeg.exe`. Default: `ffmpeg`.
+- **trim**: Trim track number from output file names. Default: `No`.
+- **jobs**: Number of parallel tasks to run. Default: `1`.
Music files are only replaced if their library version is more recent than their target directory version.
Target directory playlist files are only replaced if their content changed.
-All files are converted with an external transcoder. As of now the only supported one is **ffmpeg**: https://ffmpeg.org/
+All files are converted with an external transcoder. The only supported one is **ffmpeg**: https://ffmpeg.org/
No data is ever written outside the target directory. Transcodes are only performed on lossless files, other files are simply copied.
-#!/usr/bin/env python
+#!/usr/bin/env python3
'''
BSD 3 License
-Copyright 2025 Julien Selamme
+Copyright 2026 Julien Selamme
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
self.converterArgs: list[str] = list()
def init(self, profileName: str) -> bool:
- # -n to not overwrite any existing file.
- # -ar 44100 for 44.1 kHz audio output.
- # -fps_mode vfr to drop frames which cannot be
- # properly contained in the output file.
+ # "-n" to prevent overwriting any existing file.
+ # "-ar 44100" for 44.1 kHz audio output.
+ # "-fps_mode vfr" to drop frames which cannot be
+ # properly contained in the output file.
+ # "-codec:v copy" to copy the integrated image from source
+ # instead of converting it to png (default encoder).
commonFlags: list[str] = ["-hide_banner", "-loglevel", "error",
- "-n", "-ar", "44100", "-fps_mode", "vfr"]
+ "-n", "-ar", "44100", "-fps_mode", "vfr",
+ "-codec:v", "copy"]
- # -sample_fmt s16 for 16 bits audio output.
- # -write_id3v2 1 to write metadata in aiff files.
+ # "-sample_fmt s16" for 16 bits audio output.
+ # "-write_id3v2 1" to write metadata in aiff files.
if profileName == "aiff":
self.name = "aiff"
self.targetExtension = ".aiff"
+ ["-f", "aiff", "-sample_fmt", "s16",
"-write_id3v2", "1"])
return True
+ # "-sample_fmt s16" for 16 bits audio output.
elif profileName == "flac":
self.name = "flac"
self.targetExtension = ".flac"
self.converterArgs = (commonFlags
+ ["-f", "flac", "-sample_fmt", "s16"])
return True
- # -q:a 0 to set mp3 variable bitrate.
+ # "-b:a 320k" for CBR 320 kbps.
elif profileName == "mp3":
self.name = "mp3"
self.targetExtension = ".mp3"
self.convertExtensions = [".aiff", ".flac", ".wav"]
self.copyExtensions = [".mp3"]
self.converterArgs = (commonFlags
- + ["-f", "mp3",
- "-c:a", "libmp3lame", "-q:a", "0"])
+ + ["-f", "mp3", "-b:a", "320k"])
return True
else:
return False