]> jfr.im git - yt-dlp.git/blame - yt_dlp/extractor/medaltv.py
[extractor] Deprecate `_sort_formats`
[yt-dlp.git] / yt_dlp / extractor / medaltv.py
CommitLineData
38d70284 1import re
2
3from .common import InfoExtractor
4from ..compat import compat_str
5from ..utils import (
6 ExtractorError,
e0ddbd02 7 format_field,
38d70284 8 float_or_none,
9 int_or_none,
10 str_or_none,
07275b70 11 traverse_obj,
38d70284 12)
13
14
15class MedalTVIE(InfoExtractor):
07275b70 16 _VALID_URL = r'https?://(?:www\.)?medal\.tv/(?P<path>games/[^/?#&]+/clips)/(?P<id>[^/?#&]+)'
38d70284 17 _TESTS = [{
07275b70
JL
18 'url': 'https://medal.tv/games/valorant/clips/jTBFnLKdLy15K',
19 'md5': '6930f8972914b6b9fdc2bb3918098ba0',
20 'info_dict': {
21 'id': 'jTBFnLKdLy15K',
22 'ext': 'mp4',
23 'title': "Mornu's clutch",
24 'description': '',
25 'uploader': 'Aciel',
26 'timestamp': 1651628243,
27 'upload_date': '20220504',
28 'uploader_id': '19335460',
29 'uploader_url': 'https://medal.tv/users/19335460',
30 'comment_count': int,
31 'view_count': int,
32 'like_count': int,
33 'duration': 13,
34 }
35 }, {
36 'url': 'https://medal.tv/games/cod%20cold%20war/clips/2mA60jWAGQCBH',
37 'md5': '3d19d426fe0b2d91c26e412684e66a06',
38d70284 38 'info_dict': {
41d1cca3 39 'id': '2mA60jWAGQCBH',
38d70284 40 'ext': 'mp4',
41 'title': 'Quad Cold',
42 'description': 'Medal,https://medal.tv/desktop/',
43 'uploader': 'MowgliSB',
44 'timestamp': 1603165266,
45 'upload_date': '20201020',
41d1cca3 46 'uploader_id': '10619174',
07275b70
JL
47 'thumbnail': 'https://cdn.medal.tv/10619174/thumbnail-34934644-720p.jpg?t=1080p&c=202042&missing',
48 'uploader_url': 'https://medal.tv/users/10619174',
49 'comment_count': int,
50 'view_count': int,
51 'like_count': int,
52 'duration': 23,
38d70284 53 }
54 }, {
07275b70 55 'url': 'https://medal.tv/games/cod%20cold%20war/clips/2um24TWdty0NA',
38d70284 56 'md5': 'b6dc76b78195fff0b4f8bf4a33ec2148',
57 'info_dict': {
41d1cca3 58 'id': '2um24TWdty0NA',
38d70284 59 'ext': 'mp4',
60 'title': 'u tk me i tk u bigger',
61 'description': 'Medal,https://medal.tv/desktop/',
62 'uploader': 'Mimicc',
63 'timestamp': 1605580939,
64 'upload_date': '20201117',
41d1cca3 65 'uploader_id': '5156321',
07275b70
JL
66 'thumbnail': 'https://cdn.medal.tv/5156321/thumbnail-36787208-360p.jpg?t=1080p&c=202046&missing',
67 'uploader_url': 'https://medal.tv/users/5156321',
68 'comment_count': int,
69 'view_count': int,
70 'like_count': int,
71 'duration': 9,
38d70284 72 }
41d1cca3 73 }, {
07275b70 74 'url': 'https://medal.tv/games/valorant/clips/37rMeFpryCC-9',
41d1cca3 75 'only_matching': True,
76 }, {
07275b70 77 'url': 'https://medal.tv/games/valorant/clips/2WRj40tpY_EU9',
41d1cca3 78 'only_matching': True,
38d70284 79 }]
80
81 def _real_extract(self, url):
82 video_id = self._match_id(url)
07275b70
JL
83 path = self._match_valid_url(url).group('path')
84
38d70284 85 webpage = self._download_webpage(url, video_id)
86
07275b70
JL
87 next_data = self._search_json(
88 '<script[^>]*__NEXT_DATA__[^>]*>', webpage,
89 'next data', video_id, end_pattern='</script>', fatal=False)
90
91 build_id = next_data.get('buildId')
92 if not build_id:
93 raise ExtractorError(
94 'Could not find build ID.', video_id=video_id)
95
96 locale = next_data.get('locale', 'en')
97
98 api_response = self._download_json(
99 f'https://medal.tv/_next/data/{build_id}/{locale}/{path}/{video_id}.json', video_id)
38d70284 100
07275b70 101 clip = traverse_obj(api_response, ('pageProps', 'clip')) or {}
38d70284 102 if not clip:
103 raise ExtractorError(
104 'Could not find video information.', video_id=video_id)
105
106 title = clip['contentTitle']
107
108 source_width = int_or_none(clip.get('sourceWidth'))
109 source_height = int_or_none(clip.get('sourceHeight'))
110
111 aspect_ratio = source_width / source_height if source_width and source_height else 16 / 9
112
113 def add_item(container, item_url, height, id_key='format_id', item_id=None):
114 item_id = item_id or '%dp' % height
115 if item_id not in item_url:
116 return
117 width = int(round(aspect_ratio * height))
118 container.append({
119 'url': item_url,
120 id_key: item_id,
121 'width': width,
122 'height': height
123 })
124
125 formats = []
126 thumbnails = []
127 for k, v in clip.items():
128 if not (v and isinstance(v, compat_str)):
129 continue
130 mobj = re.match(r'(contentUrl|thumbnail)(?:(\d+)p)?$', k)
131 if not mobj:
132 continue
133 prefix = mobj.group(1)
134 height = int_or_none(mobj.group(2))
135 if prefix == 'contentUrl':
136 add_item(
137 formats, v, height or source_height,
138 item_id=None if height else 'source')
139 elif prefix == 'thumbnail':
140 add_item(thumbnails, v, height, 'id')
141
142 error = clip.get('error')
143 if not formats and error:
144 if error == 404:
b7da73eb 145 self.raise_no_formats(
38d70284 146 'That clip does not exist.',
147 expected=True, video_id=video_id)
148 else:
b7da73eb 149 self.raise_no_formats(
38d70284 150 'An unknown error occurred ({0}).'.format(error),
151 video_id=video_id)
38d70284 152
153 # Necessary because the id of the author is not known in advance.
154 # Won't raise an issue if no profile can be found as this is optional.
07275b70
JL
155 author = traverse_obj(api_response, ('pageProps', 'profile')) or {}
156 author_id = str_or_none(author.get('userId'))
a70635b8 157 author_url = format_field(author_id, None, 'https://medal.tv/users/%s')
38d70284 158
159 return {
160 'id': video_id,
161 'title': title,
162 'formats': formats,
163 'thumbnails': thumbnails,
164 'description': clip.get('contentDescription'),
165 'uploader': author.get('displayName'),
166 'timestamp': float_or_none(clip.get('created'), 1000),
167 'uploader_id': author_id,
168 'uploader_url': author_url,
169 'duration': int_or_none(clip.get('videoLengthSeconds')),
170 'view_count': int_or_none(clip.get('views')),
171 'like_count': int_or_none(clip.get('likes')),
172 'comment_count': int_or_none(clip.get('comments')),
173 }