seawolf2357 commited on
Commit
9d6653b
Β·
verified Β·
1 Parent(s): 3c15f3d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +84 -54
app.py CHANGED
@@ -620,31 +620,8 @@ if(!S.clips.length){{alert('클립을 μΆ”κ°€ν•˜μ„Έμš”');return}}
620
  S.cancelled=false;
621
  document.getElementById('exportModal').style.display='flex';
622
  document.getElementById('exportBar').style.width='0%';
623
- document.getElementById('exportMsg').textContent='FFmpeg λ‘œλ”© 쀑...';
624
- loadFFmpeg().then(doExport).catch(function(e){{
625
- document.getElementById('exportMsg').textContent='FFmpeg λ‘œλ“œ μ‹€νŒ¨: '+e.message;
626
- }});
627
- }}
628
-
629
- var ffmpeg=null;
630
- async function loadFFmpeg(){{
631
- if(ffmpeg)return;
632
- var script=document.createElement('script');
633
- script.src='https://cdn.jsdelivr.net/npm/@ffmpeg/ffmpeg@0.12.6/dist/umd/ffmpeg.min.js';
634
- document.head.appendChild(script);
635
- await new Promise(function(resolve){{script.onload=resolve}});
636
- var script2=document.createElement('script');
637
- script2.src='https://cdn.jsdelivr.net/npm/@ffmpeg/util@0.12.1/dist/umd/index.min.js';
638
- document.head.appendChild(script2);
639
- await new Promise(function(resolve){{script2.onload=resolve}});
640
- ffmpeg=new FFmpegWASM.FFmpeg();
641
- ffmpeg.on('progress',function(p){{
642
- if(p.progress)document.getElementById('exportMsg').textContent='λ³€ν™˜ 쀑... '+Math.round(p.progress*100)+'%';
643
- }});
644
- await ffmpeg.load({{
645
- coreURL:'https://cdn.jsdelivr.net/npm/@ffmpeg/core@0.12.4/dist/umd/ffmpeg-core.js',
646
- wasmURL:'https://cdn.jsdelivr.net/npm/@ffmpeg/core@0.12.4/dist/umd/ffmpeg-core.wasm'
647
- }});
648
  }}
649
 
650
  async function doExport(){{
@@ -669,7 +646,7 @@ if(t>=dur){{
669
  setTimeout(function(){{rec.stop();setTimeout(resolve,300)}},200);
670
  return;
671
  }}
672
- document.getElementById('exportBar').style.width=(t/dur*50)+'%';
673
  document.getElementById('exportMsg').textContent='λ…Ήν™” 쀑... '+Math.round(t/dur*100)+'%';
674
  ctx.fillStyle='#000';
675
  ctx.fillRect(0,0,1280,720);
@@ -694,30 +671,36 @@ requestAnimationFrame(render);
694
  if(S.cancelled)return;
695
  var webmBlob=new Blob(chunks,{{type:'video/webm'}});
696
  if(webmBlob.size<1000){{document.getElementById('exportMsg').textContent='λ…Ήν™” μ‹€νŒ¨';return}}
697
- document.getElementById('exportBar').style.width='50%';
698
- document.getElementById('exportMsg').textContent='MP4 λ³€ν™˜ 쀑...';
699
- try{{
700
- var webmData=new Uint8Array(await webmBlob.arrayBuffer());
701
- await ffmpeg.writeFile('input.webm',webmData);
702
- await ffmpeg.exec(['-i','input.webm','-c:v','libx264','-preset','fast','-crf','23','-c:a','aac','-movflags','+faststart','output.mp4']);
703
- var mp4Data=await ffmpeg.readFile('output.mp4');
704
- var mp4Blob=new Blob([mp4Data],{{type:'video/mp4'}});
705
  document.getElementById('exportBar').style.width='100%';
706
- document.getElementById('exportMsg').textContent='μ™„λ£Œ! ('+Math.round(mp4Blob.size/1024)+'KB)';
707
- var a=document.createElement('a');
708
- a.href=URL.createObjectURL(mp4Blob);
709
- a.download='video_'+Date.now()+'.mp4';
710
- a.click();
711
- await ffmpeg.deleteFile('input.webm');
712
- await ffmpeg.deleteFile('output.mp4');
713
- }}catch(e){{
714
- console.error(e);
715
- document.getElementById('exportMsg').textContent='MP4 λ³€ν™˜ μ‹€νŒ¨, WebM으둜 λ‹€μš΄λ‘œλ“œ';
716
- var a=document.createElement('a');
717
- a.href=URL.createObjectURL(webmBlob);
718
- a.download='video_'+Date.now()+'.webm';
719
- a.click();
720
- }}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
721
  }}
722
 
723
  function cancelExport(){{S.cancelled=true;document.getElementById('exportModal').style.display='none'}}
@@ -861,6 +844,53 @@ def export_mp4(export_json):
861
  print(f"[Export] Error: {e}")
862
  return None
863
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
864
  with gr.Blocks() as demo:
865
  gr.Markdown("## 🎬 Video Editor")
866
 
@@ -868,17 +898,17 @@ with gr.Blocks() as demo:
868
  e = gr.HTML(value=make_iframe([]))
869
 
870
  gr.Markdown("---")
871
- gr.Markdown("### πŸ“₯ μ„œλ²„ MP4 내보내기 (선택사항)")
872
- gr.Markdown("에디터 λ‚΄ '내보내기' λ²„νŠΌμ΄ μ•ˆλ  λ•Œ μ‚¬μš©. νƒ€μž„λΌμΈ JSON을 λΆ™μ—¬λ„£μœΌμ„Έμš”.")
873
 
874
  with gr.Row():
875
- export_data = gr.Textbox(label="νƒ€μž„λΌμΈ JSON", placeholder='{"clips":[...]}', lines=2, scale=4)
876
- export_btn = gr.Button("🎬 MP4 생성", variant="primary", scale=1)
877
 
878
  mp4_output = gr.File(label="πŸ“₯ MP4 λ‹€μš΄λ‘œλ“œ")
879
 
880
  f.change(fn=lambda x: make_iframe(process_file(x)), inputs=[f], outputs=[e])
881
- export_btn.click(fn=export_mp4, inputs=[export_data], outputs=[mp4_output])
882
 
883
  if __name__ == "__main__":
884
  demo.launch()
 
620
  S.cancelled=false;
621
  document.getElementById('exportModal').style.display='flex';
622
  document.getElementById('exportBar').style.width='0%';
623
+ document.getElementById('exportMsg').textContent='λ…Ήν™” μ€€λΉ„ 쀑...';
624
+ doExport();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
625
  }}
626
 
627
  async function doExport(){{
 
646
  setTimeout(function(){{rec.stop();setTimeout(resolve,300)}},200);
647
  return;
648
  }}
649
+ document.getElementById('exportBar').style.width=(t/dur*100)+'%';
650
  document.getElementById('exportMsg').textContent='λ…Ήν™” 쀑... '+Math.round(t/dur*100)+'%';
651
  ctx.fillStyle='#000';
652
  ctx.fillRect(0,0,1280,720);
 
671
  if(S.cancelled)return;
672
  var webmBlob=new Blob(chunks,{{type:'video/webm'}});
673
  if(webmBlob.size<1000){{document.getElementById('exportMsg').textContent='λ…Ήν™” μ‹€νŒ¨';return}}
 
 
 
 
 
 
 
 
674
  document.getElementById('exportBar').style.width='100%';
675
+ document.getElementById('exportMsg').textContent='μ™„λ£Œ! ('+Math.round(webmBlob.size/1024)+'KB) - μ•„λž˜μ—μ„œ λ‹€μš΄λ‘œλ“œν•˜μ„Έμš”';
676
+ // WebM λ‹€μš΄λ‘œλ“œ 링크 생성
677
+ var downloadDiv=document.createElement('div');
678
+ downloadDiv.style.marginTop='10px';
679
+ var webmLink=document.createElement('a');
680
+ webmLink.href=URL.createObjectURL(webmBlob);
681
+ webmLink.download='video_'+Date.now()+'.webm';
682
+ webmLink.className='btn btn-success';
683
+ webmLink.textContent='πŸ“₯ WebM λ‹€μš΄λ‘œλ“œ';
684
+ webmLink.style.marginRight='5px';
685
+ downloadDiv.appendChild(webmLink);
686
+ // base64 볡사 λ²„νŠΌ
687
+ var copyBtn=document.createElement('button');
688
+ copyBtn.className='btn btn-secondary';
689
+ copyBtn.textContent='πŸ“‹ MP4λ³€ν™˜μš© 볡사';
690
+ copyBtn.onclick=async function(){{
691
+ var reader=new FileReader();
692
+ reader.onload=function(){{
693
+ var base64=reader.result.split(',')[1];
694
+ navigator.clipboard.writeText(base64).then(function(){{
695
+ alert('볡사 μ™„λ£Œ!\\n\\nμœ„ Gradio의 "WebM 데이터" μž…λ ₯λž€μ— λΆ™μ—¬λ„£κΈ°(Ctrl+V) ν›„\\n"MP4 λ³€ν™˜" λ²„νŠΌμ„ ν΄λ¦­ν•˜μ„Έμš”.');
696
+ }}).catch(function(){{
697
+ prompt('μ•„λž˜ 데이터λ₯Ό λ³΅μ‚¬ν•˜μ„Έμš” (Ctrl+A, Ctrl+C):', base64);
698
+ }});
699
+ }};
700
+ reader.readAsDataURL(webmBlob);
701
+ }};
702
+ downloadDiv.appendChild(copyBtn);
703
+ document.querySelector('.modal-box').appendChild(downloadDiv);
704
  }}
705
 
706
  function cancelExport(){{S.cancelled=true;document.getElementById('exportModal').style.display='none'}}
 
844
  print(f"[Export] Error: {e}")
845
  return None
846
 
847
+ def convert_webm_to_mp4(webm_base64):
848
+ """WebM base64 데이터λ₯Ό MP4둜 λ³€ν™˜"""
849
+ if not webm_base64 or len(webm_base64) < 100:
850
+ return None
851
+
852
+ try:
853
+ # base64 λ””μ½”λ”©
854
+ webm_data = base64.b64decode(webm_base64)
855
+
856
+ temp_dir = tempfile.mkdtemp()
857
+ webm_path = os.path.join(temp_dir, 'input.webm')
858
+ mp4_path = os.path.join(temp_dir, f'output_{int(time.time())}.mp4')
859
+
860
+ # WebM 파일 μ €μž₯
861
+ with open(webm_path, 'wb') as f:
862
+ f.write(webm_data)
863
+
864
+ # FFmpeg둜 MP4 λ³€ν™˜
865
+ cmd = [
866
+ 'ffmpeg', '-y',
867
+ '-i', webm_path,
868
+ '-c:v', 'libx264',
869
+ '-preset', 'fast',
870
+ '-crf', '23',
871
+ '-c:a', 'aac',
872
+ '-b:a', '128k',
873
+ '-movflags', '+faststart',
874
+ mp4_path
875
+ ]
876
+
877
+ result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
878
+
879
+ # WebM 파일 μ‚­μ œ
880
+ try:
881
+ os.remove(webm_path)
882
+ except:
883
+ pass
884
+
885
+ if os.path.exists(mp4_path) and os.path.getsize(mp4_path) > 0:
886
+ return mp4_path
887
+
888
+ return None
889
+
890
+ except Exception as e:
891
+ print(f"[Convert] Error: {e}")
892
+ return None
893
+
894
  with gr.Blocks() as demo:
895
  gr.Markdown("## 🎬 Video Editor")
896
 
 
898
  e = gr.HTML(value=make_iframe([]))
899
 
900
  gr.Markdown("---")
901
+ gr.Markdown("### πŸ“₯ MP4 λ³€ν™˜")
902
+ gr.Markdown("μ—λ””ν„°μ—μ„œ '내보내기' β†’ 'MP4λ³€ν™˜μš© 볡사' 클릭 ν›„ μ•„λž˜μ— λΆ™μ—¬λ„£κΈ°")
903
 
904
  with gr.Row():
905
+ webm_data = gr.Textbox(label="WebM 데이터 (base64)", placeholder="여기에 λΆ™μ—¬λ„£κΈ° (Ctrl+V)", lines=2, scale=4)
906
+ convert_btn = gr.Button("🎬 MP4 λ³€ν™˜", variant="primary", scale=1)
907
 
908
  mp4_output = gr.File(label="πŸ“₯ MP4 λ‹€μš΄λ‘œλ“œ")
909
 
910
  f.change(fn=lambda x: make_iframe(process_file(x)), inputs=[f], outputs=[e])
911
+ convert_btn.click(fn=convert_webm_to_mp4, inputs=[webm_data], outputs=[mp4_output])
912
 
913
  if __name__ == "__main__":
914
  demo.launch()