]>
Raphaël G. Git Repositories - youtubedl/blob - youtube_dl/extractor/bilibili.py
80dd8382e4e8758274e3a7ba2418479ee3d2fbbc
   2 from __future__ 
import unicode_literals
 
   7 from .common 
import InfoExtractor
 
  25 class BiliBiliIE(InfoExtractor
): 
  26     _VALID_URL 
= r
'https?://(?:www\.|bangumi\.|)bilibili\.(?:tv|com)/(?:video/av|anime/(?P<anime_id>\d+)/play#)(?P<id>\d+)' 
  29         'url': 'http://www.bilibili.tv/video/av1074402/', 
  30         'md5': '9fa226fe2b8a9a4d5a69b4c6a183417e', 
  35             'description': 'md5:ce18c2a2d2193f0df2917d270f2e5923', 
  37             'timestamp': 1398012660, 
  38             'upload_date': '20140420', 
  39             'thumbnail': r
're:^https?://.+\.jpg', 
  41             'uploader_id': '156160', 
  44         # Tested in BiliBiliBangumiIE 
  45         'url': 'http://bangumi.bilibili.com/anime/1869/play#40062', 
  46         'only_matching': True, 
  48         'url': 'http://bangumi.bilibili.com/anime/5802/play#100643', 
  49         'md5': '3f721ad1e75030cc06faf73587cfec57', 
  53             'title': 'CHAOS;CHILD', 
  54             'description': '如果你是神明,并且能够让妄想成为现实。那你会进行怎么样的妄想?是淫靡的世界?独裁社会?毁灭性的制裁?还是……2015年,涩谷。从6年前发生的大灾害“涩谷地震”之后复兴了的这个街区里新设立的私立高中...', 
  56         'skip': 'Geo-restricted to China', 
  59     _APP_KEY 
= '84956560bc028eb7' 
  60     _BILIBILI_KEY 
= '94aba54af9065f71de72f5508f1cd42e' 
  62     def _report_error(self
, result
): 
  63         if 'message' in result
: 
  64             raise ExtractorError('%s said: %s' % (self
.IE_NAME
, result
['message']), expected
=True) 
  65         elif 'code' in result
: 
  66             raise ExtractorError('%s returns error %d' % (self
.IE_NAME
, result
['code']), expected
=True) 
  68             raise ExtractorError('Can\'t extract Bangumi episode ID') 
  70     def _real_extract(self
, url
): 
  71         url
, smuggled_data 
= unsmuggle_url(url
, {}) 
  73         mobj 
= re
.match(self
._VALID
_URL
, url
) 
  74         video_id 
= mobj
.group('id') 
  75         anime_id 
= mobj
.group('anime_id') 
  76         webpage 
= self
._download
_webpage
(url
, video_id
) 
  78         if 'anime/' not in url
: 
  79             cid 
= compat_parse_qs(self
._search
_regex
( 
  80                 [r
'EmbedPlayer\([^)]+,\s*"([^"]+)"\)', 
  81                  r
'<iframe[^>]+src="https://secure\.bilibili\.com/secure,([^"]+)"'], 
  82                 webpage
, 'player parameters'))['cid'][0] 
  84             if 'no_bangumi_tip' not in smuggled_data
: 
  85                 self
.to_screen('Downloading episode %s. To download all videos in anime %s, re-run youtube-dl with %s' % ( 
  86                     video_id
, anime_id
, compat_urlparse
.urljoin(url
, '//bangumi.bilibili.com/anime/%s' % anime_id
))) 
  88                 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 
  90             headers
.update(self
.geo_verification_headers()) 
  92             js 
= self
._download
_json
( 
  93                 'http://bangumi.bilibili.com/web_api/get_source', video_id
, 
  94                 data
=urlencode_postdata({'episode_id': video_id
}), 
  96             if 'result' not in js
: 
  97                 self
._report
_error
(js
) 
  98             cid 
= js
['result']['cid'] 
 100         payload 
= 'appkey=%s&cid=%s&otype=json&quality=2&type=mp4' % (self
._APP
_KEY
, cid
) 
 101         sign 
= hashlib
.md5((payload 
+ self
._BILIBILI
_KEY
).encode('utf-8')).hexdigest() 
 103         video_info 
= self
._download
_json
( 
 104             'http://interface.bilibili.com/playurl?%s&sign=%s' % (payload
, sign
), 
 105             video_id
, note
='Downloading video info page', 
 106             headers
=self
.geo_verification_headers()) 
 108         if 'durl' not in video_info
: 
 109             self
._report
_error
(video_info
) 
 113         for idx
, durl 
in enumerate(video_info
['durl']): 
 116                 'filesize': int_or_none(durl
['size']), 
 118             for backup_url 
in durl
.get('backup_url', []): 
 121                     # backup URLs have lower priorities 
 122                     'preference': -2 if 'hd.mp4' in backup_url 
else -3, 
 125             self
._sort
_formats
(formats
) 
 128                 'id': '%s_part%s' % (video_id
, idx
), 
 129                 'duration': float_or_none(durl
.get('length'), 1000), 
 133         title 
= self
._html
_search
_regex
('<h1[^>]+title="([^"]+)">', webpage
, 'title') 
 134         description 
= self
._html
_search
_meta
('description', webpage
) 
 135         timestamp 
= unified_timestamp(self
._html
_search
_regex
( 
 136             r
'<time[^>]+datetime="([^"]+)"', webpage
, 'upload time', default
=None)) 
 137         thumbnail 
= self
._html
_search
_meta
(['og:image', 'thumbnailUrl'], webpage
) 
 139         # TODO 'view_count' requires deobfuscating Javascript 
 143             'description': description
, 
 144             'timestamp': timestamp
, 
 145             'thumbnail': thumbnail
, 
 146             'duration': float_or_none(video_info
.get('timelength'), scale
=1000), 
 149         uploader_mobj 
= re
.search( 
 150             r
'<a[^>]+href="(?:https?:)?//space\.bilibili\.com/(?P<id>\d+)"[^>]+title="(?P<name>[^"]+)"', 
 154                 'uploader': uploader_mobj
.group('name'), 
 155                 'uploader_id': uploader_mobj
.group('id'), 
 158         for entry 
in entries
: 
 161         if len(entries
) == 1: 
 164             for idx
, entry 
in enumerate(entries
): 
 165                 entry
['id'] = '%s_part%d' % (video_id
, (idx 
+ 1)) 
 168                 '_type': 'multi_video', 
 171                 'description': description
, 
 176 class BiliBiliBangumiIE(InfoExtractor
): 
 177     _VALID_URL 
= r
'https?://bangumi\.bilibili\.com/anime/(?P<id>\d+)' 
 179     IE_NAME 
= 'bangumi.bilibili.com' 
 180     IE_DESC 
= 'BiliBili番剧' 
 183         'url': 'http://bangumi.bilibili.com/anime/1869', 
 187             'description': 'md5:6a9622b911565794c11f25f81d6a97d2', 
 189         'playlist_count': 26, 
 191         'url': 'http://bangumi.bilibili.com/anime/1869', 
 195             'description': 'md5:6a9622b911565794c11f25f81d6a97d2', 
 198             'md5': '91da8621454dd58316851c27c68b0c13', 
 203                 'description': '故事发生在日本的江户时代。风是一个小酒馆的打工女。一日,酒馆里来了一群恶霸,虽然他们的举动令风十分不满,但是毕竟风只是一届女流,无法对他们采取什么行动,只能在心里嘟哝。这时,酒家里又进来了个“不良份子...', 
 204                 'timestamp': 1414538739, 
 205                 'upload_date': '20141028', 
 206                 'episode': '疾风怒涛 Tempestuous Temperaments', 
 211             'playlist_items': '1', 
 216     def suitable(cls
, url
): 
 217         return False if BiliBiliIE
.suitable(url
) else super(BiliBiliBangumiIE
, cls
).suitable(url
) 
 219     def _real_extract(self
, url
): 
 220         bangumi_id 
= self
._match
_id
(url
) 
 222         # Sometimes this API returns a JSONP response 
 223         season_info 
= self
._download
_json
( 
 224             'http://bangumi.bilibili.com/jsonp/seasoninfo/%s.ver' % bangumi_id
, 
 225             bangumi_id
, transform_source
=strip_jsonp
)['result'] 
 228             '_type': 'url_transparent', 
 229             'url': smuggle_url(episode
['webplay_url'], {'no_bangumi_tip': 1}), 
 230             'ie_key': BiliBiliIE
.ie_key(), 
 231             'timestamp': parse_iso8601(episode
.get('update_time'), delimiter
=' '), 
 232             'episode': episode
.get('index_title'), 
 233             'episode_number': int_or_none(episode
.get('index')), 
 234         } for episode 
in season_info
['episodes']] 
 236         entries 
= sorted(entries
, key
=lambda entry
: entry
.get('episode_number')) 
 238         return self
.playlist_result( 
 240             season_info
.get('bangumi_title'), season_info
.get('evaluate'))