#!/usr/bin/env python from ..common import * from ..extractor import VideoExtractor import hashlib class Bilibili(VideoExtractor): name = "Bilibili" # Bilibili media encoding options, in descending quality order. stream_types = [ {'id': 'flv_p60', 'quality': 116, 'audio_quality': 30280, 'container': 'FLV', 'video_resolution': '1080p', 'desc': '高清 1080P60'}, # 'id': 'hdflv2', 'quality': 112? {'id': 'flv', 'quality': 80, 'audio_quality': 30280, 'container': 'FLV', 'video_resolution': '1080p', 'desc': '高清 1080P'}, {'id': 'flv720_p60', 'quality': 74, 'audio_quality': 30280, 'container': 'FLV', 'video_resolution': '720p', 'desc': '高清 720P60'}, {'id': 'flv720', 'quality': 64, 'audio_quality': 30280, 'container': 'FLV', 'video_resolution': '720p', 'desc': '高清 720P'}, {'id': 'hdmp4', 'quality': 48, 'audio_quality': 30280, 'container': 'MP4', 'video_resolution': '720p', 'desc': '高清 720P (MP4)'}, {'id': 'flv480', 'quality': 32, 'audio_quality': 30280, 'container': 'FLV', 'video_resolution': '480p', 'desc': '清晰 480P'}, {'id': 'flv360', 'quality': 16, 'audio_quality': 30216, 'container': 'FLV', 'video_resolution': '360p', 'desc': '流畅 360P'}, # 'quality': 15? {'id': 'mp4', 'quality': 0}, ] @staticmethod def height_to_quality(height): if height <= 360: return 16 elif height <= 480: return 32 elif height <= 720: return 64 else: return 80 @staticmethod def bilibili_headers(referer=None, cookie=None): # a reasonable UA ua = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36' headers = {'User-Agent': ua} if referer is not None: headers.update({'Referer': referer}) if cookie is not None: headers.update({'Cookie': cookie}) return headers @staticmethod def bilibili_api(avid, cid, qn=0): return 'https://api.bilibili.com/x/player/playurl?avid=%s&cid=%s&qn=%s&type=&otype=json&fnver=0&fnval=16' % (avid, cid, qn) @staticmethod def bilibili_audio_api(sid): return 'https://www.bilibili.com/audio/music-service-c/web/url?sid=%s' % sid @staticmethod def bilibili_audio_info_api(sid): return 'https://www.bilibili.com/audio/music-service-c/web/song/info?sid=%s' % sid @staticmethod def bilibili_audio_menu_info_api(sid): return 'https://www.bilibili.com/audio/music-service-c/web/menu/info?sid=%s' % sid @staticmethod def bilibili_audio_menu_song_api(sid, ps=100): return 'https://www.bilibili.com/audio/music-service-c/web/song/of-menu?sid=%s&pn=1&ps=%s' % (sid, ps) @staticmethod def bilibili_bangumi_api(avid, cid, ep_id, qn=0): return 'https://api.bilibili.com/pgc/player/web/playurl?avid=%s&cid=%s&qn=%s&type=&otype=json&ep_id=%s&fnver=0&fnval=16' % (avid, cid, qn, ep_id) @staticmethod def bilibili_interface_api(cid, qn=0): entropy = 'rbMCKn@KuamXWlPMoJGsKcbiJKUfkPF_8dABscJntvqhRSETg' appkey, sec = ''.join([chr(ord(i) + 2) for i in entropy[::-1]]).split(':') params = 'appkey=%s&cid=%s&otype=json&qn=%s&quality=%s&type=' % (appkey, cid, qn, qn) chksum = hashlib.md5(bytes(params + sec, 'utf8')).hexdigest() return 'https://interface.bilibili.com/v2/playurl?%s&sign=%s' % (params, chksum) @staticmethod def bilibili_live_api(cid): return 'https://api.live.bilibili.com/room/v1/Room/playUrl?cid=%s&quality=0&platform=web' % cid @staticmethod def bilibili_live_room_info_api(room_id): return 'https://api.live.bilibili.com/room/v1/Room/get_info?room_id=%s' % room_id @staticmethod def bilibili_live_room_init_api(room_id): return 'https://api.live.bilibili.com/room/v1/Room/room_init?id=%s' % room_id @staticmethod def bilibili_space_channel_api(mid, cid, ps=100): return 'https://api.bilibili.com/x/space/channel/video?mid=%s&cid=%s&pn=1&ps=%s&order=0&jsonp=jsonp' % (mid, cid, ps) @staticmethod def bilibili_space_favlist_api(vmid, fid, ps=100): return 'https://api.bilibili.com/x/space/fav/arc?vmid=%s&fid=%s&pn=1&ps=%s&order=0&jsonp=jsonp' % (vmid, fid, ps) @staticmethod def bilibili_space_video_api(mid, ps=100): return 'https://space.bilibili.com/ajax/member/getSubmitVideos?mid=%s&page=1&pagesize=%s&order=0&jsonp=jsonp' % (mid, ps) @staticmethod def bilibili_vc_api(video_id): return 'https://api.vc.bilibili.com/clip/v1/video/detail?video_id=%s' % video_id def prepare(self, **kwargs): self.stream_qualities = {s['quality']: s for s in self.stream_types} try: html_content = get_content(self.url, headers=self.bilibili_headers()) except: html_content = '' # live always returns 400 (why?) #self.title = match1(html_content, # r'

bangumi/play/ep # redirect: bangumi.bilibili.com/anime -> bangumi/play/ep elif re.match(r'https?://(www\.)?bilibili\.com/bangumi/play/ss(\d+)', self.url) or \ re.match(r'https?://bangumi\.bilibili\.com/anime/(\d+)/play', self.url): initial_state_text = match1(html_content, r'__INITIAL_STATE__=(.*?);\(function\(\)') # FIXME initial_state = json.loads(initial_state_text) ep_id = initial_state['epList'][0]['id'] self.url = 'https://www.bilibili.com/bangumi/play/ep%s' % ep_id html_content = get_content(self.url, headers=self.bilibili_headers()) # sort it out if re.match(r'https?://(www\.)?bilibili\.com/audio/au(\d+)', self.url): sort = 'audio' elif re.match(r'https?://(www\.)?bilibili\.com/bangumi/play/ep(\d+)', self.url): sort = 'bangumi' elif match1(html_content, r'