aboutsummaryrefslogtreecommitdiffstats
path: root/main.py
diff options
context:
space:
mode:
Diffstat (limited to 'main.py')
-rw-r--r--main.py91
1 files changed, 86 insertions, 5 deletions
diff --git a/main.py b/main.py
index be9a742..dffe4b3 100644
--- a/main.py
+++ b/main.py
@@ -1,9 +1,12 @@
import argparse
+import subprocess
from pathlib import Path
from typing import Iterator
from tqdm import tqdm
import syncedlyrics
from mutagen.flac import FLAC
+import ffmpeg
+
def iter_files(base: Path) -> Iterator[Path]:
iterator = base.rglob("*")
@@ -11,17 +14,73 @@ def iter_files(base: Path) -> Iterator[Path]:
if p.is_file():
yield p.resolve()
+
def rename_file(filepath: Path, new_name: str) -> None:
target = filepath.with_name(new_name)
filepath.rename(target)
+def get_audio_issues(path: Path) -> dict:
+ audio = FLAC(str(path))
+ info = audio.info
+ sample_rate = getattr(info, "sample_rate", 0)
+ bits_per_sample = getattr(info, "bits_per_sample", 24)
+ max_blocksize = getattr(info, "max_blocksize", 4096)
+
+ return {
+ "needs_sample_rate_fix": sample_rate > 192000,
+ "needs_bitdepth_fix": bits_per_sample > 24,
+ "needs_blocksize_fix": max_blocksize != 4096,
+ "sample_rate": sample_rate,
+ "bits_per_sample": bits_per_sample,
+ "max_blocksize": max_blocksize,
+ }
+
+
+def fix_with_ffmpeg(path: Path, fix_sample_rate: bool, fix_bitdepth: bool) -> Path:
+ output_kwargs = {
+ "acodec": "flac",
+ "map": "0:a",
+ }
+ if fix_bitdepth:
+ output_kwargs["sample_fmt"] = "s24"
+ if fix_sample_rate:
+ output_kwargs["ar"] = "192000"
+
+ temp_path = path.with_suffix(".tmp.flac")
+ (
+ ffmpeg
+ .input(str(path))
+ .output(str(temp_path), **output_kwargs)
+ .overwrite_output()
+ .run(quiet=True)
+ )
+ path.unlink()
+ temp_path.rename(path)
+ return path
+
+
+def fix_blocksize(path: Path, blocksize: int = 4096) -> Path:
+ temp_path = path.with_suffix(".tmp.flac")
+ command = ["flac", "--force", f"--blocksize={blocksize}", str(path), "-o", str(temp_path)]
+ try:
+ subprocess.run(command, capture_output=True, text=True, check=True)
+ path.unlink()
+ temp_path.rename(path)
+ except subprocess.CalledProcessError as e:
+ temp_path.unlink(missing_ok=True)
+ print(f" Error fixing blocksize for {path.name}:")
+ print(e.stderr)
+ return path
+
+
def get_track_info(path: Path) -> tuple:
audio = FLAC(str(path))
title = audio.get("TITLE", [""])
artist = audio.get("ARTIST", [""])
return (title[0], artist[0])
+
def resize_album_art(path: Path) -> None:
audio = FLAC(str(path))
if not audio.pictures:
@@ -37,24 +96,45 @@ def resize_album_art(path: Path) -> None:
pic.data = out.getvalue()
audio.save()
+
def main():
parser = argparse.ArgumentParser()
parser.add_argument("base_dir", type=Path)
- parser.add_argument("--nolrc", "-n", action="store_true", dest="flag")
+ parser.add_argument("--nolrc", "-n", action="store_true", dest="nolrc")
args = parser.parse_args()
base = args.base_dir
files = [p for p in iter_files(base) if p.suffix == ".flac"]
for fp in tqdm(files, desc="Processing FLAC files", unit="file"):
- print("Fetching track info and renaming file")
+ print(f"\nProcessing: {fp.name}")
title, artist = get_track_info(fp)
new_file_name = title + ".flac"
rename_file(fp, new_file_name)
fp = fp.with_name(new_file_name)
- print("Resizing album art to 500x500")
+ issues = get_audio_issues(fp)
+ print(f" Stats: {issues['sample_rate']}Hz, {issues['bits_per_sample']}-bit, blocksize={issues['max_blocksize']}")
+
+ if issues["needs_sample_rate_fix"] or issues["needs_bitdepth_fix"]:
+ reasons = []
+ if issues["needs_sample_rate_fix"]:
+ reasons.append(f"sample rate {issues['sample_rate']}Hz -> 192000Hz")
+ if issues["needs_bitdepth_fix"]:
+ reasons.append(f"bit depth {issues['bits_per_sample']}-bit -> 24-bit")
+ print(f" Fixing via ffmpeg: {', '.join(reasons)}")
+ fp = fix_with_ffmpeg(fp, issues["needs_sample_rate_fix"], issues["needs_bitdepth_fix"])
+
+ post_info = FLAC(str(fp)).info
+ if getattr(post_info, "max_blocksize", 4096) != 4096:
+ issues["needs_blocksize_fix"] = True
+
+ if issues["needs_blocksize_fix"]:
+ print(f" Fixing blocksize -> 4096 via flac CLI")
+ fp = fix_blocksize(fp)
+
+ print(" Resizing album art to 500x500")
resize_album_art(fp)
if args.nolrc:
@@ -62,14 +142,15 @@ def main():
lrc_path = fp.with_suffix(".lrc")
if lrc_path.exists():
- print("Skipping", lrc_path, "as LRC already exists")
+ print(f" Skipping LRC for {fp.name} (already exists)")
continue
- print(f"Fetching LRC file for {title} {artist}")
+ print(f" Fetching LRC for: {title} - {artist}")
lrc = syncedlyrics.search(f"{title} {artist}", providers=["Lrclib", "Megalobiz", "NetEase"])
with open(lrc_path, "w", encoding="utf-8") as f:
f.write(lrc if lrc else "")
+
if __name__ == "__main__":
main()
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage