2 from __future__ 
import unicode_literals
 
   9 from .common 
import InfoExtractor
 
  10 from ..aes 
import aes_cbc_decrypt
 
  11 from ..compat 
import compat_urllib_parse_unquote
 
  26 class DRTVIE(InfoExtractor
): 
  27     _VALID_URL 
= r
'https?://(?:www\.)?dr\.dk/(?:tv/se|nyheder|radio(?:/ondemand)?)/(?:[^/]+/)*(?P<id>[\da-z-]+)(?:[/#?]|$)' 
  29     _GEO_COUNTRIES 
= ['DK'] 
  32         'url': 'https://www.dr.dk/tv/se/boern/ultra/klassen-ultra/klassen-darlig-taber-10', 
  33         'md5': '25e659cccc9a2ed956110a299fdf5983', 
  35             'id': 'klassen-darlig-taber-10', 
  37             'title': 'Klassen - Dårlig taber (10)', 
  38             'description': 'md5:815fe1b7fa656ed80580f31e8b3c79aa', 
  39             'timestamp': 1539085800, 
  40             'upload_date': '20181009', 
  43             'season': 'Klassen I', 
  45             'season_id': 'urn:dr:mu:bundle:57d7e8216187a4031cfd6f6b', 
  46             'episode': 'Episode 10', 
  50         'expected_warnings': ['Unable to download f4m manifest'], 
  53         'url': 'https://www.dr.dk/nyheder/indland/live-christianias-rydning-af-pusher-street-er-i-gang', 
  55             'id': 'urn:dr:mu:programcard:57c926176187a50a9c6e83c6', 
  57             'title': 'christiania pusher street ryddes drdkrjpo', 
  58             'description': 'md5:2a71898b15057e9b97334f61d04e6eb5', 
  59             'timestamp': 1472800279, 
  60             'upload_date': '20160902', 
  64             'skip_download': True, 
  66         'expected_warnings': ['Unable to download f4m manifest'], 
  68         # with SignLanguage formats 
  69         'url': 'https://www.dr.dk/tv/se/historien-om-danmark/-/historien-om-danmark-stenalder', 
  71             'id': 'historien-om-danmark-stenalder', 
  73             'title': 'Historien om Danmark: Stenalder', 
  74             'description': 'md5:8c66dcbc1669bbc6f873879880f37f2a', 
  75             'timestamp': 1546628400, 
  76             'upload_date': '20190104', 
  78             'formats': 'mincount:20', 
  81             'skip_download': True, 
  84         'url': 'https://www.dr.dk/radio/p4kbh/regionale-nyheder-kh4/p4-nyheder-2019-06-26-17-30-9', 
  85         'only_matching': True, 
  88     def _real_extract(self
, url
): 
  89         video_id 
= self
._match
_id
(url
) 
  91         webpage 
= self
._download
_webpage
(url
, video_id
) 
  93         if '>Programmet er ikke længere tilgængeligt' in webpage
: 
  95                 'Video %s is not available' % video_id
, expected
=True) 
  97         video_id 
= self
._search
_regex
( 
  98             (r
'data-(?:material-identifier|episode-slug)="([^"]+)"', 
  99              r
'data-resource="[^>"]+mu/programcard/expanded/([^"]+)"'), 
 100             webpage
, 'video id', default
=None) 
 103             video_id 
= compat_urllib_parse_unquote(self
._search
_regex
( 
 104                 r
'(urn(?:%3A|:)dr(?:%3A|:)mu(?:%3A|:)programcard(?:%3A|:)[\da-f]+)', 
 107         data 
= self
._download
_json
( 
 108             'https://www.dr.dk/mu-online/api/1.4/programcard/%s' % video_id
, 
 109             video_id
, 'Downloading video JSON', query
={'expanded': 'true'}) 
 111         title 
= str_or_none(data
.get('Title')) or re
.sub( 
 112             r
'\s*\|\s*(?:TV\s*\|\s*DR|DRTV)$', '', 
 113             self
._og
_search
_title
(webpage
)) 
 114         description 
= self
._og
_search
_description
( 
 115             webpage
, default
=None) or data
.get('Description') 
 117         timestamp 
= unified_timestamp( 
 118             data
.get('PrimaryBroadcastStartTime') or data
.get('SortDateTime')) 
 123         restricted_to_denmark 
= False 
 129         primary_asset 
= data
.get('PrimaryAsset') 
 130         if isinstance(primary_asset
, dict): 
 131             assets
.append(primary_asset
) 
 132         secondary_assets 
= data
.get('SecondaryAssets') 
 133         if isinstance(secondary_assets
, list): 
 134             for secondary_asset 
in secondary_assets
: 
 135                 if isinstance(secondary_asset
, dict): 
 136                     assets
.append(secondary_asset
) 
 138         def hex_to_bytes(hex): 
 139             return binascii
.a2b_hex(hex.encode('ascii')) 
 144             data 
= bytes_to_intlist(hex_to_bytes(e
[10:10 + n
])) 
 145             key 
= bytes_to_intlist(hashlib
.sha256( 
 146                 ('%s:sRBzYNXBzkKgnjj8pGtkACch' % a
).encode('utf-8')).digest()) 
 147             iv 
= bytes_to_intlist(hex_to_bytes(a
)) 
 148             decrypted 
= aes_cbc_decrypt(data
, key
, iv
) 
 149             return intlist_to_bytes( 
 150                 decrypted
[:-decrypted
[-1]]).decode('utf-8').split('?')[0] 
 153             kind 
= asset
.get('Kind') 
 155                 thumbnail 
= url_or_none(asset
.get('Uri')) 
 156             elif kind 
in ('VideoResource', 'AudioResource'): 
 157                 duration 
= float_or_none(asset
.get('DurationInMilliseconds'), 1000) 
 158                 restricted_to_denmark 
= asset
.get('RestrictedToDenmark') 
 159                 asset_target 
= asset
.get('Target') 
 160                 for link 
in asset
.get('Links', []): 
 161                     uri 
= link
.get('Uri') 
 163                         encrypted_uri 
= link
.get('EncryptedUri') 
 164                         if not encrypted_uri
: 
 167                             uri 
= decrypt_uri(encrypted_uri
) 
 170                                 'Unable to decrypt EncryptedUri', video_id
) 
 172                     uri 
= url_or_none(uri
) 
 175                     target 
= link
.get('Target') 
 176                     format_id 
= target 
or '' 
 177                     if asset_target 
in ('SpokenSubtitles', 'SignLanguage', 'VisuallyInterpreted'): 
 179                         format_id 
+= '-%s' % asset_target
 
 180                     elif asset_target 
== 'Default': 
 185                         f4m_formats 
= self
._extract
_f
4m
_formats
( 
 186                             uri 
+ '?hdcore=3.3.0&plugin=aasp-3.3.0.99.43', 
 187                             video_id
, preference
, f4m_id
=format_id
, fatal
=False) 
 188                         if kind 
== 'AudioResource': 
 189                             for f 
in f4m_formats
: 
 191                         formats
.extend(f4m_formats
) 
 192                     elif target 
== 'HLS': 
 193                         formats
.extend(self
._extract
_m
3u8_formats
( 
 194                             uri
, video_id
, 'mp4', entry_protocol
='m3u8_native', 
 195                             preference
=preference
, m3u8_id
=format_id
, 
 198                         bitrate 
= link
.get('Bitrate') 
 200                             format_id 
+= '-%s' % bitrate
 
 203                             'format_id': format_id
, 
 204                             'tbr': int_or_none(bitrate
), 
 205                             'ext': link
.get('FileFormat'), 
 206                             'vcodec': 'none' if kind 
== 'AudioResource' else None, 
 207                             'preference': preference
, 
 209             subtitles_list 
= asset
.get('SubtitlesList') or asset
.get('Subtitleslist') 
 210             if isinstance(subtitles_list
, list): 
 214                 for subs 
in subtitles_list
: 
 215                     if not isinstance(subs
, dict): 
 217                     sub_uri 
= url_or_none(subs
.get('Uri')) 
 220                     lang 
= subs
.get('Language') or 'da' 
 221                     subtitles
.setdefault(LANGS
.get(lang
, lang
), []).append({ 
 223                         'ext': mimetype2ext(subs
.get('MimeType')) or 'vtt' 
 226         if not formats 
and restricted_to_denmark
: 
 227             self
.raise_geo_restricted( 
 228                 'Unfortunately, DR is not allowed to show this program outside Denmark.', 
 229                 countries
=self
._GEO
_COUNTRIES
) 
 231         self
._sort
_formats
(formats
) 
 236             'description': description
, 
 237             'thumbnail': thumbnail
, 
 238             'timestamp': timestamp
, 
 239             'duration': duration
, 
 241             'subtitles': subtitles
, 
 242             'series': str_or_none(data
.get('SeriesTitle')), 
 243             'season': str_or_none(data
.get('SeasonTitle')), 
 244             'season_number': int_or_none(data
.get('SeasonNumber')), 
 245             'season_id': str_or_none(data
.get('SeasonUrn')), 
 246             'episode': str_or_none(data
.get('EpisodeTitle')), 
 247             'episode_number': int_or_none(data
.get('EpisodeNumber')), 
 248             'release_year': int_or_none(data
.get('ProductionYear')), 
 252 class DRTVLiveIE(InfoExtractor
): 
 253     IE_NAME 
= 'drtv:live' 
 254     _VALID_URL 
= r
'https?://(?:www\.)?dr\.dk/(?:tv|TV)/live/(?P<id>[\da-z-]+)' 
 255     _GEO_COUNTRIES 
= ['DK'] 
 257         'url': 'https://www.dr.dk/tv/live/dr1', 
 261             'title': 're:^DR1 [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}$', 
 265             'skip_download': True, 
 269     def _real_extract(self
, url
): 
 270         channel_id 
= self
._match
_id
(url
) 
 271         channel_data 
= self
._download
_json
( 
 272             'https://www.dr.dk/mu-online/api/1.0/channel/' + channel_id
, 
 274         title 
= self
._live
_title
(channel_data
['Title']) 
 277         for streaming_server 
in channel_data
.get('StreamingServers', []): 
 278             server 
= streaming_server
.get('Server') 
 281             link_type 
= streaming_server
.get('LinkType') 
 282             for quality 
in streaming_server
.get('Qualities', []): 
 283                 for stream 
in quality
.get('Streams', []): 
 284                     stream_path 
= stream
.get('Stream') 
 287                     stream_url 
= update_url_query( 
 288                         '%s/%s' % (server
, stream_path
), {'b': ''}) 
 289                     if link_type 
== 'HLS': 
 290                         formats
.extend(self
._extract
_m
3u8_formats
( 
 291                             stream_url
, channel_id
, 'mp4', 
 292                             m3u8_id
=link_type
, fatal
=False, live
=True)) 
 293                     elif link_type 
== 'HDS': 
 294                         formats
.extend(self
._extract
_f
4m
_formats
(update_url_query( 
 295                             '%s/%s' % (server
, stream_path
), {'hdcore': '3.7.0'}), 
 296                             channel_id
, f4m_id
=link_type
, fatal
=False)) 
 297         self
._sort
_formats
(formats
) 
 302             'thumbnail': channel_data
.get('PrimaryImageUri'),