ztoolx commited on
Commit
dbbaf34
·
verified ·
1 Parent(s): 26f3ad0
Files changed (1) hide show
  1. main.py +0 -134
main.py DELETED
@@ -1,134 +0,0 @@
1
- from fastapi import FastAPI, HTTPException
2
- from fastapi.middleware.cors import CORSMiddleware
3
- import requests
4
- import re
5
- from pydantic import BaseModel
6
-
7
- app = FastAPI()
8
-
9
- # Enable CORS so your Vercel frontend can call this API
10
- app.add_middleware(
11
- CORSMiddleware,
12
- allow_origins=["*"],
13
- allow_credentials=True,
14
- allow_methods=["*"],
15
- allow_headers=["*"],
16
- )
17
-
18
- class TranscriptRequest(BaseModel):
19
- url: str
20
-
21
- def extract_video_id(url: str) -> str:
22
- pattern = r'(?:youtube\.com\/(?:[^\/]+\/.+\/|(?:v|e(?:mbed)?)\/|.*[?&]v=)|youtu\.be\/)([^"&?\/\s]{11})'
23
- match = re.search(pattern, url)
24
- if match:
25
- return match.group(1)
26
- return None
27
-
28
- def parse_caption_xml(xml_string: str) -> str:
29
- # Extract text content from the XML safely using regex
30
- text_matches = re.findall(r'<text[^>]*>([^<]*)</text>', xml_string)
31
- if not text_matches:
32
- return ""
33
-
34
- full_text = " ".join(text_matches)
35
- # Clean up HTML entities
36
- full_text = full_text.replace("&amp;#39;", "'").replace("&amp;quot;", '"').replace("&amp;amp;", "&").replace("&#39;", "'").replace("&lt;", "<").replace("&gt;", ">")
37
- return full_text
38
-
39
- @app.post("/api/transcript")
40
- async def get_transcript(req: TranscriptRequest):
41
- url = req.url
42
-
43
- video_id = extract_video_id(url)
44
- if not video_id:
45
- raise HTTPException(status_code=400, detail="Invalid YouTube URL")
46
-
47
- try:
48
- print(f"Fetching InnerTube data for video_id: {video_id}")
49
-
50
- # YouTube's internal API endpoint
51
- innertube_url = "https://www.youtube.com/youtubei/v1/player?prettyPrint=false"
52
-
53
- payload = {
54
- "context": {
55
- "client": {
56
- "clientName": "WEB",
57
- "clientVersion": "2.20241210.00.00",
58
- "hl": "en",
59
- "gl": "US"
60
- }
61
- },
62
- "videoId": video_id
63
- }
64
-
65
- headers = {
66
- "Content-Type": "application/json",
67
- "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
68
- "Origin": "https://www.youtube.com",
69
- "Referer": "https://www.youtube.com/"
70
- }
71
-
72
- # 1. Fetch the video player data
73
- response = requests.post(innertube_url, json=payload, headers=headers, timeout=5)
74
-
75
- if response.status_code != 200:
76
- raise HTTPException(status_code=500, detail=f"YouTube API returned status {response.status_code}")
77
-
78
- data = response.json()
79
-
80
- # Check if video is playable
81
- playability = data.get("playabilityStatus", {})
82
- if playability.get("status") == "ERROR":
83
- raise HTTPException(status_code=404, detail=playability.get("reason", "Video unavailable"))
84
-
85
- # 2. Extract caption tracks
86
- caption_tracks = data.get("captions", {}).get("playerCaptionsTracklistRenderer", {}).get("captionTracks", [])
87
-
88
- if not caption_tracks:
89
- raise HTTPException(status_code=404, detail="No captions found for this video.")
90
-
91
- # Prefer English, fallback to first available
92
- selected_track = None
93
- for track in caption_tracks:
94
- if track.get("languageCode") == "en":
95
- selected_track = track
96
- break
97
-
98
- if not selected_track:
99
- selected_track = caption_tracks[0]
100
-
101
- caption_url = selected_track.get("baseUrl")
102
- if not caption_url:
103
- raise HTTPException(status_code=404, detail="Caption URL not found.")
104
-
105
- print(f"Fetching captions from URL...")
106
-
107
- # 3. Fetch the actual caption XML
108
- caption_response = requests.get(caption_url, headers={"User-Agent": headers["User-Agent"]}, timeout=5)
109
-
110
- if caption_response.status_code != 200:
111
- raise HTTPException(status_code=500, detail=f"Caption fetch failed with status {caption_response.status_code}")
112
-
113
- # 4. Parse the XML into plain text
114
- full_text = parse_caption_xml(caption_response.text)
115
-
116
- if not full_text.strip():
117
- raise HTTPException(status_code=404, detail="Caption XML was empty.")
118
-
119
- print(f"Successfully fetched transcript. Length: {len(full_text)} chars")
120
- return {"text": full_text}
121
-
122
- except HTTPException:
123
- raise
124
- except requests.exceptions.Timeout:
125
- raise HTTPException(status_code=504, detail="Request to YouTube timed out.")
126
- except Exception as e:
127
- error_name = type(e).__name__
128
- print(f"UNHANDLED ERROR for {video_id}: {error_name} - {str(e)}")
129
- raise HTTPException(status_code=500, detail=f"Failed to fetch transcript: {error_name} - {str(e)}")
130
-
131
- # KEEP-ALIVE ENDPOINT
132
- @app.get("/api/health")
133
- async def health_check():
134
- return {"status": "ok", "message": "Transcript API is running!"}