import gradio as gr import os, re, tempfile from typing import List, Tuple from pydub import AudioSegment # ---------- Utils ---------- def ms_to_hhmmss(ms: int) -> str: sec = int(round(ms / 1000.0)) h = sec // 3600 m = (sec % 3600) // 60 s = sec % 60 return f"{h:02d}:{m:02d}:{s:02d}" if h > 0 else f"{m:02d}:{s:02d}" def clean_title(path: str) -> str: base = os.path.splitext(os.path.basename(path))[0] base = re.sub(r"^\s*\d{1,3}[\.\-\)_\s]+", "", base) # hapus “01. ”, “1 - ”, “02_” dll base = re.sub(r"\s{2,}", " ", base).strip() return base or "Untitled" def natural_key(name: str): b = os.path.basename(name) return [int(t) if t.isdigit() else t.lower() for t in re.split(r"(\d+)", b)] # ---------- Core ---------- def build_timecodes_from_paths(paths: List[str]) -> Tuple[str, str]: if not paths: return "Please upload audio files.", None # paths dari gr.File(file_count='multiple', type='filepath') -> List[str] # sort natural agar 01 < 2 < 10 paths = sorted(paths, key=natural_key) starts, titles, cursor = [], [], 0 skipped = [] for p in paths: try: seg = AudioSegment.from_file(p) starts.append(cursor) titles.append(clean_title(p)) cursor += len(seg) except Exception as e: skipped.append(f"{os.path.basename(p)} ({e})") if not starts: return "No readable audio files. Please re-upload.", None lines = [f"{ms_to_hhmmss(ts)} – {t}" for ts, t in zip(starts, titles)] if skipped: lines.append("\n# Skipped files") lines += [f"- {msg}" for msg in skipped] tmpdir = tempfile.mkdtemp() out_path = os.path.join(tmpdir, "timecodes.txt") with open(out_path, "w", encoding="utf-8") as f: f.write("\n".join(lines)) return "\n".join(lines), out_path # ---------- UI ---------- with gr.Blocks(title="Auto Timecode Generator (Batch Tracks)") as demo: gr.Markdown( "## 🎶 Auto Timecode Generator (Batch → YouTube)\n" "Upload beberapa file audio (MP3/WAV/FLAC/M4A). Aplikasi akan:\n" "- Urutkan berdasarkan **nama file** (natural sort)\n" "- Ambil **judul** dari nama file (tanpa nomor & ekstensi)\n" "- Hitung **timestamp kumulatif** berdasarkan durasi tiap file\n" "- Menghasilkan output siap **copy–paste** + **download `timecodes.txt`**" ) files = gr.File( label="Upload multiple audio files", file_count="multiple", # <- penting type="filepath", # <- penting ) btn = gr.Button("Generate Timecodes", variant="primary") out = gr.Textbox(label="Timecodes", lines=22) dl = gr.File(label="Download timecodes.txt") # Auto-generate begitu selesai upload files.upload(fn=build_timecodes_from_paths, inputs=[files], outputs=[out, dl]) # Tombol manual juga ada btn.click(fn=build_timecodes_from_paths, inputs=[files], outputs=[out, dl]) if __name__ == "__main__": demo.launch()