nangunan commited on
Commit
22db699
Β·
1 Parent(s): 5b7e8f2
Files changed (2) hide show
  1. app.py +108 -0
  2. requirements.txt +6 -0
app.py ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import whisper
3
+ import subprocess
4
+ import os
5
+ import uuid
6
+ import torch
7
+ import re
8
+ import nltk
9
+ from nltk.tokenize import sent_tokenize
10
+ from transformers import pipeline, AutoTokenizer
11
+
12
+ # NLTK λ¦¬μ†ŒμŠ€ λ‹€μš΄λ‘œλ“œ
13
+ nltk.download('punkt')
14
+
15
+ # πŸ”Š Whisper μžλ§‰ 생성 λͺ¨λΈ
16
+ asr_model = whisper.load_model("medium")
17
+
18
+ # 🧠 μš”μ•½ λͺ¨λΈ (mT5 기반 λ‹€κ΅­μ–΄ μš”μ•½ 지원)
19
+ model_name = "csebuetnlp/mT5_multilingual_XLSum"
20
+ tokenizer = AutoTokenizer.from_pretrained(model_name, use_fast=False)
21
+ summarizer = pipeline("summarization", model=model_name, tokenizer=tokenizer, device=0 if torch.cuda.is_available() else -1)
22
+
23
+ # yt-dlp 및 ffmpeg 경둜 μ„€μ •
24
+ yt_dlp_path = "C:/Windows/System32/yt-dlp.exe"
25
+ ffmpeg_path = "C:/ProgramData/chocolatey/bin"
26
+
27
+ # 🎡 μ˜€λ””μ˜€ λ‹€μš΄λ‘œλ“œ ν•¨μˆ˜
28
+ def download_audio_with_ytdlp(youtube_url):
29
+ audio_filename = f"yt_audio_{uuid.uuid4().hex[:8]}.mp3"
30
+ command = [
31
+ yt_dlp_path,
32
+ "-f", "bestaudio",
33
+ "--extract-audio",
34
+ "--audio-format", "mp3",
35
+ "--ffmpeg-location", ffmpeg_path,
36
+ "-o", audio_filename,
37
+ youtube_url
38
+ ]
39
+ try:
40
+ subprocess.run(command, capture_output=True, text=True, check=True)
41
+ if not os.path.exists(audio_filename):
42
+ raise RuntimeError(f"μ˜€λ””μ˜€ 파일 생성 μ‹€νŒ¨: {audio_filename}")
43
+ return audio_filename
44
+ except subprocess.CalledProcessError as e:
45
+ raise RuntimeError(f"yt-dlp μ‹€ν–‰ 였λ₯˜:\n{e.stderr}")
46
+
47
+ # πŸ“„ μžλ§‰ μ •μ œ
48
+ def clean_transcript(raw_text):
49
+ text = re.sub(r'\s+', ' ', raw_text).strip()
50
+ return sent_tokenize(text)
51
+
52
+ # 🧠 μžλ§‰μ„ λΆ„ν• ν•˜μ—¬ μš”μ•½
53
+ def summarize_long_text(text, chunk_char_limit=1000):
54
+ sentences = clean_transcript(text)
55
+ chunks, current_chunk = [], ""
56
+
57
+ for sentence in sentences:
58
+ if len(current_chunk) + len(sentence) < chunk_char_limit:
59
+ current_chunk += sentence + " "
60
+ else:
61
+ chunks.append(current_chunk.strip())
62
+ current_chunk = sentence + " "
63
+ if current_chunk:
64
+ chunks.append(current_chunk.strip())
65
+
66
+ summaries = []
67
+ for chunk in chunks:
68
+ try:
69
+ result = summarizer(chunk, max_length=128, min_length=30, do_sample=False)
70
+ summaries.append(result[0]['summary_text'])
71
+ except:
72
+ summaries.append("⚠️ μš”μ•½ μ‹€νŒ¨")
73
+
74
+ return "\n\n".join(summaries)
75
+
76
+ # πŸ” 전체 처리 νŒŒμ΄ν”„λΌμΈ
77
+ def process_youtube(youtube_url):
78
+ try:
79
+ audio_file = download_audio_with_ytdlp(youtube_url)
80
+ result = asr_model.transcribe(audio_file)
81
+ transcript = result.get("text", "")
82
+
83
+ if os.path.exists(audio_file):
84
+ os.remove(audio_file)
85
+
86
+ if len(transcript.strip()) < 100:
87
+ summary = "⚠️ μžλ§‰ λ‚΄μš©μ΄ λ„ˆλ¬΄ μ§§μ•„ μš”μ•½ν•  수 μ—†μŠ΅λ‹ˆλ‹€."
88
+ else:
89
+ summary = summarize_long_text(transcript)
90
+
91
+ return transcript, summary
92
+ except Exception as e:
93
+ return f"[μ—λŸ¬] {str(e)}", ""
94
+
95
+ # 🌐 Gradio UI ꡬ성
96
+ demo = gr.Interface(
97
+ fn=process_youtube,
98
+ inputs=gr.Textbox(label="유튜브 μ˜μƒ URL"),
99
+ outputs=[
100
+ gr.Textbox(label="πŸŽ™ μžλ§‰ (Whisper κ²°κ³Ό)", lines=10),
101
+ gr.Textbox(label="🧠 μš”μ•½ (μš”μ•½ κ²°κ³Ό)", lines=5)
102
+ ],
103
+ title="🎬 유튜브 μžλ§‰ & μš”μ•½ μ„œλΉ„μŠ€ (ν•œ/영 ν˜Όν•© μ΅œμ ν™”)",
104
+ description="yt-dlp둜 μ˜€λ””μ˜€ λ‹€μš΄λ‘œλ“œ β†’ Whisper μžλ§‰ 생성 β†’ mT5 μš”μ•½ λͺ¨λΈλ‘œ μš”μ•½ 처리"
105
+ )
106
+
107
+ if __name__ == "__main__":
108
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ gradio
2
+ openai-whisper
3
+ torch
4
+ transformers
5
+ nltk
6
+ yt-dlp