]>
Commit | Line | Data |
---|---|---|
c1edb853 | 1 | from .common import InfoExtractor |
3d2623a8 | 2 | from ..networking import HEADRequest |
c1edb853 | 3 | from ..utils import ( |
c1edb853 | 4 | UserNotLive, |
5 | float_or_none, | |
6 | merge_dicts, | |
7 | str_or_none, | |
8 | traverse_obj, | |
9 | unified_timestamp, | |
10 | url_or_none, | |
11 | ) | |
12 | ||
13 | ||
14 | class KickBaseIE(InfoExtractor): | |
15 | def _real_initialize(self): | |
c8a61a91 | 16 | self._request_webpage( |
17 | HEADRequest('https://kick.com/'), None, 'Setting up session', fatal=False, impersonate=True) | |
c1edb853 | 18 | xsrf_token = self._get_cookies('https://kick.com/').get('XSRF-TOKEN') |
19 | if not xsrf_token: | |
20 | self.write_debug('kick.com did not set XSRF-TOKEN cookie') | |
21 | KickBaseIE._API_HEADERS = { | |
22 | 'Authorization': f'Bearer {xsrf_token.value}', | |
23 | 'X-XSRF-TOKEN': xsrf_token.value, | |
24 | } if xsrf_token else {} | |
25 | ||
26 | def _call_api(self, path, display_id, note='Downloading API JSON', headers={}, **kwargs): | |
27 | return self._download_json( | |
28 | f'https://kick.com/api/v1/{path}', display_id, note=note, | |
c8a61a91 | 29 | headers=merge_dicts(headers, self._API_HEADERS), impersonate=True, **kwargs) |
c1edb853 | 30 | |
31 | ||
32 | class KickIE(KickBaseIE): | |
ef8509c3 | 33 | _VALID_URL = r'https?://(?:www\.)?kick\.com/(?!(?:video|categories|search|auth)(?:[/?#]|$))(?P<id>[\w-]+)' |
c1edb853 | 34 | _TESTS = [{ |
35 | 'url': 'https://kick.com/yuppy', | |
36 | 'info_dict': { | |
37 | 'id': '6cde1-kickrp-joe-flemmingskick-info-heremust-knowmust-see21', | |
38 | 'ext': 'mp4', | |
39 | 'title': str, | |
40 | 'description': str, | |
41 | 'channel': 'yuppy', | |
42 | 'channel_id': '33538', | |
43 | 'uploader': 'Yuppy', | |
44 | 'uploader_id': '33793', | |
45 | 'upload_date': str, | |
46 | 'live_status': 'is_live', | |
47 | 'timestamp': int, | |
48 | 'thumbnail': r're:^https?://.*\.jpg', | |
49 | 'categories': list, | |
50 | }, | |
51 | 'skip': 'livestream', | |
52 | }, { | |
53 | 'url': 'https://kick.com/kmack710', | |
54 | 'only_matching': True, | |
55 | }] | |
56 | ||
57 | def _real_extract(self, url): | |
58 | channel = self._match_id(url) | |
59 | response = self._call_api(f'channels/{channel}', channel) | |
60 | if not traverse_obj(response, 'livestream', expected_type=dict): | |
61 | raise UserNotLive(video_id=channel) | |
62 | ||
63 | return { | |
64 | 'id': str(traverse_obj( | |
65 | response, ('livestream', ('slug', 'id')), get_all=False, default=channel)), | |
66 | 'formats': self._extract_m3u8_formats( | |
67 | response['playback_url'], channel, 'mp4', live=True), | |
68 | 'title': traverse_obj( | |
69 | response, ('livestream', ('session_title', 'slug')), get_all=False, default=''), | |
70 | 'description': traverse_obj(response, ('user', 'bio')), | |
71 | 'channel': channel, | |
72 | 'channel_id': str_or_none(traverse_obj(response, 'id', ('livestream', 'channel_id'))), | |
73 | 'uploader': traverse_obj(response, 'name', ('user', 'username')), | |
74 | 'uploader_id': str_or_none(traverse_obj(response, 'user_id', ('user', 'id'))), | |
75 | 'is_live': True, | |
76 | 'timestamp': unified_timestamp(traverse_obj(response, ('livestream', 'created_at'))), | |
77 | 'thumbnail': traverse_obj( | |
78 | response, ('livestream', 'thumbnail', 'url'), expected_type=url_or_none), | |
79 | 'categories': traverse_obj(response, ('recent_categories', ..., 'name')), | |
80 | } | |
81 | ||
82 | ||
83 | class KickVODIE(KickBaseIE): | |
84 | _VALID_URL = r'https?://(?:www\.)?kick\.com/video/(?P<id>[\da-f]{8}-(?:[\da-f]{4}-){3}[\da-f]{12})' | |
85 | _TESTS = [{ | |
c8a61a91 | 86 | 'url': 'https://kick.com/video/58bac65b-e641-4476-a7ba-3707a35e60e3', |
87 | 'md5': '3870f94153e40e7121a6e46c068b70cb', | |
c1edb853 | 88 | 'info_dict': { |
c8a61a91 | 89 | 'id': '58bac65b-e641-4476-a7ba-3707a35e60e3', |
c1edb853 | 90 | 'ext': 'mp4', |
c8a61a91 | 91 | 'title': '🤠REBIRTH IS BACK!!!!🤠!stake CODE JAREDFPS 🤠', |
92 | 'description': 'md5:02b0c46f9b4197fb545ab09dddb85b1d', | |
93 | 'channel': 'jaredfps', | |
94 | 'channel_id': '26608', | |
95 | 'uploader': 'JaredFPS', | |
96 | 'uploader_id': '26799', | |
97 | 'upload_date': '20240402', | |
98 | 'timestamp': 1712097108, | |
99 | 'duration': 33859.0, | |
c1edb853 | 100 | 'thumbnail': r're:^https?://.*\.jpg', |
c8a61a91 | 101 | 'categories': ['Call of Duty: Warzone'], |
c1edb853 | 102 | }, |
103 | 'params': { | |
104 | 'skip_download': 'm3u8', | |
105 | }, | |
c8a61a91 | 106 | 'expected_warnings': [r'impersonation'], |
c1edb853 | 107 | }] |
108 | ||
109 | def _real_extract(self, url): | |
110 | video_id = self._match_id(url) | |
111 | response = self._call_api(f'video/{video_id}', video_id) | |
112 | ||
113 | return { | |
114 | 'id': video_id, | |
115 | 'formats': self._extract_m3u8_formats(response['source'], video_id, 'mp4'), | |
116 | 'title': traverse_obj( | |
117 | response, ('livestream', ('session_title', 'slug')), get_all=False, default=''), | |
118 | 'description': traverse_obj(response, ('livestream', 'channel', 'user', 'bio')), | |
119 | 'channel': traverse_obj(response, ('livestream', 'channel', 'slug')), | |
120 | 'channel_id': str_or_none(traverse_obj(response, ('livestream', 'channel', 'id'))), | |
121 | 'uploader': traverse_obj(response, ('livestream', 'channel', 'user', 'username')), | |
122 | 'uploader_id': str_or_none(traverse_obj(response, ('livestream', 'channel', 'user_id'))), | |
123 | 'timestamp': unified_timestamp(response.get('created_at')), | |
124 | 'duration': float_or_none(traverse_obj(response, ('livestream', 'duration')), scale=1000), | |
125 | 'thumbnail': traverse_obj( | |
126 | response, ('livestream', 'thumbnail'), expected_type=url_or_none), | |
127 | 'categories': traverse_obj(response, ('livestream', 'categories', ..., 'name')), | |
128 | } |