1
0
Fork 0
music-transcoder/music_transcoder/__main__.py
2025-10-19 14:20:59 +02:00

107 lines
2.7 KiB
Python

from dataclasses import dataclass
import click
import os
import subprocess
INPUT_FILE_EXTS_OPUSENC = [
".flac",
".wav",
".aiff",
]
INPUT_FILE_EXTS_COPY = [
".opus",
".ogg", # this can also contain FLAC (fry about it)
".mp3",
".m4a",
]
def get_opusenc_args(
input_path: str,
output_path: str,
bitrate: int = 192,
) -> list[str]:
return [
"opusenc",
"--bitrate",
str(bitrate),
"--music",
input_path,
output_path,
]
@dataclass
class InputFile:
filepath: str
relpath: str
relpath_noext: str
transcode: bool
@click.option(
"--input-dir",
"-i",
type=click.Path(exists=True, dir_okay=True, file_okay=False, resolve_path=True),
)
@click.option(
"--output-dir",
"-o",
type=click.Path(
exists=False, dir_okay=True, file_okay=False, writable=True, resolve_path=True
),
)
@click.command()
def music_transcoder(input_dir, output_dir):
input_files: list[InputFile] = []
for dirpath, _, filenames in os.walk(input_dir):
for filename in filenames:
_, ext = os.path.splitext(filename)
transcode: bool
if ext in INPUT_FILE_EXTS_OPUSENC:
transcode = True
elif ext in INPUT_FILE_EXTS_COPY:
transcode = False
else:
print("[WARN] rejecting file:", dirpath, filename)
continue
filepath = os.path.join(dirpath, filename)
relpath = os.path.relpath(filepath, input_dir)
relpath_noext, _ = os.path.splitext(relpath)
input_files.append(
InputFile(
filepath=filepath,
relpath=relpath,
relpath_noext=relpath_noext,
transcode=transcode,
)
)
for input_file in input_files:
if input_file.transcode:
output_path = os.path.join(output_dir, input_file.relpath_noext + ".opus")
else:
output_path = os.path.join(output_dir, input_file.relpath)
if os.path.exists(output_path):
print("[WARN] skipping existing file", output_path)
continue
output_path_dir, _ = os.path.split(output_path)
os.makedirs(output_path_dir, exist_ok=True)
if not input_file.transcode:
print("[INFO] hardlinking lossy file →", output_path)
os.link(input_file.filepath, output_path)
continue
print("[INFO] transcoding lossless file →", output_path)
opusenc_args = get_opusenc_args(input_file.filepath, output_path)
subprocess.run(opusenc_args, check=True)
if __name__ == "__main__":
music_transcoder()