1 from .common
import InfoExtractor
12 class DigitalConcertHallIE(InfoExtractor
):
13 IE_DESC
= 'DigitalConcertHall extractor'
14 _VALID_URL
= r
'https?://(?:www\.)?digitalconcerthall\.com/(?P<language>[a-z]+)/(?P<type>film|concert)/(?P<id>[0-9]+)'
15 _OAUTH_URL
= 'https://api.digitalconcerthall.com/v2/oauth2/token'
17 _NETRC_MACHINE
= 'digitalconcerthall'
19 'note': 'Playlist with only one video',
20 'url': 'https://www.digitalconcerthall.com/en/concert/53201',
24 'composer': 'Kurt Weill',
25 'title': '[Magic Night]',
26 'thumbnail': r
're:^https?://images.digitalconcerthall.com/cms/thumbnails.*\.jpg$',
27 'upload_date': '20210624',
28 'timestamp': 1624548600,
30 'album_artist': 'Members of the Berliner Philharmoniker / Simon Rössler',
32 'params': {'skip_download': 'm3u8'}
,
34 'note': 'Concert with several works and an interview',
35 'url': 'https://www.digitalconcerthall.com/en/concert/53785',
38 'album_artist': 'Berliner Philharmoniker / Kirill Petrenko',
39 'title': 'Kirill Petrenko conducts Mendelssohn and Shostakovich',
41 'params': {'skip_download': 'm3u8'}
,
44 'url': 'https://www.digitalconcerthall.com/en/film/388',
48 'title': 'The Berliner Philharmoniker and Frank Peter Zimmermann',
49 'description': 'md5:cfe25a7044fa4be13743e5089b5b5eb2',
50 'thumbnail': r
're:^https?://images.digitalconcerthall.com/cms/thumbnails.*\.jpg$',
51 'upload_date': '20220714',
52 'timestamp': 1657785600,
53 'album_artist': 'Frank Peter Zimmermann / Benedikt von Bernstorff / Jakob von Bernstorff',
55 'params': {'skip_download': 'm3u8'}
,
58 def _perform_login(self
, username
, password
):
59 token_response
= self
._download
_json
(
61 None, 'Obtaining token', errnote
='Unable to obtain token', data
=urlencode_postdata({
63 'grant_type': 'device',
64 'device_vendor': 'unknown',
65 'app_id': 'dch.webapp',
66 'app_version': '1.0.0',
67 'client_secret': '2ySLN+2Fwb',
69 'Content-Type': 'application/x-www-form-urlencoded',
71 self
._ACCESS
_TOKEN
= token_response
['access_token']
75 None, note
='Logging in', errnote
='Unable to login', data
=urlencode_postdata({
76 'grant_type': 'password',
80 'Content-Type': 'application/x-www-form-urlencoded',
81 'Referer': 'https://www.digitalconcerthall.com',
82 'Authorization': f
'Bearer {self._ACCESS_TOKEN}'
84 except ExtractorError
:
85 self
.raise_login_required(msg
='Login info incorrect')
87 def _real_initialize(self
):
88 if not self
._ACCESS
_TOKEN
:
89 self
.raise_login_required(method
='password')
91 def _entries(self
, items
, language
, type_
, **kwargs
):
94 stream_info
= self
._download
_json
(
95 self
._proto
_relative
_url
(item
['_links']['streams']['href']), video_id
, headers
={
96 'Accept': 'application/json',
97 'Authorization': f
'Bearer {self._ACCESS_TOKEN}',
98 'Accept-Language': language
101 m3u8_url
= traverse_obj(
102 stream_info
, ('channel', lambda k
, _
: k
.startswith('vod_mixed'), 'stream', 0, 'url'), get_all
=False)
103 formats
= self
._extract
_m
3u8_formats
(m3u8_url
, video_id
, 'mp4', 'm3u8_native', fatal
=False)
107 'title': item
.get('title'),
108 'composer': item
.get('name_composer'),
111 'duration': item
.get('duration_total'),
112 'timestamp': traverse_obj(item
, ('date', 'published')),
113 'description': item
.get('short_description') or stream_info
.get('short_description'),
116 'start_time': chapter
.get('time'),
117 'end_time': try_get(chapter
, lambda x
: x
['time'] + x
['duration']),
118 'title': chapter
.get('text'),
119 } for chapter
in item
['cuepoints']] if item
.get('cuepoints') and type_
== 'concert' else None,
122 def _real_extract(self
, url
):
123 language
, type_
, video_id
= self
._match
_valid
_url
(url
).group('language', 'type', 'id')
127 thumbnail_url
= self
._html
_search
_regex
(
128 r
'(https?://images\.digitalconcerthall\.com/cms/thumbnails/.*\.jpg)',
129 self
._download
_webpage
(url
, video_id
), 'thumbnail')
131 'url': thumbnail_url
,
132 **parse_resolution(thumbnail_url
)
135 vid_info
= self
._download
_json
(
136 f
'https://api.digitalconcerthall.com/v2/{type_}/{video_id}', video_id
, headers
={
137 'Accept': 'application/json',
138 'Accept-Language': language
140 album_artist
= ' / '.join(traverse_obj(vid_info
, ('_links', 'artist', ..., 'name')) or '')
141 videos
= [vid_info
] if type_
== 'film' else traverse_obj(vid_info
, ('_embedded', ..., ...))
146 'title': vid_info
.get('title'),
147 'entries': self
._entries
(videos
, language
, thumbnails
=thumbnails
, album_artist
=album_artist
, type_
=type_
),
148 'thumbnails': thumbnails
,
149 'album_artist': album_artist
,