brotoo commited on
Commit
b5cd35d
·
verified ·
1 Parent(s): a0a663f

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py +30 -68
  2. requirements.txt +1 -2
app.py CHANGED
@@ -1,10 +1,8 @@
1
  import logging
2
  import os
3
  import re
4
- import shutil
5
- import tempfile
6
  from typing import Any, Dict, List
7
- from urllib.parse import urlparse
8
 
9
  import requests
10
  import uvicorn
@@ -14,7 +12,7 @@ from fastapi.middleware.cors import CORSMiddleware
14
  from pydantic import BaseModel, HttpUrl
15
  from readability import Document
16
  from transformers import pipeline
17
- from yt_dlp import YoutubeDL
18
 
19
  # Optional cache dir to avoid re-downloading models on restarts
20
  os.environ.setdefault("HF_HOME", "/data/hf_cache")
@@ -27,7 +25,6 @@ logger = logging.getLogger("app")
27
 
28
  # Globals for lazy loading
29
  summarizer = None
30
- whisper_model = None
31
 
32
  MODEL_NAME = "brotoo/BART-NewsSummarizer"
33
 
@@ -155,55 +152,36 @@ def get_summarizer():
155
  return summarizer
156
 
157
 
158
- def get_whisper():
159
- global whisper_model
160
- if whisper_model is None:
161
- logger.info("Loading Whisper model: base")
162
- import whisper # type: ignore
163
-
164
- whisper_model = whisper.load_model("base", device="cpu")
165
- logger.info("Whisper model loaded")
166
- return whisper_model
167
-
168
-
169
- def temp_audio_path() -> str:
170
- directory = tempfile.mkdtemp(prefix="yt_audio_")
171
- return os.path.join(directory, "audio.%(ext)s")
172
-
173
-
174
- def find_first_wav(path: str) -> str:
175
- if os.path.isfile(path) and path.lower().endswith(".wav"):
176
- return path
177
- if os.path.isdir(path):
178
- for entry in os.listdir(path):
179
- candidate = os.path.join(path, entry)
180
- if os.path.isfile(candidate) and candidate.lower().endswith(".wav"):
181
- return candidate
182
  return ""
183
 
184
 
185
- def download_youtube_audio(url: str) -> str:
186
- output_template = temp_audio_path()
187
- temp_dir = os.path.dirname(output_template)
188
- ydl_opts = {
189
- "format": "bestaudio/best",
190
- "outtmpl": output_template,
191
- "postprocessors": [
192
- {
193
- "key": "FFmpegExtractAudio",
194
- "preferredcodec": "wav",
195
- "preferredquality": "192",
196
- }
197
- ],
198
- "quiet": True,
199
- "no_warnings": True,
200
- }
201
- with YoutubeDL(ydl_opts) as ydl:
202
- ydl.download([url])
203
- wav_path = find_first_wav(temp_dir)
204
- if not wav_path:
205
- raise ValueError("Failed to download or convert YouTube audio.")
206
- return wav_path
207
 
208
 
209
  app = FastAPI(title="News and Video Summarizer", version="1.0.0")
@@ -257,16 +235,8 @@ async def summarize_video(payload: SummarizeVideoRequest) -> Dict[str, Any]:
257
  logger.exception("Failed to load summarizer")
258
  return {"error": f"Model load failed: {exc}"}
259
 
260
- audio_path = ""
261
- temp_dir = ""
262
  try:
263
- whisper = get_whisper()
264
- audio_path = download_youtube_audio(str(payload.url))
265
- temp_dir = os.path.dirname(audio_path)
266
- transcript = whisper.transcribe(audio_path, language="en")
267
- transcript_text = clean_text(transcript.get("text", ""))
268
- if not transcript_text:
269
- raise HTTPException(status_code=500, detail="No transcript text could be produced from the audio.")
270
  summary = summarize_text(transcript_text, model)
271
  if not summary:
272
  raise HTTPException(status_code=500, detail="Summarization failed.")
@@ -276,14 +246,6 @@ async def summarize_video(payload: SummarizeVideoRequest) -> Dict[str, Any]:
276
  except Exception as exc:
277
  logger.exception("Unexpected error during video summarization")
278
  return {"error": f"Video summarization failed: {exc}"}
279
- finally:
280
- try:
281
- if audio_path and os.path.exists(audio_path):
282
- os.remove(audio_path)
283
- if temp_dir:
284
- shutil.rmtree(temp_dir, ignore_errors=True)
285
- except Exception:
286
- logger.exception("Failed to clean up temporary audio files")
287
 
288
 
289
  if __name__ == "__main__":
 
1
  import logging
2
  import os
3
  import re
 
 
4
  from typing import Any, Dict, List
5
+ from urllib.parse import parse_qs, urlparse
6
 
7
  import requests
8
  import uvicorn
 
12
  from pydantic import BaseModel, HttpUrl
13
  from readability import Document
14
  from transformers import pipeline
15
+ from youtube_transcript_api import YouTubeTranscriptApi
16
 
17
  # Optional cache dir to avoid re-downloading models on restarts
18
  os.environ.setdefault("HF_HOME", "/data/hf_cache")
 
25
 
26
  # Globals for lazy loading
27
  summarizer = None
 
28
 
29
  MODEL_NAME = "brotoo/BART-NewsSummarizer"
30
 
 
152
  return summarizer
153
 
154
 
155
+ def extract_youtube_video_id(url: str) -> str:
156
+ parsed = urlparse(url)
157
+ host = (parsed.hostname or "").lower()
158
+ if host in {"youtu.be"}:
159
+ return parsed.path.lstrip("/").split("/")[0]
160
+ if "youtube.com" in host:
161
+ query_params = parse_qs(parsed.query)
162
+ video_id = query_params.get("v", [""])[0]
163
+ if not video_id and parsed.path.startswith("/shorts/"):
164
+ video_id = parsed.path.split("/shorts/", 1)[-1].split("/")[0]
165
+ return video_id
 
 
 
 
 
 
 
 
 
 
 
 
 
166
  return ""
167
 
168
 
169
+ def extract_youtube_transcript(url: str) -> str:
170
+ video_id = extract_youtube_video_id(url)
171
+ if not video_id:
172
+ raise HTTPException(status_code=400, detail="Invalid YouTube URL.")
173
+ try:
174
+ transcript = YouTubeTranscriptApi.get_transcript(video_id, languages=["en", "id"])
175
+ text = " ".join(segment.get("text", "") for segment in transcript)
176
+ cleaned = clean_text(text)
177
+ if not cleaned:
178
+ raise HTTPException(status_code=500, detail="Transcript was empty.")
179
+ return cleaned
180
+ except HTTPException:
181
+ raise
182
+ except Exception as exc:
183
+ logger.exception("Failed to fetch YouTube transcript")
184
+ raise HTTPException(status_code=500, detail=f"Could not fetch transcript: {exc}")
 
 
 
 
 
 
185
 
186
 
187
  app = FastAPI(title="News and Video Summarizer", version="1.0.0")
 
235
  logger.exception("Failed to load summarizer")
236
  return {"error": f"Model load failed: {exc}"}
237
 
 
 
238
  try:
239
+ transcript_text = extract_youtube_transcript(str(payload.url))
 
 
 
 
 
 
240
  summary = summarize_text(transcript_text, model)
241
  if not summary:
242
  raise HTTPException(status_code=500, detail="Summarization failed.")
 
246
  except Exception as exc:
247
  logger.exception("Unexpected error during video summarization")
248
  return {"error": f"Video summarization failed: {exc}"}
 
 
 
 
 
 
 
 
249
 
250
 
251
  if __name__ == "__main__":
requirements.txt CHANGED
@@ -4,10 +4,9 @@ torch==2.2.0+cpu
4
  --extra-index-url https://download.pytorch.org/whl/cpu
5
  fastapi>=0.115.0
6
  uvicorn[standard]>=0.30.0
7
- openai-whisper==20231117
8
- yt_dlp>=2023.11.16
9
  readability-lxml>=0.8.1
10
  beautifulsoup4>=4.12.2
11
  requests>=2.31.0
12
  pydantic>=1.10.15
13
  lxml>=4.9.3
 
 
4
  --extra-index-url https://download.pytorch.org/whl/cpu
5
  fastapi>=0.115.0
6
  uvicorn[standard]>=0.30.0
 
 
7
  readability-lxml>=0.8.1
8
  beautifulsoup4>=4.12.2
9
  requests>=2.31.0
10
  pydantic>=1.10.15
11
  lxml>=4.9.3
12
+ youtube-transcript-api