]> jfr.im git - yt-dlp.git/blame - yt_dlp/extractor/digitalconcerthall.py
[ie/youtube] Suppress "Unavailable videos are hidden" warning (#10159)
[yt-dlp.git] / yt_dlp / extractor / digitalconcerthall.py
CommitLineData
8bcd4048 1from .common import InfoExtractor
8bcd4048 2from ..utils import (
3 ExtractorError,
8bcd4048 4 try_get,
2a4f2e82 5 url_or_none,
8bcd4048 6 urlencode_postdata,
7)
2a4f2e82 8from ..utils.traversal import traverse_obj
8bcd4048 9
10
11class DigitalConcertHallIE(InfoExtractor):
12 IE_DESC = 'DigitalConcertHall extractor'
2a4f2e82 13 _VALID_URL = r'https?://(?:www\.)?digitalconcerthall\.com/(?P<language>[a-z]+)/(?P<type>film|concert|work)/(?P<id>[0-9]+)-?(?P<part>[0-9]+)?'
8bcd4048 14 _OAUTH_URL = 'https://api.digitalconcerthall.com/v2/oauth2/token'
15 _ACCESS_TOKEN = None
16 _NETRC_MACHINE = 'digitalconcerthall'
17 _TESTS = [{
18 'note': 'Playlist with only one video',
19 'url': 'https://www.digitalconcerthall.com/en/concert/53201',
20 'info_dict': {
21 'id': '53201-1',
22 'ext': 'mp4',
23 'composer': 'Kurt Weill',
24 'title': '[Magic Night]',
25 'thumbnail': r're:^https?://images.digitalconcerthall.com/cms/thumbnails.*\.jpg$',
26 'upload_date': '20210624',
27 'timestamp': 1624548600,
28 'duration': 2798,
2a4f2e82 29 'album_artists': ['Members of the Berliner Philharmoniker', 'Simon Rössler'],
30 'composers': ['Kurt Weill'],
8bcd4048 31 },
32 'params': {'skip_download': 'm3u8'},
33 }, {
34 'note': 'Concert with several works and an interview',
35 'url': 'https://www.digitalconcerthall.com/en/concert/53785',
36 'info_dict': {
37 'id': '53785',
2a4f2e82 38 'album_artists': ['Berliner Philharmoniker', 'Kirill Petrenko'],
8bcd4048 39 'title': 'Kirill Petrenko conducts Mendelssohn and Shostakovich',
2a4f2e82 40 'thumbnail': r're:^https?://images.digitalconcerthall.com/cms/thumbnails.*\.jpg$',
8bcd4048 41 },
42 'params': {'skip_download': 'm3u8'},
43 'playlist_count': 3,
55ed4ff7
MAM
44 }, {
45 'url': 'https://www.digitalconcerthall.com/en/film/388',
46 'info_dict': {
47 'id': '388',
48 'ext': 'mp4',
49 'title': 'The Berliner Philharmoniker and Frank Peter Zimmermann',
50 'description': 'md5:cfe25a7044fa4be13743e5089b5b5eb2',
51 'thumbnail': r're:^https?://images.digitalconcerthall.com/cms/thumbnails.*\.jpg$',
52 'upload_date': '20220714',
53 'timestamp': 1657785600,
2a4f2e82 54 'album_artists': ['Frank Peter Zimmermann', 'Benedikt von Bernstorff', 'Jakob von Bernstorff'],
55 },
56 'params': {'skip_download': 'm3u8'},
57 }, {
58 'note': 'Concert with several works and an interview',
59 'url': 'https://www.digitalconcerthall.com/en/work/53785-1',
60 'info_dict': {
61 'id': '53785',
62 'album_artists': ['Berliner Philharmoniker', 'Kirill Petrenko'],
63 'title': 'Kirill Petrenko conducts Mendelssohn and Shostakovich',
64 'thumbnail': r're:^https?://images.digitalconcerthall.com/cms/thumbnails.*\.jpg$',
55ed4ff7
MAM
65 },
66 'params': {'skip_download': 'm3u8'},
2a4f2e82 67 'playlist_count': 1,
8bcd4048 68 }]
69
52efa4b3 70 def _perform_login(self, username, password):
8bcd4048 71 token_response = self._download_json(
72 self._OAUTH_URL,
73 None, 'Obtaining token', errnote='Unable to obtain token', data=urlencode_postdata({
74 'affiliate': 'none',
75 'grant_type': 'device',
76 'device_vendor': 'unknown',
77 'app_id': 'dch.webapp',
78 'app_version': '1.0.0',
79 'client_secret': '2ySLN+2Fwb',
80 }), headers={
81 'Content-Type': 'application/x-www-form-urlencoded',
82 })
83 self._ACCESS_TOKEN = token_response['access_token']
84 try:
85 self._download_json(
86 self._OAUTH_URL,
87 None, note='Logging in', errnote='Unable to login', data=urlencode_postdata({
88 'grant_type': 'password',
89 'username': username,
90 'password': password,
91 }), headers={
92 'Content-Type': 'application/x-www-form-urlencoded',
93 'Referer': 'https://www.digitalconcerthall.com',
add96eb9 94 'Authorization': f'Bearer {self._ACCESS_TOKEN}',
8bcd4048 95 })
96 except ExtractorError:
97 self.raise_login_required(msg='Login info incorrect')
98
99 def _real_initialize(self):
52efa4b3 100 if not self._ACCESS_TOKEN:
101 self.raise_login_required(method='password')
8bcd4048 102
55ed4ff7 103 def _entries(self, items, language, type_, **kwargs):
8bcd4048 104 for item in items:
105 video_id = item['id']
106 stream_info = self._download_json(
107 self._proto_relative_url(item['_links']['streams']['href']), video_id, headers={
108 'Accept': 'application/json',
109 'Authorization': f'Bearer {self._ACCESS_TOKEN}',
add96eb9 110 'Accept-Language': language,
8bcd4048 111 })
112
2a4f2e82 113 formats = []
114 for m3u8_url in traverse_obj(stream_info, ('channel', ..., 'stream', ..., 'url', {url_or_none})):
115 formats.extend(self._extract_m3u8_formats(m3u8_url, video_id, 'mp4', fatal=False))
8bcd4048 116
117 yield {
118 'id': video_id,
119 'title': item.get('title'),
120 'composer': item.get('name_composer'),
8bcd4048 121 'formats': formats,
122 'duration': item.get('duration_total'),
123 'timestamp': traverse_obj(item, ('date', 'published')),
124 'description': item.get('short_description') or stream_info.get('short_description'),
125 **kwargs,
126 'chapters': [{
127 'start_time': chapter.get('time'),
128 'end_time': try_get(chapter, lambda x: x['time'] + x['duration']),
129 'title': chapter.get('text'),
55ed4ff7 130 } for chapter in item['cuepoints']] if item.get('cuepoints') and type_ == 'concert' else None,
8bcd4048 131 }
132
133 def _real_extract(self, url):
2a4f2e82 134 language, type_, video_id, part = self._match_valid_url(url).group('language', 'type', 'id', 'part')
8bcd4048 135 if not language:
136 language = 'en'
137
2a4f2e82 138 api_type = 'concert' if type_ == 'work' else type_
8bcd4048 139 vid_info = self._download_json(
2a4f2e82 140 f'https://api.digitalconcerthall.com/v2/{api_type}/{video_id}', video_id, headers={
8bcd4048 141 'Accept': 'application/json',
add96eb9 142 'Accept-Language': language,
8bcd4048 143 })
2a4f2e82 144 album_artists = traverse_obj(vid_info, ('_links', 'artist', ..., 'name'))
55ed4ff7 145 videos = [vid_info] if type_ == 'film' else traverse_obj(vid_info, ('_embedded', ..., ...))
8bcd4048 146
2a4f2e82 147 if type_ == 'work':
148 videos = [videos[int(part) - 1]]
149
150 thumbnail = traverse_obj(vid_info, (
151 'image', ..., {self._proto_relative_url}, {url_or_none},
152 {lambda x: x.format(width=0, height=0)}, any)) # NB: 0x0 is the original size
153
8bcd4048 154 return {
155 '_type': 'playlist',
156 'id': video_id,
157 'title': vid_info.get('title'),
2a4f2e82 158 'entries': self._entries(
159 videos, language, type_, thumbnail=thumbnail, album_artists=album_artists),
160 'thumbnail': thumbnail,
161 'album_artists': album_artists,
8bcd4048 162 }