]>
Raphaël G. Git Repositories - youtubedl/blob - youtube_dl/extractor/adn.py
   2 from __future__ 
import unicode_literals
 
  10 from .common 
import InfoExtractor
 
  11 from ..aes 
import aes_cbc_decrypt
 
  12 from ..compat 
import ( 
  29 class ADNIE(InfoExtractor
): 
  30     IE_DESC 
= 'Anime Digital Network' 
  31     _VALID_URL 
= r
'https?://(?:www\.)?animedigitalnetwork\.fr/video/[^/]+/(?P<id>\d+)' 
  33         'url': 'http://animedigitalnetwork.fr/video/blue-exorcist-kyoto-saga/7778-episode-1-debut-des-hostilites', 
  34         'md5': 'e497370d847fd79d9d4c74be55575c7a', 
  38             'title': 'Blue Exorcist - Kyôto Saga - Épisode 1', 
  39             'description': 'md5:2f7b5aa76edbc1a7a92cedcda8a528d5', 
  42     _BASE_URL 
= 'http://animedigitalnetwork.fr' 
  43     _RSA_KEY 
= (0xc35ae1e4356b65a73b551493da94b8cb443491c0aa092a357a5aee57ffc14dda85326f42d716e539a34542a0d3f363adf16c5ec222d713d5997194030ee2e4f0d1fb328c01a81cf6868c090d50de8e169c6b13d1675b9eeed1cbc51e1fffca9b38af07f37abd790924cd3bee59d0257cfda4fe5f3f0534877e21ce5821447d1b, 65537) 
  54     def _ass_subtitles_timecode(seconds
): 
  55         return '%01d:%02d:%02d.%02d' % (seconds 
/ 3600, (seconds 
% 3600) / 60, seconds 
% 60, (seconds 
% 1) * 100) 
  57     def _get_subtitles(self
, sub_path
, video_id
): 
  61         enc_subtitles 
= self
._download
_webpage
( 
  62             urljoin(self
._BASE
_URL
, sub_path
), 
  63             video_id
, 'Downloading subtitles location', fatal
=False) or '{}' 
  64         subtitle_location 
= (self
._parse
_json
(enc_subtitles
, video_id
, fatal
=False) or {}).get('location') 
  66             enc_subtitles 
= self
._download
_webpage
( 
  67                 urljoin(self
._BASE
_URL
, subtitle_location
), 
  68                 video_id
, 'Downloading subtitles data', fatal
=False, 
  69                 headers
={'Origin': 'https://animedigitalnetwork.fr'}) 
  73         # http://animedigitalnetwork.fr/components/com_vodvideo/videojs/adn-vjs.min.js 
  74         dec_subtitles 
= intlist_to_bytes(aes_cbc_decrypt( 
  75             bytes_to_intlist(compat_b64decode(enc_subtitles
[24:])), 
  76             bytes_to_intlist(binascii
.unhexlify(self
._K 
+ '4b8ef13ec1872730')), 
  77             bytes_to_intlist(compat_b64decode(enc_subtitles
[:24])) 
  79         subtitles_json 
= self
._parse
_json
( 
  80             dec_subtitles
[:-compat_ord(dec_subtitles
[-1])].decode(), 
  82         if not subtitles_json
: 
  86         for sub_lang
, sub 
in subtitles_json
.items(): 
  87             ssa 
= '''[Script Info] 
  90 Format: Name,Fontname,Fontsize,PrimaryColour,SecondaryColour,TertiaryColour,BackColour,Bold,Italic,BorderStyle,Outline,Shadow,Alignment,MarginL,MarginR,MarginV,AlphaLevel,Encoding 
  91 Style: Default,Arial,18,16777215,16777215,16777215,0,-1,0,1,1,0,2,20,20,20,0,0 
  93 Format: Marked,Start,End,Style,Name,MarginL,MarginR,MarginV,Effect,Text''' 
  95                 start
, end
, text
, line_align
, position_align 
= ( 
  96                     float_or_none(current
.get('startTime')), 
  97                     float_or_none(current
.get('endTime')), 
  98                     current
.get('text'), current
.get('lineAlign'), 
  99                     current
.get('positionAlign')) 
 100                 if start 
is None or end 
is None or text 
is None: 
 102                 alignment 
= self
._POS
_ALIGN
_MAP
.get(position_align
, 2) + self
._LINE
_ALIGN
_MAP
.get(line_align
, 0) 
 103                 ssa 
+= os
.linesep 
+ 'Dialogue: Marked=0,%s,%s,Default,,0,0,0,,%s%s' % ( 
 104                     self
._ass
_subtitles
_timecode
(start
), 
 105                     self
._ass
_subtitles
_timecode
(end
), 
 106                     '{\\a%d}' % alignment 
if alignment 
!= 2 else '', 
 107                     text
.replace('\n', '\\N').replace('<i>', '{\\i1}').replace('</i>', '{\\i0}')) 
 109             if sub_lang 
== 'vostf': 
 111             subtitles
.setdefault(sub_lang
, []).extend([{ 
 113                 'data': json
.dumps(sub
), 
 120     def _real_extract(self
, url
): 
 121         video_id 
= self
._match
_id
(url
) 
 122         webpage 
= self
._download
_webpage
(url
, video_id
) 
 123         player_config 
= self
._parse
_json
(self
._search
_regex
( 
 124             r
'playerConfig\s*=\s*({.+});', webpage
, 
 125             'player config', default
='{}'), video_id
, fatal
=False) 
 126         if not player_config
: 
 127             config_url 
= urljoin(self
._BASE
_URL
, self
._search
_regex
( 
 128                 r
'(?:id="player"|class="[^"]*adn-player-container[^"]*")[^>]+data-url="([^"]+)"', 
 129                 webpage
, 'config url')) 
 130             player_config 
= self
._download
_json
( 
 131                 config_url
, video_id
, 
 132                 'Downloading player config JSON metadata')['player'] 
 135         video_info_str 
= self
._search
_regex
( 
 136             r
'videoInfo\s*=\s*({.+});', webpage
, 
 137             'video info', fatal
=False) 
 139             video_info 
= self
._parse
_json
( 
 140                 video_info_str
, video_id
, fatal
=False) or {} 
 142         options 
= player_config
.get('options') or {} 
 143         metas 
= options
.get('metas') or {} 
 144         links 
= player_config
.get('links') or {} 
 145         sub_path 
= player_config
.get('subtitles') 
 148             links_url 
= player_config
.get('linksurl') or options
['videoUrl'] 
 149             token 
= options
['token'] 
 150             self
._K 
= ''.join([random
.choice('0123456789abcdef') for _ 
in range(16)]) 
 151             message 
= bytes_to_intlist(json
.dumps({ 
 156             padded_message 
= intlist_to_bytes(pkcs1pad(message
, 128)) 
 158             encrypted_message 
= long_to_bytes(pow(bytes_to_long(padded_message
), e
, n
)) 
 159             authorization 
= base64
.b64encode(encrypted_message
).decode() 
 160             links_data 
= self
._download
_json
( 
 161                 urljoin(self
._BASE
_URL
, links_url
), video_id
, 
 162                 'Downloading links JSON metadata', headers
={ 
 163                     'Authorization': 'Bearer ' + authorization
, 
 165             links 
= links_data
.get('links') or {} 
 166             metas 
= metas 
or links_data
.get('meta') or {} 
 167             sub_path 
= sub_path 
or links_data
.get('subtitles') or \
 
 168                 'index.php?option=com_vodapi&task=subtitles.getJSON&format=json&id=' + video_id
 
 169             sub_path 
+= '&token=' + token
 
 170             error 
= links_data
.get('error') 
 171         title 
= metas
.get('title') or video_info
['title'] 
 174         for format_id
, qualities 
in links
.items(): 
 175             if not isinstance(qualities
, dict): 
 177             for quality
, load_balancer_url 
in qualities
.items(): 
 178                 load_balancer_data 
= self
._download
_json
( 
 179                     load_balancer_url
, video_id
, 
 180                     'Downloading %s %s JSON metadata' % (format_id
, quality
), 
 182                 m3u8_url 
= load_balancer_data
.get('location') 
 185                 m3u8_formats 
= self
._extract
_m
3u8_formats
( 
 186                     m3u8_url
, video_id
, 'mp4', 'm3u8_native', 
 187                     m3u8_id
=format_id
, fatal
=False) 
 188                 if format_id 
== 'vf': 
 189                     for f 
in m3u8_formats
: 
 191                 formats
.extend(m3u8_formats
) 
 193             error 
= options
.get('error') 
 194         if not formats 
and error
: 
 195             raise ExtractorError('%s said: %s' % (self
.IE_NAME
, error
), expected
=True) 
 196         self
._sort
_formats
(formats
) 
 201             'description': strip_or_none(metas
.get('summary') or video_info
.get('resume')), 
 202             'thumbnail': video_info
.get('image'), 
 204             'subtitles': self
.extract_subtitles(sub_path
, video_id
), 
 205             'episode': metas
.get('subtitle') or video_info
.get('videoTitle'), 
 206             'series': video_info
.get('playlistTitle'),