bep40 commited on
Commit
ecc9c7b
·
verified ·
1 Parent(s): 7a44ecb

Upload yt_scraper.py

Browse files
Files changed (1) hide show
  1. yt_scraper.py +16 -106
yt_scraper.py CHANGED
@@ -57,179 +57,89 @@ def run_yt_dlp(args, timeout=120):
57
  def get_channel_shorts_via_playlist(channel_username, max_count=100):
58
  """Get shorts from channel's shorts page using yt-dlp"""
59
  shorts = []
60
-
61
- # Method 1: Fetch from /shorts page
62
  url = f"https://www.youtube.com/@{channel_username}/shorts"
63
  items = run_yt_dlp([
64
- "--dump-json",
65
- "--flat-playlist",
66
- "--no-download",
67
  "--playlist-end", str(max_count),
68
  "--no-check-certificates",
69
  "--user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
70
  url
71
  ], timeout=90)
72
-
73
  seen_ids = set()
74
  for item in items:
75
  vid = item.get('id', '')
76
- if not vid or vid in seen_ids:
77
- continue
78
  seen_ids.add(vid)
79
-
80
  title = item.get('title', 'VTV Nam Bộ Short')
81
  duration = item.get('duration', 0) or 0
82
-
83
- # Only include actual shorts (<= 120s to be safe)
84
  if duration <= 120 or '#shorts' in title.lower() or '#short' in title.lower():
85
- shorts.append({
86
- 'id': vid,
87
- 'title': title,
88
- 'duration': duration,
89
- 'channel': channel_username,
90
- 'img': f"https://i.ytimg.com/vi/{vid}/hqdefault.jpg",
91
- })
92
-
93
  return shorts
94
 
95
  def get_channel_videos_filter_shorts(channel_username, max_count=200):
96
  """Get all videos from /videos page and filter for shorts by duration"""
97
  shorts = []
98
-
99
  url = f"https://www.youtube.com/@{channel_username}/videos"
100
  items = run_yt_dlp([
101
- "--dump-json",
102
- "--flat-playlist",
103
- "--no-download",
104
  "--playlist-end", str(max_count),
105
  "--match-filter", "duration > 0 and duration <= 120",
106
  "--no-check-certificates",
107
  "--user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
108
  url
109
  ], timeout=120)
110
-
111
  seen_ids = set()
112
  for item in items:
113
  vid = item.get('id', '')
114
- if not vid or vid in seen_ids:
115
- continue
116
  seen_ids.add(vid)
117
-
118
- title = item.get('title', 'VTV Nam Bộ Short')
119
- duration = item.get('duration', 0) or 0
120
-
121
- shorts.append({
122
- 'id': vid,
123
- 'title': title,
124
- 'duration': duration,
125
- 'channel': channel_username,
126
- 'img': f"https://i.ytimg.com/vi/{vid}/hqdefault.jpg",
127
- })
128
-
129
  return shorts
130
 
131
  def get_shorts_with_direct_url(video_ids):
132
  """Get direct video download URLs for given video IDs"""
133
  results = []
134
-
135
- for vid in video_ids[:20]: # Limit to avoid timeout
136
  try:
137
  url = f"https://www.youtube.com/shorts/{vid}"
138
- items = run_yt_dlp([
139
- "--dump-json",
140
- "--no-download",
141
- "--no-check-certificates",
142
- "--format", "best[filesize<10M]/best",
143
- url
144
- ], timeout=30)
145
-
146
  if items:
147
  info = items[0]
148
  direct_url = info.get('url', '')
149
  if not direct_url:
150
- # Try to get from formats
151
  formats = info.get('formats', [])
152
  for f in formats:
153
  if f.get('vcodec') != 'none' and f.get('acodec') != 'none':
154
  direct_url = f.get('url', '')
155
  break
156
-
157
  if direct_url:
158
- results.append({
159
- 'id': vid,
160
- 'title': info.get('title', ''),
161
- 'direct_url': direct_url,
162
- 'thumbnail': info.get('thumbnail', f"https://i.ytimg.com/vi/{vid}/hqdefault.jpg"),
163
- 'duration': info.get('duration', 0),
164
- })
165
  except Exception as e:
166
  print(f"Error getting URL for {vid}: {e}")
167
-
168
  return results
169
 
170
  def get_vtvnambo_shorts(max_count=50):
171
  """Get all shorts from VTV Nam Bộ using yt-dlp"""
172
  cached = _cached('vtvnambo_shorts_yt')
173
- if cached is not None:
174
- return cached
175
-
176
- all_shorts = []
177
- seen_ids = set()
178
-
179
- # Method 1: /shorts page
180
- print(f"[yt-dlp] Fetching /shorts page...")
181
  shorts_page = get_channel_shorts_via_playlist('vtvnambo', max_count)
182
  for s in shorts_page:
183
- if s['id'] not in seen_ids:
184
- seen_ids.add(s['id'])
185
- all_shorts.append(s)
186
- print(f"[yt-dlp] /shorts page: {len(shorts_page)} shorts")
187
-
188
- # Method 2: /videos page with duration filter
189
  if len(all_shorts) < 5:
190
- print(f"[yt-dlp] Fetching /videos page with filter...")
191
  videos_filtered = get_channel_videos_filter_shorts('vtvnambo', max_count * 2)
192
  for s in videos_filtered:
193
- if s['id'] not in seen_ids:
194
- seen_ids.add(s['id'])
195
- all_shorts.append(s)
196
- print(f"[yt-dlp] /videos filter: {len(videos_filtered)} shorts")
197
-
198
  result = all_shorts[:max_count]
199
  _set_cache('vtvnambo_shorts_yt', result)
200
- print(f"[yt-dlp] Total: {len(result)} shorts from VTV Nam Bộ")
201
  return result
202
 
203
  def get_wc_related_shorts(max_count=30):
204
  """Get World Cup / football related shorts"""
205
  all_shorts = get_vtvnambo_shorts(max_count * 3)
206
-
207
- wc_kws = [
208
- 'world cup', 'wc 2026', 'worldcup', 'fifa', 'bóng đá',
209
- 'trận đấu', 'đội tuyển', 'tuyển', 'vòng loại',
210
- 'khoảnh khắc', 'highlights', 'bàn thắng', 'goal',
211
- 'kết quả', 'tỉ số', 'việt nam', 'vn',
212
- 'ngoại hạng', 'premier league', 'champions league',
213
- 'laliga', 'serie a', 'bundesliga', 'ligue 1',
214
- 'copa', 'europa', 'c1', 'c2',
215
- 'messi', 'ronaldo', 'neymar', 'mbappe', 'haaland',
216
- 'v-league', 'vleague', 'bóng đá việt',
217
- 'đội bóng', 'hlv', 'huấn luyện viên',
218
- 'chuyển nhượng', 'transfer',
219
- 'asian cup', 'aff cup', 'sea games',
220
- 'olympic', 'u23', 'u20', 'u17',
221
- ]
222
-
223
- wc_shorts = []
224
- for s in all_shorts:
225
- tl = s.get('title', '').lower()
226
- if any(k in tl for k in wc_kws):
227
- wc_shorts.append(s)
228
-
229
- if not wc_shorts:
230
- wc_shorts = all_shorts
231
-
232
  return wc_shorts[:max_count]
233
 
234
- # Aliases
235
  get_vtvnamo_shorts = get_vtvnambo_shorts
 
57
  def get_channel_shorts_via_playlist(channel_username, max_count=100):
58
  """Get shorts from channel's shorts page using yt-dlp"""
59
  shorts = []
 
 
60
  url = f"https://www.youtube.com/@{channel_username}/shorts"
61
  items = run_yt_dlp([
62
+ "--dump-json", "--flat-playlist", "--no-download",
 
 
63
  "--playlist-end", str(max_count),
64
  "--no-check-certificates",
65
  "--user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
66
  url
67
  ], timeout=90)
 
68
  seen_ids = set()
69
  for item in items:
70
  vid = item.get('id', '')
71
+ if not vid or vid in seen_ids: continue
 
72
  seen_ids.add(vid)
 
73
  title = item.get('title', 'VTV Nam Bộ Short')
74
  duration = item.get('duration', 0) or 0
 
 
75
  if duration <= 120 or '#shorts' in title.lower() or '#short' in title.lower():
76
+ shorts.append({'id': vid, 'title': title, 'duration': duration, 'channel': channel_username, 'img': f"https://i.ytimg.com/vi/{vid}/hqdefault.jpg"})
 
 
 
 
 
 
 
77
  return shorts
78
 
79
  def get_channel_videos_filter_shorts(channel_username, max_count=200):
80
  """Get all videos from /videos page and filter for shorts by duration"""
81
  shorts = []
 
82
  url = f"https://www.youtube.com/@{channel_username}/videos"
83
  items = run_yt_dlp([
84
+ "--dump-json", "--flat-playlist", "--no-download",
 
 
85
  "--playlist-end", str(max_count),
86
  "--match-filter", "duration > 0 and duration <= 120",
87
  "--no-check-certificates",
88
  "--user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
89
  url
90
  ], timeout=120)
 
91
  seen_ids = set()
92
  for item in items:
93
  vid = item.get('id', '')
94
+ if not vid or vid in seen_ids: continue
 
95
  seen_ids.add(vid)
96
+ shorts.append({'id': vid, 'title': item.get('title', ''), 'duration': item.get('duration', 0) or 0, 'channel': channel_username, 'img': f"https://i.ytimg.com/vi/{vid}/hqdefault.jpg"})
 
 
 
 
 
 
 
 
 
 
 
97
  return shorts
98
 
99
  def get_shorts_with_direct_url(video_ids):
100
  """Get direct video download URLs for given video IDs"""
101
  results = []
102
+ for vid in video_ids[:20]:
 
103
  try:
104
  url = f"https://www.youtube.com/shorts/{vid}"
105
+ items = run_yt_dlp(["--dump-json", "--no-download", "--no-check-certificates", "--format", "best[filesize<10M]/best", url], timeout=30)
 
 
 
 
 
 
 
106
  if items:
107
  info = items[0]
108
  direct_url = info.get('url', '')
109
  if not direct_url:
 
110
  formats = info.get('formats', [])
111
  for f in formats:
112
  if f.get('vcodec') != 'none' and f.get('acodec') != 'none':
113
  direct_url = f.get('url', '')
114
  break
 
115
  if direct_url:
116
+ results.append({'id': vid, 'title': info.get('title', ''), 'direct_url': direct_url, 'thumbnail': info.get('thumbnail', f"https://i.ytimg.com/vi/{vid}/hqdefault.jpg"), 'duration': info.get('duration', 0)})
 
 
 
 
 
 
117
  except Exception as e:
118
  print(f"Error getting URL for {vid}: {e}")
 
119
  return results
120
 
121
  def get_vtvnambo_shorts(max_count=50):
122
  """Get all shorts from VTV Nam Bộ using yt-dlp"""
123
  cached = _cached('vtvnambo_shorts_yt')
124
+ if cached is not None: return cached
125
+ all_shorts = []; seen_ids = set()
 
 
 
 
 
 
126
  shorts_page = get_channel_shorts_via_playlist('vtvnambo', max_count)
127
  for s in shorts_page:
128
+ if s['id'] not in seen_ids: seen_ids.add(s['id']); all_shorts.append(s)
 
 
 
 
 
129
  if len(all_shorts) < 5:
 
130
  videos_filtered = get_channel_videos_filter_shorts('vtvnambo', max_count * 2)
131
  for s in videos_filtered:
132
+ if s['id'] not in seen_ids: seen_ids.add(s['id']); all_shorts.append(s)
 
 
 
 
133
  result = all_shorts[:max_count]
134
  _set_cache('vtvnambo_shorts_yt', result)
 
135
  return result
136
 
137
  def get_wc_related_shorts(max_count=30):
138
  """Get World Cup / football related shorts"""
139
  all_shorts = get_vtvnambo_shorts(max_count * 3)
140
+ wc_kws = ['world cup', 'wc 2026', 'worldcup', 'fifa', 'bóng đá', 'trận đấu', 'đội tuyển', 'tuyển', 'vòng loại', 'khoảnh khắc', 'highlights', 'bàn thắng', 'goal', 'kết quả', 'tỉ số', 'việt nam', 'vn', 'ngoại hạng', 'premier league', 'champions league', 'laliga', 'serie a', 'bundesliga', 'ligue 1', 'copa', 'europa', 'c1', 'c2', 'messi', 'ronaldo', 'neymar', 'mbappe', 'haaland', 'v-league', 'vleague', 'bóng đá việt', 'đội bóng', 'hlv', 'huấn luyện viên', 'chuyển nhượng', 'transfer', 'asian cup', 'aff cup', 'sea games', 'olympic', 'u23', 'u20', 'u17']
141
+ wc_shorts = [s for s in all_shorts if any(k in s.get('title','').lower() for k in wc_kws)]
142
+ if not wc_shorts: wc_shorts = all_shorts
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
143
  return wc_shorts[:max_count]
144
 
 
145
  get_vtvnamo_shorts = get_vtvnambo_shorts