]> jfr.im git - yt-dlp.git/blobdiff - yt_dlp/extractor/twitch.py
[cleanup] Add more ruff rules (#10149)
[yt-dlp.git] / yt_dlp / extractor / twitch.py
index 80cba09155dba9448d71d9b043da0c84d6a2fa37..44b19ad13587975c663faaa9f9e093d63611cc9b 100644 (file)
@@ -3,13 +3,9 @@
 import json
 import random
 import re
+import urllib.parse
 
 from .common import InfoExtractor
-from ..compat import (
-    compat_parse_qs,
-    compat_str,
-    compat_urllib_parse_urlparse,
-)
 from ..utils import (
     ExtractorError,
     UserNotLive,
@@ -64,7 +60,7 @@ def _CLIENT_ID(self):
     def _perform_login(self, username, password):
         def fail(message):
             raise ExtractorError(
-                'Unable to login. Twitch said: %s' % message, expected=True)
+                f'Unable to login. Twitch said: {message}', expected=True)
 
         def login_step(page, urlh, note, data):
             form = self._hidden_inputs(page)
@@ -156,12 +152,12 @@ def _download_gql(self, video_id, ops, note, fatal=True):
                 'persistedQuery': {
                     'version': 1,
                     'sha256Hash': self._OPERATION_HASHES[op['operationName']],
-                }
+                },
             }
         return self._download_base_gql(video_id, ops, note)
 
     def _download_access_token(self, video_id, token_kind, param_name):
-        method = '%sPlaybackAccessToken' % token_kind
+        method = f'{token_kind}PlaybackAccessToken'
         ops = {
             'query': '''{
               %s(
@@ -176,11 +172,11 @@ def _download_access_token(self, video_id, token_kind, param_name):
                 value
                 signature
               }
-            }''' % (method, param_name, video_id),
+            }''' % (method, param_name, video_id),  # noqa: UP031
         }
         return self._download_base_gql(
             video_id, ops,
-            'Downloading %s access token GraphQL' % token_kind)['data'][method]
+            f'Downloading {token_kind} access token GraphQL')['data'][method]
 
     def _get_thumbnails(self, thumbnail):
         return [{
@@ -242,8 +238,8 @@ class TwitchVodIE(TwitchBaseIE):
                 {
                     'start_time': 0,
                     'end_time': 17208,
-                    'title': 'League of Legends'
-                }
+                    'title': 'League of Legends',
+                },
             ],
             'live_status': 'was_live',
         },
@@ -301,25 +297,25 @@ class TwitchVodIE(TwitchBaseIE):
                 {
                     'start_time': 0,
                     'end_time': 573,
-                    'title': 'League of Legends'
+                    'title': 'League of Legends',
                 },
                 {
                     'start_time': 573,
                     'end_time': 3922,
-                    'title': 'Legends of Runeterra'
+                    'title': 'Legends of Runeterra',
                 },
                 {
                     'start_time': 3922,
                     'end_time': 11643,
-                    'title': 'Art'
-                }
+                    'title': 'Art',
+                },
             ],
             'live_status': 'was_live',
             'thumbnail': r're:^https?://.*\.jpg$',
             'view_count': int,
         },
         'params': {
-            'skip_download': True
+            'skip_download': True,
         },
     }, {
         'note': 'Storyboards',
@@ -338,18 +334,18 @@ class TwitchVodIE(TwitchBaseIE):
                 {
                     'start_time': 0,
                     'end_time': 573,
-                    'title': 'League of Legends'
+                    'title': 'League of Legends',
                 },
                 {
                     'start_time': 573,
                     'end_time': 3922,
-                    'title': 'Legends of Runeterra'
+                    'title': 'Legends of Runeterra',
                 },
                 {
                     'start_time': 3922,
                     'end_time': 11643,
-                    'title': 'Art'
-                }
+                    'title': 'Art',
+                },
             ],
             'live_status': 'was_live',
             'thumbnail': r're:^https?://.*\.jpg$',
@@ -359,8 +355,8 @@ class TwitchVodIE(TwitchBaseIE):
         },
         'params': {
             'format': 'mhtml',
-            'skip_download': True
-        }
+            'skip_download': True,
+        },
     }, {
         'note': 'VOD with single chapter',
         'url': 'https://www.twitch.tv/videos/1536751224',
@@ -377,17 +373,17 @@ class TwitchVodIE(TwitchBaseIE):
                 {
                     'start_time': 0,
                     'end_time': 8353,
-                    'title': 'League of Legends'
-                }
+                    'title': 'League of Legends',
+                },
             ],
             'live_status': 'was_live',
             'thumbnail': r're:^https?://.*\.jpg$',
             'view_count': int,
         },
         'params': {
-            'skip_download': True
+            'skip_download': True,
         },
-        'expected_warnings': ['Unable to download JSON metadata: HTTP Error 403: Forbidden']
+        'expected_warnings': ['Unable to download JSON metadata: HTTP Error 403: Forbidden'],
     }, {
         'url': 'https://www.twitch.tv/tangotek/schedule?vodID=1822395420',
         'only_matching': True,
@@ -488,7 +484,7 @@ def _extract_info_gql(self, info, item_id):
         vod_id = info.get('id') or item_id
         # id backward compatibility for download archives
         if vod_id[0] != 'v':
-            vod_id = 'v%s' % vod_id
+            vod_id = f'v{vod_id}'
         thumbnail = url_or_none(info.get('previewThumbnailURL'))
         is_live = None
         if thumbnail:
@@ -503,8 +499,8 @@ def _extract_info_gql(self, info, item_id):
             'description': info.get('description'),
             'duration': int_or_none(info.get('lengthSeconds')),
             'thumbnails': self._get_thumbnails(thumbnail),
-            'uploader': try_get(info, lambda x: x['owner']['displayName'], compat_str),
-            'uploader_id': try_get(info, lambda x: x['owner']['login'], compat_str),
+            'uploader': try_get(info, lambda x: x['owner']['displayName'], str),
+            'uploader_id': try_get(info, lambda x: x['owner']['login'], str),
             'timestamp': unified_timestamp(info.get('publishedAt')),
             'view_count': int_or_none(info.get('viewCount')),
             'chapters': list(self._extract_chapters(info, item_id)),
@@ -559,8 +555,8 @@ def _real_extract(self, url):
         self._prefer_source(formats)
         info['formats'] = formats
 
-        parsed_url = compat_urllib_parse_urlparse(url)
-        query = compat_parse_qs(parsed_url.query)
+        parsed_url = urllib.parse.urlparse(url)
+        query = urllib.parse.parse_qs(parsed_url.query)
         if 't' in query:
             info['start_time'] = parse_duration(query['t'][0])
 
@@ -568,7 +564,7 @@ def _real_extract(self, url):
             info['subtitles'] = {
                 'rechat': [{
                     'url': update_url_query(
-                        'https://api.twitch.tv/v5/videos/%s/comments' % vod_id, {
+                        f'https://api.twitch.tv/v5/videos/{vod_id}/comments', {
                             'client_id': self._CLIENT_ID,
                         }),
                     'ext': 'json',
@@ -587,7 +583,7 @@ def _make_video_result(node):
         '_type': 'url_transparent',
         'ie_key': TwitchVodIE.ie_key(),
         'id': 'v' + video_id,
-        'url': 'https://www.twitch.tv/videos/%s' % video_id,
+        'url': f'https://www.twitch.tv/videos/{video_id}',
         'title': node.get('title'),
         'thumbnail': node.get('previewThumbnailURL'),
         'duration': float_or_none(node.get('lengthSeconds')),
@@ -638,7 +634,7 @@ class TwitchPlaylistBaseIE(TwitchBaseIE):
     def _entries(self, channel_name, *args):
         cursor = None
         variables_common = self._make_variables(channel_name, *args)
-        entries_key = '%ss' % self._ENTRY_KIND
+        entries_key = f'{self._ENTRY_KIND}s'
         for page_num in itertools.count(1):
             variables = variables_common.copy()
             variables['limit'] = self._PAGE_LIMIT
@@ -649,7 +645,7 @@ def _entries(self, channel_name, *args):
                     'operationName': self._OPERATION_NAME,
                     'variables': variables,
                 }],
-                'Downloading %ss GraphQL page %s' % (self._NODE_KIND, page_num),
+                f'Downloading {self._NODE_KIND}s GraphQL page {page_num}',
                 fatal=False)
             if not page:
                 break
@@ -671,7 +667,7 @@ def _entries(self, channel_name, *args):
                 if entry:
                     cursor = edge.get('cursor')
                     yield entry
-            if not cursor or not isinstance(cursor, compat_str):
+            if not cursor or not isinstance(cursor, str):
                 break
 
 
@@ -765,7 +761,7 @@ def suitable(cls, url):
                 if any(ie.suitable(url) for ie in (
                     TwitchVideosClipsIE,
                     TwitchVideosCollectionsIE))
-                else super(TwitchVideosIE, cls).suitable(url))
+                else super().suitable(url))
 
     @staticmethod
     def _make_variables(channel_name, broadcast_type, sort):
@@ -782,15 +778,15 @@ def _extract_entry(node):
     def _real_extract(self, url):
         channel_name = self._match_id(url)
         qs = parse_qs(url)
-        filter = qs.get('filter', ['all'])[0]
+        video_filter = qs.get('filter', ['all'])[0]
         sort = qs.get('sort', ['time'])[0]
-        broadcast = self._BROADCASTS.get(filter, self._DEFAULT_BROADCAST)
+        broadcast = self._BROADCASTS.get(video_filter, self._DEFAULT_BROADCAST)
         return self.playlist_result(
             self._entries(channel_name, broadcast.type, sort),
             playlist_id=channel_name,
-            playlist_title='%s - %s sorted by %s'
-            % (channel_name, broadcast.label,
-               self._SORTED_BY.get(sort, self._DEFAULT_SORTED_BY)))
+            playlist_title=(
+                f'{channel_name} - {broadcast.label} '
+                f'sorted by {self._SORTED_BY.get(sort, self._DEFAULT_SORTED_BY)}'))
 
 
 class TwitchVideosClipsIE(TwitchPlaylistBaseIE):
@@ -828,11 +824,11 @@ class TwitchVideosClipsIE(TwitchPlaylistBaseIE):
     _NODE_KIND = 'Clip'
 
     @staticmethod
-    def _make_variables(channel_name, filter):
+    def _make_variables(channel_name, channel_filter):
         return {
             'login': channel_name,
             'criteria': {
-                'filter': filter,
+                'filter': channel_filter,
             },
         }
 
@@ -858,12 +854,12 @@ def _extract_entry(node):
     def _real_extract(self, url):
         channel_name = self._match_id(url)
         qs = parse_qs(url)
-        range = qs.get('range', ['7d'])[0]
-        clip = self._RANGE.get(range, self._DEFAULT_CLIP)
+        date_range = qs.get('range', ['7d'])[0]
+        clip = self._RANGE.get(date_range, self._DEFAULT_CLIP)
         return self.playlist_result(
             self._entries(channel_name, clip.filter),
             playlist_id=channel_name,
-            playlist_title='%s - Clips %s' % (channel_name, clip.label))
+            playlist_title=f'{channel_name} - Clips {clip.label}')
 
 
 class TwitchVideosCollectionsIE(TwitchPlaylistBaseIE):
@@ -907,7 +903,7 @@ def _extract_entry(node):
             '_type': 'url_transparent',
             'ie_key': TwitchCollectionIE.ie_key(),
             'id': collection_id,
-            'url': 'https://www.twitch.tv/collections/%s' % collection_id,
+            'url': f'https://www.twitch.tv/collections/{collection_id}',
             'title': node.get('title'),
             'thumbnail': node.get('thumbnailURL'),
             'duration': float_or_none(node.get('lengthSeconds')),
@@ -919,7 +915,7 @@ def _real_extract(self, url):
         channel_name = self._match_id(url)
         return self.playlist_result(
             self._entries(channel_name), playlist_id=channel_name,
-            playlist_title='%s - Collections' % channel_name)
+            playlist_title=f'{channel_name} - Collections')
 
 
 class TwitchStreamIE(TwitchBaseIE):
@@ -996,7 +992,7 @@ def suitable(cls, url):
                     TwitchVideosClipsIE,
                     TwitchVideosCollectionsIE,
                     TwitchClipsIE))
-                else super(TwitchStreamIE, cls).suitable(url))
+                else super().suitable(url))
 
     def _real_extract(self, url):
         channel_name = self._match_id(url).lower()
@@ -1025,7 +1021,7 @@ def _real_extract(self, url):
 
         if not user:
             raise ExtractorError(
-                '%s does not exist' % channel_name, expected=True)
+                f'{channel_name} does not exist', expected=True)
 
         stream = user['stream']
 
@@ -1046,16 +1042,16 @@ def _real_extract(self, url):
         sq_user = try_get(gql, lambda x: x[1]['data']['user'], dict) or {}
         uploader = sq_user.get('displayName')
         description = try_get(
-            sq_user, lambda x: x['broadcastSettings']['title'], compat_str)
+            sq_user, lambda x: x['broadcastSettings']['title'], str)
 
         thumbnail = url_or_none(try_get(
             gql, lambda x: x[2]['data']['user']['stream']['previewImageURL'],
-            compat_str))
+            str))
 
         title = uploader or channel_name
         stream_type = stream.get('type')
         if stream_type in ['rerun', 'live']:
-            title += ' (%s)' % stream_type
+            title += f' ({stream_type})'
 
         return {
             'id': stream_id,
@@ -1165,7 +1161,7 @@ def _real_extract(self, url):
     }
     viewCount
   }
-}''' % video_id}, 'Downloading clip GraphQL', fatal=False)
+}''' % video_id}, 'Downloading clip GraphQL', fatal=False)  # noqa: UP031
 
         if data:
             clip = try_get(data, lambda x: x['data']['clip'], dict) or clip
@@ -1213,7 +1209,7 @@ def _real_extract(self, url):
             'view_count': int_or_none(clip.get('viewCount')),
             'timestamp': unified_timestamp(clip.get('createdAt')),
             'thumbnails': thumbnails,
-            'creator': try_get(clip, lambda x: x['broadcaster']['displayName'], compat_str),
-            'uploader': try_get(clip, lambda x: x['curator']['displayName'], compat_str),
-            'uploader_id': try_get(clip, lambda x: x['curator']['id'], compat_str),
+            'creator': try_get(clip, lambda x: x['broadcaster']['displayName'], str),
+            'uploader': try_get(clip, lambda x: x['curator']['displayName'], str),
+            'uploader_id': try_get(clip, lambda x: x['curator']['id'], str),
         }