mirror of
				https://github.com/yt-dlp/yt-dlp.git
				synced 2025-11-04 08:35:12 +00:00 
			
		
		
		
	@@ -24,6 +24,8 @@ from ..utils import (
 | 
				
			|||||||
    parse_iso8601,
 | 
					    parse_iso8601,
 | 
				
			||||||
    parse_qs,
 | 
					    parse_qs,
 | 
				
			||||||
    qualities,
 | 
					    qualities,
 | 
				
			||||||
 | 
					    str_or_none,
 | 
				
			||||||
 | 
					    traverse_obj,
 | 
				
			||||||
    try_get,
 | 
					    try_get,
 | 
				
			||||||
    unified_timestamp,
 | 
					    unified_timestamp,
 | 
				
			||||||
    update_url_query,
 | 
					    update_url_query,
 | 
				
			||||||
@@ -52,6 +54,7 @@ class TwitchBaseIE(InfoExtractor):
 | 
				
			|||||||
        'VideoAccessToken_Clip': '36b89d2507fce29e5ca551df756d27c1cfe079e2609642b4390aa4c35796eb11',
 | 
					        'VideoAccessToken_Clip': '36b89d2507fce29e5ca551df756d27c1cfe079e2609642b4390aa4c35796eb11',
 | 
				
			||||||
        'VideoPreviewOverlay': '3006e77e51b128d838fa4e835723ca4dc9a05c5efd4466c1085215c6e437e65c',
 | 
					        'VideoPreviewOverlay': '3006e77e51b128d838fa4e835723ca4dc9a05c5efd4466c1085215c6e437e65c',
 | 
				
			||||||
        'VideoMetadata': '226edb3e692509f727fd56821f5653c05740242c82b0388883e0c0e75dcbf687',
 | 
					        'VideoMetadata': '226edb3e692509f727fd56821f5653c05740242c82b0388883e0c0e75dcbf687',
 | 
				
			||||||
 | 
					        'VideoPlayer_ChapterSelectButtonVideo': '8d2793384aac3773beab5e59bd5d6f585aedb923d292800119e03d40cd0f9b41',
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _real_initialize(self):
 | 
					    def _real_initialize(self):
 | 
				
			||||||
@@ -249,6 +252,38 @@ class TwitchVodIE(TwitchBaseIE):
 | 
				
			|||||||
    }, {
 | 
					    }, {
 | 
				
			||||||
        'url': 'https://player.twitch.tv/?video=480452374',
 | 
					        'url': 'https://player.twitch.tv/?video=480452374',
 | 
				
			||||||
        'only_matching': True,
 | 
					        'only_matching': True,
 | 
				
			||||||
 | 
					    }, {
 | 
				
			||||||
 | 
					        'url': 'https://www.twitch.tv/videos/635475444',
 | 
				
			||||||
 | 
					        'info_dict': {
 | 
				
			||||||
 | 
					            'id': 'v635475444',
 | 
				
			||||||
 | 
					            'ext': 'mp4',
 | 
				
			||||||
 | 
					            'title': 'Riot Games',
 | 
				
			||||||
 | 
					            'duration': 11643,
 | 
				
			||||||
 | 
					            'uploader': 'Riot Games',
 | 
				
			||||||
 | 
					            'uploader_id': 'riotgames',
 | 
				
			||||||
 | 
					            'timestamp': 1590770569,
 | 
				
			||||||
 | 
					            'upload_date': '20200529',
 | 
				
			||||||
 | 
					            'chapters': [
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    'start_time': 0,
 | 
				
			||||||
 | 
					                    'end_time': 573,
 | 
				
			||||||
 | 
					                    'title': 'League of Legends'
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    'start_time': 573,
 | 
				
			||||||
 | 
					                    'end_time': 3922,
 | 
				
			||||||
 | 
					                    'title': 'Legends of Runeterra'
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    'start_time': 3922,
 | 
				
			||||||
 | 
					                    'end_time': 11643,
 | 
				
			||||||
 | 
					                    'title': 'Art'
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'params': {
 | 
				
			||||||
 | 
					            'skip_download': True
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }]
 | 
					    }]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _download_info(self, item_id):
 | 
					    def _download_info(self, item_id):
 | 
				
			||||||
@@ -259,16 +294,24 @@ class TwitchVodIE(TwitchBaseIE):
 | 
				
			|||||||
                    'channelLogin': '',
 | 
					                    'channelLogin': '',
 | 
				
			||||||
                    'videoID': item_id,
 | 
					                    'videoID': item_id,
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
 | 
					            }, {
 | 
				
			||||||
 | 
					                'operationName': 'VideoPlayer_ChapterSelectButtonVideo',
 | 
				
			||||||
 | 
					                'variables': {
 | 
				
			||||||
 | 
					                    'includePrivate': False,
 | 
				
			||||||
 | 
					                    'videoID': item_id,
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
            }],
 | 
					            }],
 | 
				
			||||||
            'Downloading stream metadata GraphQL')[0]['data']
 | 
					            'Downloading stream metadata GraphQL')
 | 
				
			||||||
        video = data.get('video')
 | 
					
 | 
				
			||||||
 | 
					        video = traverse_obj(data, (0, 'data', 'video'))
 | 
				
			||||||
 | 
					        video['moments'] = traverse_obj(data, (1, 'data', 'video', 'moments', 'edges', ..., 'node'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if video is None:
 | 
					        if video is None:
 | 
				
			||||||
            raise ExtractorError(
 | 
					            raise ExtractorError(
 | 
				
			||||||
                'Video %s does not exist' % item_id, expected=True)
 | 
					                'Video %s does not exist' % item_id, expected=True)
 | 
				
			||||||
        return self._extract_info_gql(video, item_id)
 | 
					        return self._extract_info_gql(video, item_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @staticmethod
 | 
					    def _extract_info(self, info):
 | 
				
			||||||
    def _extract_info(info):
 | 
					 | 
				
			||||||
        status = info.get('status')
 | 
					        status = info.get('status')
 | 
				
			||||||
        if status == 'recording':
 | 
					        if status == 'recording':
 | 
				
			||||||
            is_live = True
 | 
					            is_live = True
 | 
				
			||||||
@@ -304,8 +347,22 @@ class TwitchVodIE(TwitchBaseIE):
 | 
				
			|||||||
            'is_live': is_live,
 | 
					            'is_live': is_live,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @staticmethod
 | 
					    def _extract_moments(self, info, item_id):
 | 
				
			||||||
    def _extract_info_gql(info, item_id):
 | 
					        for moment in info.get('moments') or []:
 | 
				
			||||||
 | 
					            start_time = int_or_none(moment.get('positionMilliseconds'), 1000)
 | 
				
			||||||
 | 
					            duration = int_or_none(moment.get('durationMilliseconds'), 1000)
 | 
				
			||||||
 | 
					            name = str_or_none(moment.get('description'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if start_time is None or duration is None:
 | 
				
			||||||
 | 
					                self.report_warning(f'Important chapter information missing for chapter {name}', item_id)
 | 
				
			||||||
 | 
					                continue
 | 
				
			||||||
 | 
					            yield {
 | 
				
			||||||
 | 
					                'start_time': start_time,
 | 
				
			||||||
 | 
					                'end_time': start_time + duration,
 | 
				
			||||||
 | 
					                'title': name,
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _extract_info_gql(self, info, item_id):
 | 
				
			||||||
        vod_id = info.get('id') or item_id
 | 
					        vod_id = info.get('id') or item_id
 | 
				
			||||||
        # id backward compatibility for download archives
 | 
					        # id backward compatibility for download archives
 | 
				
			||||||
        if vod_id[0] != 'v':
 | 
					        if vod_id[0] != 'v':
 | 
				
			||||||
@@ -314,6 +371,7 @@ class TwitchVodIE(TwitchBaseIE):
 | 
				
			|||||||
        if thumbnail:
 | 
					        if thumbnail:
 | 
				
			||||||
            for p in ('width', 'height'):
 | 
					            for p in ('width', 'height'):
 | 
				
			||||||
                thumbnail = thumbnail.replace('{%s}' % p, '0')
 | 
					                thumbnail = thumbnail.replace('{%s}' % p, '0')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return {
 | 
					        return {
 | 
				
			||||||
            'id': vod_id,
 | 
					            'id': vod_id,
 | 
				
			||||||
            'title': info.get('title') or 'Untitled Broadcast',
 | 
					            'title': info.get('title') or 'Untitled Broadcast',
 | 
				
			||||||
@@ -324,6 +382,7 @@ class TwitchVodIE(TwitchBaseIE):
 | 
				
			|||||||
            'uploader_id': try_get(info, lambda x: x['owner']['login'], compat_str),
 | 
					            'uploader_id': try_get(info, lambda x: x['owner']['login'], compat_str),
 | 
				
			||||||
            'timestamp': unified_timestamp(info.get('publishedAt')),
 | 
					            'timestamp': unified_timestamp(info.get('publishedAt')),
 | 
				
			||||||
            'view_count': int_or_none(info.get('viewCount')),
 | 
					            'view_count': int_or_none(info.get('viewCount')),
 | 
				
			||||||
 | 
					            'chapters': list(self._extract_moments(info, item_id)),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _real_extract(self, url):
 | 
					    def _real_extract(self, url):
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user