mirror of
				https://github.com/yt-dlp/yt-dlp.git
				synced 2025-10-31 14:45:14 +00:00 
			
		
		
		
	| @@ -7,33 +7,45 @@ from ..utils import ( | ||||
|     int_or_none, | ||||
|     js_to_json, | ||||
|     traverse_obj, | ||||
|     url_or_none, | ||||
|     urlencode_postdata, | ||||
| ) | ||||
| 
 | ||||
| 
 | ||||
| class TubiTvIE(InfoExtractor): | ||||
|     _VALID_URL = r'''(?x) | ||||
|                     (?: | ||||
|                         tubitv:| | ||||
|                         https?://(?:www\.)?tubitv\.com/(?:video|movies|tv-shows)/ | ||||
|                     ) | ||||
|                     (?P<id>[0-9]+)''' | ||||
|     _VALID_URL = r'https?://(?:www\.)?tubitv\.com/(?P<type>video|movies|tv-shows)/(?P<id>\d+)' | ||||
|     _LOGIN_URL = 'http://tubitv.com/login' | ||||
|     _NETRC_MACHINE = 'tubitv' | ||||
|     _GEO_COUNTRIES = ['US'] | ||||
|     _TESTS = [{ | ||||
|         'url': 'https://tubitv.com/movies/383676/tracker', | ||||
|         'md5': '566fa0f76870302d11af0de89511d3f0', | ||||
|         'url': 'https://tubitv.com/movies/100004539/the-39-steps', | ||||
|         'info_dict': { | ||||
|             'id': '383676', | ||||
|             'id': '100004539', | ||||
|             'ext': 'mp4', | ||||
|             'title': 'Tracker', | ||||
|             'description': 'md5:ff320baf43d0ad2655e538c1d5cd9706', | ||||
|             'uploader_id': 'f866e2677ea2f0dff719788e4f7f9195', | ||||
|             'release_year': 2010, | ||||
|             'title': 'The 39 Steps', | ||||
|             'description': 'md5:bb2f2dd337f0dc58c06cb509943f54c8', | ||||
|             'uploader_id': 'abc2558d54505d4f0f32be94f2e7108c', | ||||
|             'release_year': 1935, | ||||
|             'thumbnail': r're:^https?://.+\.(jpe?g|png)$', | ||||
|             'duration': 6122, | ||||
|             'duration': 5187, | ||||
|         }, | ||||
|         'params': {'skip_download': 'm3u8'}, | ||||
|     }, { | ||||
|         'url': 'https://tubitv.com/tv-shows/554628/s01-e01-rise-of-the-snakes', | ||||
|         'info_dict': { | ||||
|             'id': '554628', | ||||
|             'ext': 'mp4', | ||||
|             'title': 'S01:E01 - Rise of the Snakes', | ||||
|             'description': 'md5:ba136f586de53af0372811e783a3f57d', | ||||
|             'episode': 'Rise of the Snakes', | ||||
|             'episode_number': 1, | ||||
|             'season': 'Season 1', | ||||
|             'season_number': 1, | ||||
|             'uploader_id': '2a9273e728c510d22aa5c57d0646810b', | ||||
|             'release_year': 2011, | ||||
|             'thumbnail': r're:^https?://.+\.(jpe?g|png)$', | ||||
|             'duration': 1376, | ||||
|         }, | ||||
|         'params': {'skip_download': 'm3u8'}, | ||||
|     }, { | ||||
|         'url': 'http://tubitv.com/video/283829/the_comedian_at_the_friday', | ||||
|         'md5': '43ac06be9326f41912dc64ccf7a80320', | ||||
| @@ -81,45 +93,39 @@ class TubiTvIE(InfoExtractor): | ||||
|                 'Login failed (invalid username/password)', expected=True) | ||||
| 
 | ||||
|     def _real_extract(self, url): | ||||
|         video_id = self._match_id(url) | ||||
|         video_data = self._download_json(f'https://tubitv.com/oz/videos/{video_id}/content', video_id, query={ | ||||
|             'video_resources': ['dash', 'hlsv3', 'hlsv6', *self._UNPLAYABLE_FORMATS], | ||||
|         }) | ||||
|         title = video_data['title'] | ||||
|         video_id, video_type = self._match_valid_url(url).group('id', 'type') | ||||
|         webpage = self._download_webpage(f'https://tubitv.com/{video_type}/{video_id}/', video_id) | ||||
|         video_data = self._search_json( | ||||
|             r'window\.__data\s*=', webpage, 'data', video_id, | ||||
|             transform_source=js_to_json)['video']['byId'][video_id] | ||||
| 
 | ||||
|         formats = [] | ||||
|         drm_formats = False | ||||
| 
 | ||||
|         for resource in video_data['video_resources']: | ||||
|             if resource['type'] in ('dash', ): | ||||
|                 formats += self._extract_mpd_formats(resource['manifest']['url'], video_id, mpd_id=resource['type'], fatal=False) | ||||
|             elif resource['type'] in ('hlsv3', 'hlsv6'): | ||||
|                 formats += self._extract_m3u8_formats(resource['manifest']['url'], video_id, 'mp4', m3u8_id=resource['type'], fatal=False) | ||||
|             elif resource['type'] in self._UNPLAYABLE_FORMATS: | ||||
|         for resource in traverse_obj(video_data, ('video_resources', lambda _, v: url_or_none(v['manifest']['url']))): | ||||
|             resource_type = resource.get('type') | ||||
|             manifest_url = resource['manifest']['url'] | ||||
|             if resource_type == 'dash': | ||||
|                 formats.extend(self._extract_mpd_formats(manifest_url, video_id, mpd_id=resource_type, fatal=False)) | ||||
|             elif resource_type in ('hlsv3', 'hlsv6'): | ||||
|                 formats.extend(self._extract_m3u8_formats(manifest_url, video_id, 'mp4', m3u8_id=resource_type, fatal=False)) | ||||
|             elif resource_type in self._UNPLAYABLE_FORMATS: | ||||
|                 drm_formats = True | ||||
|             else: | ||||
|                 self.report_warning(f'Skipping unknown resource type "{resource_type}"') | ||||
| 
 | ||||
|         if not formats and drm_formats: | ||||
|             self.report_drm(video_id) | ||||
|         elif not formats and not video_data.get('policy_match'):  # policy_match is False if content was removed | ||||
|             raise ExtractorError('This content is currently unavailable', expected=True) | ||||
| 
 | ||||
|         thumbnails = [] | ||||
|         for thumbnail_url in video_data.get('thumbnails', []): | ||||
|             if not thumbnail_url: | ||||
|                 continue | ||||
|             thumbnails.append({ | ||||
|                 'url': self._proto_relative_url(thumbnail_url), | ||||
|             }) | ||||
| 
 | ||||
|         subtitles = {} | ||||
|         for sub in video_data.get('subtitles', []): | ||||
|             sub_url = sub.get('url') | ||||
|             if not sub_url: | ||||
|                 continue | ||||
|         for sub in traverse_obj(video_data, ('subtitles', lambda _, v: url_or_none(v['url']))): | ||||
|             subtitles.setdefault(sub.get('lang', 'English'), []).append({ | ||||
|                 'url': self._proto_relative_url(sub_url), | ||||
|                 'url': self._proto_relative_url(sub['url']), | ||||
|             }) | ||||
| 
 | ||||
|         title = traverse_obj(video_data, ('title', {str})) | ||||
|         season_number, episode_number, episode_title = self._search_regex( | ||||
|             r'^S(\d+):E(\d+) - (.+)', title, 'episode info', fatal=False, group=(1, 2, 3), default=(None, None, None)) | ||||
| 
 | ||||
| @@ -128,18 +134,21 @@ class TubiTvIE(InfoExtractor): | ||||
|             'title': title, | ||||
|             'formats': formats, | ||||
|             'subtitles': subtitles, | ||||
|             'thumbnails': thumbnails, | ||||
|             'description': video_data.get('description'), | ||||
|             'duration': int_or_none(video_data.get('duration')), | ||||
|             'uploader_id': video_data.get('publisher_id'), | ||||
|             'release_year': int_or_none(video_data.get('year')), | ||||
|             'season_number': int_or_none(season_number), | ||||
|             'episode_number': int_or_none(episode_number), | ||||
|             'episode_title': episode_title | ||||
|             'episode': episode_title, | ||||
|             **traverse_obj(video_data, { | ||||
|                 'description': ('description', {str}), | ||||
|                 'duration': ('duration', {int_or_none}), | ||||
|                 'uploader_id': ('publisher_id', {str}), | ||||
|                 'release_year': ('year', {int_or_none}), | ||||
|                 'thumbnails': ('thumbnails', ..., {url_or_none}, {'url': {self._proto_relative_url}}), | ||||
|             }), | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
| class TubiTvShowIE(InfoExtractor): | ||||
|     _WORKING = False | ||||
|     _VALID_URL = r'https?://(?:www\.)?tubitv\.com/series/[0-9]+/(?P<show_name>[^/?#]+)' | ||||
|     _TESTS = [{ | ||||
|         'url': 'https://tubitv.com/series/3936/the-joy-of-painting-with-bob-ross?start=true', | ||||
| @@ -160,7 +169,7 @@ class TubiTvShowIE(InfoExtractor): | ||||
|             if traverse_obj(show_json, ('byId', episode_id, 'type')) == 's': | ||||
|                 continue | ||||
|             yield self.url_result( | ||||
|                 'tubitv:%s' % episode_id, | ||||
|                 f'https://tubitv.com/tv-shows/{episode_id}/', | ||||
|                 ie=TubiTvIE.ie_key(), video_id=episode_id) | ||||
| 
 | ||||
|     def _real_extract(self, url): | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Ben Galliart
					Ben Galliart