Skip to content

Commit 5c1e5a1

Browse files
committed
[TubiTv] Add TubiTvShow series/season extractor based on yt-dlp
1 parent 76067fb commit 5c1e5a1

File tree

2 files changed

+82
-1
lines changed

2 files changed

+82
-1
lines changed

youtube_dl/extractor/extractors.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -1328,7 +1328,10 @@
13281328
from .trunews import TruNewsIE
13291329
from .trutv import TruTVIE
13301330
from .tube8 import Tube8IE
1331-
from .tubitv import TubiTvIE
1331+
from .tubitv import (
1332+
TubiTvIE,
1333+
TubiTvShowIE,
1334+
)
13321335
from .tumblr import TumblrIE
13331336
from .tunein import (
13341337
TuneInClipIE,

youtube_dl/extractor/tubitv.py

+78
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
ExtractorError,
77
get_element_by_id,
88
int_or_none,
9+
join_nonempty,
910
js_to_json,
1011
merge_dicts,
1112
parse_age_limit,
@@ -193,3 +194,80 @@ def _real_extract(self, url):
193194
'series': ('title', T(strip_or_none)),
194195
# 'series_id': ('id', T(compat_str)),
195196
}), get_all=False), info)
197+
198+
199+
class TubiTvShowIE(InfoExtractor):
200+
IE_NAME = 'tubitv:series'
201+
_VALID_URL = r'https?://(?:www\.)?tubitv\.com/series/\d+/(?P<show_name>[^/?#]+)(?:/season-(?P<season>\d+))?'
202+
_TESTS = [{
203+
'url': 'https://tubitv.com/series/3936/the-joy-of-painting-with-bob-ross?start=true',
204+
'playlist_mincount': 390,
205+
'info_dict': {
206+
'id': 'the-joy-of-painting-with-bob-ross',
207+
},
208+
}, {
209+
'url': 'https://tubitv.com/series/3936/the-joy-of-painting-with-bob-ross/season-1',
210+
'playlist_count': 13,
211+
'info_dict': {
212+
'id': 'the-joy-of-painting-with-bob-ross-season-1',
213+
},
214+
}, {
215+
'url': 'https://tubitv.com/series/3936/the-joy-of-painting-with-bob-ross/season-3',
216+
'playlist_count': 13,
217+
'info_dict': {
218+
'id': 'the-joy-of-painting-with-bob-ross-season-3',
219+
},
220+
}]
221+
222+
def _real_extract(self, url):
223+
playlist_id, selected_season = self._match_valid_url(url).group(
224+
'show_name', 'season')
225+
226+
def entries(s_url, playlist_id, selected_season_num):
227+
228+
def get_season_data(s_num, fatal=False):
229+
if s_num is None:
230+
url, s_id = s_url, playlist_id
231+
else:
232+
url = '%s/season-%d' % (s_url, s_num)
233+
s_id = '%s-season-%d' % (playlist_id, s_num)
234+
webpage = self._download_webpage(url, s_id, fatal=fatal)
235+
data = self._search_json(
236+
r'window\s*\.\s*__data\s*=', webpage or '', 'data', s_id,
237+
transform_source=js_to_json, default={})
238+
return data['video'] if fatal else data.get('video', {})
239+
240+
data = get_season_data(None, fatal=True)
241+
# The {series_id}.seasons JSON may lack some episodes that are available
242+
# Iterate over the season numbers instead [1]
243+
# 1. https://github.com/yt-dlp/yt-dlp/issues/11170#issuecomment-2399918777
244+
seasons = (
245+
traverse_obj(data, (
246+
'byId', lambda _, v: v['type'] == 's', 'seasons', Ellipsis,
247+
'number', T(int_or_none)))
248+
if selected_season is None
249+
else [selected_season])
250+
251+
unavail_cnt = 0
252+
select_episodes = lambda _, v: v['type'] == 'v'
253+
for season_number in seasons:
254+
if not data:
255+
data = get_season_data(season_number)
256+
257+
unavail_cnt += len(traverse_obj(data, ('byId', select_episodes, 'policy_match', T(lambda m: (not m) or None))))
258+
259+
for episode_id, episode in traverse_obj(data, ('byId', select_episodes, T(lambda e: (e['id'], e)))):
260+
yield merge_dicts(self.url_result(
261+
'https://tubitv.com/tv-shows/{0}/'.format(episode_id), TubiTvIE.ie_key(), episode_id), {
262+
'season_number': season_number,
263+
'episode_number': int_or_none(episode.get('num')),
264+
})
265+
266+
data = None
267+
268+
if unavail_cnt > 0:
269+
self.report_warning('%d items were marked as unavailable: check that the desired content is available or provide login parameters if needed' % unavail_cnt)
270+
271+
return self.playlist_result(
272+
entries(url, playlist_id, int_or_none(selected_season)),
273+
join_nonempty(playlist_id, selected_season, delim='-season-'))

0 commit comments

Comments
 (0)