[ie/twitter:spaces] Support video spaces (#10789)
[yt-dlp3.git] / yt_dlp / extractor / pixivsketch.py
blob344cdb3d052d13809afaf15e9f9bdb9ed6eedec3
1 from .common import InfoExtractor
2 from ..utils import (
3 ExtractorError,
4 traverse_obj,
5 unified_timestamp,
9 class PixivSketchBaseIE(InfoExtractor):
10 def _call_api(self, video_id, path, referer, note='Downloading JSON metadata'):
11 response = self._download_json(f'https://sketch.pixiv.net/api/{path}', video_id, note=note, headers={
12 'Referer': referer,
13 'X-Requested-With': referer,
15 errors = traverse_obj(response, ('errors', ..., 'message'))
16 if errors:
17 raise ExtractorError(' '.join(f'{e}.' for e in errors))
18 return response.get('data') or {}
21 class PixivSketchIE(PixivSketchBaseIE):
22 IE_NAME = 'pixiv:sketch'
23 _VALID_URL = r'https?://sketch\.pixiv\.net/@(?P<uploader_id>[a-zA-Z0-9_-]+)/lives/(?P<id>\d+)/?'
24 _TESTS = [{
25 'url': 'https://sketch.pixiv.net/@nuhutya/lives/3654620468641830507',
26 'info_dict': {
27 'id': '7370666691623196569',
28 'title': 'まにあえクリスマス!',
29 'uploader': 'ぬふちゃ',
30 'uploader_id': 'nuhutya',
31 'channel_id': '9844815',
32 'age_limit': 0,
33 'timestamp': 1640351536,
35 'skip': True,
36 }, {
37 # these two (age_limit > 0) requires you to login on website, but it's actually not required for download
38 'url': 'https://sketch.pixiv.net/@namahyou/lives/4393103321546851377',
39 'info_dict': {
40 'id': '4907995960957946943',
41 'title': 'クリスマスなんて知らん🖕',
42 'uploader': 'すゃもり',
43 'uploader_id': 'suya2mori2',
44 'channel_id': '31169300',
45 'age_limit': 15,
46 'timestamp': 1640347640,
48 'skip': True,
49 }, {
50 'url': 'https://sketch.pixiv.net/@8aki/lives/3553803162487249670',
51 'info_dict': {
52 'id': '1593420639479156945',
53 'title': 'おまけ本作業(リョナ有)',
54 'uploader': 'おぶい / Obui',
55 'uploader_id': 'oving',
56 'channel_id': '17606',
57 'age_limit': 18,
58 'timestamp': 1640330263,
60 'skip': True,
63 def _real_extract(self, url):
64 video_id, uploader_id = self._match_valid_url(url).group('id', 'uploader_id')
65 data = self._call_api(video_id, f'lives/{video_id}.json', url)
67 if not traverse_obj(data, 'is_broadcasting'):
68 raise ExtractorError(f'This live is offline. Use https://sketch.pixiv.net/@{uploader_id} for ongoing live.', expected=True)
70 m3u8_url = traverse_obj(data, ('owner', 'hls_movie', 'url'))
71 formats = self._extract_m3u8_formats(
72 m3u8_url, video_id, ext='mp4',
73 entry_protocol='m3u8_native', m3u8_id='hls')
75 return {
76 'id': video_id,
77 'title': data.get('name'),
78 'formats': formats,
79 'uploader': traverse_obj(data, ('user', 'name'), ('owner', 'user', 'name')),
80 'uploader_id': traverse_obj(data, ('user', 'unique_name'), ('owner', 'user', 'unique_name')),
81 'channel_id': str(traverse_obj(data, ('user', 'pixiv_user_id'), ('owner', 'user', 'pixiv_user_id'))),
82 'age_limit': 18 if data.get('is_r18') else 15 if data.get('is_r15') else 0,
83 'timestamp': unified_timestamp(data.get('created_at')),
84 'is_live': True,
88 class PixivSketchUserIE(PixivSketchBaseIE):
89 IE_NAME = 'pixiv:sketch:user'
90 _VALID_URL = r'https?://sketch\.pixiv\.net/@(?P<id>[a-zA-Z0-9_-]+)/?'
91 _TESTS = [{
92 'url': 'https://sketch.pixiv.net/@nuhutya',
93 'only_matching': True,
94 }, {
95 'url': 'https://sketch.pixiv.net/@namahyou',
96 'only_matching': True,
97 }, {
98 'url': 'https://sketch.pixiv.net/@8aki',
99 'only_matching': True,
102 @classmethod
103 def suitable(cls, url):
104 return super().suitable(url) and not PixivSketchIE.suitable(url)
106 def _real_extract(self, url):
107 user_id = self._match_id(url)
108 data = self._call_api(user_id, f'lives/users/@{user_id}.json', url)
110 if not traverse_obj(data, 'is_broadcasting'):
111 try:
112 self._call_api(user_id, 'users/current.json', url, 'Investigating reason for request failure')
113 except ExtractorError as ex:
114 if ex.cause and ex.cause.code == 401:
115 self.raise_login_required(f'Please log in, or use direct link like https://sketch.pixiv.net/@{user_id}/1234567890', method='cookies')
116 raise ExtractorError('This user is offline', expected=True)
118 return self.url_result(f'https://sketch.pixiv.net/@{user_id}/lives/{data["id"]}')