Spaces:
Sleeping
Sleeping
Download app.py from robertusdanan/autoclipper-api: direct link, hf CLI and curl.
- Browser
- Download file 4.79 kB
-
https://huggingface.co/spaces/robertusdanan/autoclipper-api/resolve/main/app.py
- Command line
-
hf download hf://spaces/robertusdanan/autoclipper-api/app.py
-
curl -L -o app.py https://huggingface.co/spaces/robertusdanan/autoclipper-api/resolve/main/app.py
4.79 kB
| import gradio as gr | |
| import os, json, subprocess, textwrap, uuid | |
| import spaces | |
| 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() |