Avi3738 commited on
Commit
139814d
·
verified ·
1 Parent(s): 0ddafb4

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +98 -0
app.py ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import tempfile, os, subprocess, shutil
3
+ from typing import List, Tuple
4
+ from mutagen.id3 import ID3, ID3NoHeaderError, GEOB, TSSE
5
+ from mutagen.mp3 import MP3
6
+
7
+ TARGET_SR = 44100
8
+ TARGET_CH = 1
9
+ TARGET_BR = "128k"
10
+
11
+ def standardize_one(in_path: str, tsse_text: str, geob_bytes: bytes, geob_mime: str, geob_desc: str) -> str:
12
+ base = os.path.splitext(os.path.basename(in_path))[0]
13
+ out_path = os.path.join(tempfile.gettempdir(), f"{base}_standardized.mp3")
14
+
15
+ # 1) Re-encode audio with FFmpeg and enforce container/tag options
16
+ cmd = [
17
+ "ffmpeg", "-y",
18
+ "-i", in_path,
19
+ "-ar", str(TARGET_SR),
20
+ "-ac", str(TARGET_CH),
21
+ "-c:a", "libmp3lame",
22
+ "-b:a", TARGET_BR,
23
+ "-write_xing", "0",
24
+ "-id3v2_version", "4",
25
+ "-write_id3v1", "0",
26
+ "-metadata", f"TSSE={tsse_text or 'HF-Space'}",
27
+ out_path
28
+ ]
29
+ subprocess.run(cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
30
+
31
+ # 2) Ensure ID3v2.4 and add GEOB + TSSE with Mutagen
32
+ try:
33
+ tags = ID3(out_path)
34
+ except ID3NoHeaderError:
35
+ tags = ID3()
36
+
37
+ # Enforce TSSE (software/encoder)
38
+ tags.setall("TSSE", [TSSE(encoding=3, text=[tsse_text or "HF-Space"])])
39
+ # Always add a GEOB frame (either from upload or a tiny placeholder)
40
+ if geob_bytes is None or len(geob_bytes) == 0:
41
+ geob_bytes = b"HF-Space GEOB placeholder"
42
+ geob_mime = geob_mime or "application/octet-stream"
43
+ geob_desc = geob_desc or "asset"
44
+ tags.add(GEOB(encoding=3, mime=geob_mime or "application/octet-stream", desc=geob_desc or "asset", data=geob_bytes))
45
+
46
+ # Save as ID3v2.4 explicitly
47
+ tags.save(out_path, v2_version=4)
48
+
49
+ # Sanity check via Mutagen
50
+ _ = MP3(out_path) # will raise if corrupt
51
+ return out_path
52
+
53
+ def standardize_many(files: List[gr.File], tsse_text: str, geob_file: gr.File, geob_mime: str, geob_desc: str):
54
+ outputs = []
55
+ geob_bytes = None
56
+ if geob_file is not None and hasattr(geob_file, "name") and geob_file.name:
57
+ with open(geob_file.name, "rb") as f:
58
+ geob_bytes = f.read()
59
+
60
+ for f in files or []:
61
+ out = standardize_one(f.name, tsse_text, geob_bytes, geob_mime, geob_desc)
62
+ outputs.append(out)
63
+
64
+ # Optionally zip if multiple
65
+ if len(outputs) > 1:
66
+ zip_path = os.path.join(tempfile.gettempdir(), "standardized_mp3s.zip")
67
+ if os.path.exists(zip_path):
68
+ os.remove(zip_path)
69
+ with tempfile.TemporaryDirectory() as tmpd:
70
+ paths = []
71
+ for p in outputs:
72
+ dst = os.path.join(tmpd, os.path.basename(p))
73
+ shutil.copy2(p, dst)
74
+ paths.append(dst)
75
+ shutil.make_archive(zip_path[:-4], 'zip', tmpd)
76
+ return zip_path
77
+ elif len(outputs) == 1:
78
+ return outputs[0]
79
+ else:
80
+ return None
81
+
82
+ with gr.Blocks(title="MP3 Standardizer (44.1k/mono/128k CBR, no Xing, ID3v2.4, TSSE+GEOB)") as demo:
83
+ gr.Markdown("Upload MP3s to standardize them to 44.1 kHz, mono, 128 kbps CBR, no Xing/Info, ID3v2.4 only, plus TSSE and GEOB tags.")
84
+
85
+ with gr.Row():
86
+ files = gr.File(label="MP3 files", file_types=[".mp3"], file_count="multiple")
87
+ with gr.Accordion("Metadata options", open=False):
88
+ tsse = gr.Textbox(label="TSSE (encoder/software)", value="HF-Space", placeholder="e.g., Lavf60.16.100")
89
+ geob = gr.File(label="GEOB payload (optional)", file_types=[".bin", ".txt", ".json", ".png", ".jpg", ".mp3", ".mp4"], file_count="single")
90
+ geob_mime = gr.Textbox(label="GEOB MIME type", value="application/octet-stream")
91
+ geob_desc = gr.Textbox(label="GEOB description", value="asset")
92
+
93
+ run = gr.Button("Standardize")
94
+ result = gr.File(label="Download standardized file(s)")
95
+ run.click(standardize_many, inputs=[files, tsse, geob, geob_mime, geob_desc], outputs=[result])
96
+
97
+ if __name__ == "__main__":
98
+ demo.launch()