File size: 4,794 Bytes
57b7a6c
5185bc4
e9af821
57b7a6c
692c8a4
e26c737
5185bc4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d695c13
5185bc4
 
 
 
 
d695c13
5185bc4
 
10b3d47
5185bc4
 
 
 
 
 
10b3d47
d695c13
5185bc4
d695c13
 
57b7a6c
 
4d556fd
57b7a6c
5185bc4
 
 
 
 
 
 
 
 
 
d695c13
5185bc4
 
 
 
 
 
 
 
 
d695c13
5185bc4
 
 
 
 
 
 
 
 
 
 
 
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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
import gradio as gr
import os, json, subprocess, textwrap, uuid
import spaces


@spaces.GPU(duration=30)
def render_video(video_file, srt_file, thumb_file, hermes_json, allowed_crop):
    try:
        uid = str(uuid.uuid4())
        wrk = f"/tmp/{uid}"
        os.makedirs(wrk, exist_ok=True)
        out_path = f"{wrk}/final_short.mp4"
        
        data = json.loads(hermes_json)
        clips = data.get("clips", [])
        if not clips: return None

        def pt(ts):
            h,m,s = ts.replace(',','.').split(':')
            return float(h)*3600 + float(m)*60 + float(s)

        def ft(sec):
            h, rem = divmod(sec, 3600)
            m, s = divmod(rem, 60)
            return f"{int(h):02d}:{int(m):02d}:{int(s):02d},{int((s-int(s))*1000):03d}"

        raw_srt = open(srt_file).read().strip().split("\n\n")
        new_srt, srt_idx, run_t, offset = [], 1, 0.0, 1.5
        filt, conc = [], []

        for i, c in enumerate(clips):
            s, e = pt(c["start"]), pt(c["end"])
            dur = e - s
            filt.append(f"[0:v]trim=start={s}:end={e},setpts=PTS-STARTPTS,fps=30[v{i}]; ")
            filt.append(f"[0:a]atrim=start={s}:end={e},asetpts=PTS-STARTPTS,aformat=sample_rates=48000:channel_layouts=stereo[a{i}]; ")
            conc.append(f"[v{i}][a{i}]")

            for blk in raw_srt:
                if not blk.strip(): continue
                lines = blk.strip().split("\n")
                if len(lines) >= 3:
                    ts, te = pt(lines[1].split(" --> ")[0]), pt(lines[1].split(" --> ")[1])
                    if te > s and ts < e:
                        ns, ne = max(ts, s), min(te, e)
                        ns_adj, ne_adj = (ns - s) + run_t + offset, (ne - s) + run_t + offset
                        text = " ".join(lines[2:])
                        words = text.split()
                        dur_sub = ne - ns
                        if dur_sub > 0 and (len(words) / dur_sub) > 6.0: continue
                        max_w = 4
                        chunks = [" ".join(words[j:j+max_w]) for j in range(0, len(words), max_w)]
                        if not chunks: continue
                        c_dur = (ne_adj - ns_adj) / len(chunks)
                        for k, c_txt in enumerate(chunks):
                            c_st, c_en = ns_adj + k * c_dur, ns_adj + (k+1) * c_dur
                            wrp = textwrap.fill(c_txt, width=22)
                            new_srt.append(f"{srt_idx}\n{ft(c_st)} --> {ft(c_en)}\n{wrp}")
                            srt_idx += 1
        run_t += dur

        filt_str = "".join(filt) + "".join(conc) + f"concat=n={len(clips)}:v=1:a=1[raw_vid][raw_aud];\n"
        clip_srt_path = f"{wrk}/clip.srt"
        open(clip_srt_path, "w").write("\n\n".join(new_srt))

        crop_filter = "crop=iw-200:ih:100:0," if allowed_crop == "true" else ""
        
        # PERUBAHAN HANYA DI SINI: Alignment=10 (Center-Top) & MarginV=260 (Turun pas di bawah video utama)
        full_filter = filt_str + f"""[raw_vid]split[raw_bg][raw_fg];
[raw_bg]{crop_filter}scale=1080:1920:force_original_aspect_ratio=increase,crop=1080:1920,boxblur=20:5,setsar=1,fps=30[bg];
[raw_fg]{crop_filter}scale=1080:1920:force_original_aspect_ratio=decrease,crop=1080:1920,setsar=1,fps=30[fg];
[bg][fg]overlay=(W-w)/2:(H-h)/2[mainvid];
[1:v]scale=1080:1920:force_original_aspect_ratio=increase,crop=1080:1920,setsar=1,fps=30[thumbv];
[thumbv][mainvid]concat=n=2:v=1:a=0,subtitles={clip_srt_path}:force_style='Fontsize=15,Bold=1,PrimaryColour=&H00FFFFFF,OutlineColour=&H00000000,BorderStyle=1,Outline=2.5,Shadow=1,Alignment=10,MarginV=260'[withsubs];
[2:a][raw_aud]concat=n=2:v=0:a=1[outa]"""
        
        ff_txt = f"{wrk}/full_filter.txt"
        open(ff_txt, "w").write(full_filter)

        subprocess.run([
            "ffmpeg", "-y", "-i", video_file,
            "-loop", "1", "-t", "1.5", "-i", thumb_file,
            "-f", "lavfi", "-t", "1.5", "-i", "anullsrc=r=48000:cl=stereo",
            "-filter_complex_script", ff_txt,
            "-map", "[withsubs]", "-map", "[outa]",
            "-c:v", "libx264", "-preset", "veryfast", "-crf", "26",
            "-maxrate", "4M", "-bufsize", "8M", "-pix_fmt", "yuv420p",
            "-c:a", "aac", "-b:a", "128k", out_path
        ], check=True)

        return out_path
    except Exception as e:
        print(f"Error: {str(e)}")
        return None

# Antarmuka resmi Gradio
demo = gr.Interface(
    fn=render_video,
    inputs=[
        gr.File(label="Video MP4", type="filepath"),
        gr.File(label="Subtitle SRT", type="filepath"),
        gr.File(label="Thumbnail JPG", type="filepath"),
        gr.Textbox(label="Hermes JSON"),
        gr.Textbox(label="Allowed Crop")
    ],
    outputs=gr.File(label="Output Video MP4")
)
demo.launch()