File size: 3,051 Bytes
ec3ccac
e107f19
c01f21e
ec3ccac
 
 
 
e107f19
 
 
 
ec3ccac
 
e107f19
 
 
b471059
e107f19
 
 
 
 
b471059
ec3ccac
e107f19
 
 
 
 
 
 
ec3ccac
e107f19
 
 
 
 
 
 
 
 
 
ec3ccac
e107f19
 
ec3ccac
e107f19
 
 
 
ec3ccac
b471059
 
 
 
 
 
 
e107f19
 
 
 
 
 
 
 
 
b471059
e107f19
 
 
 
 
b471059
e107f19
 
 
b471059
e107f19
 
 
 
ec3ccac
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
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()