[douyutv] Use new api

use lapi for flv info, and html5 api for room info
#10153 #10318
pull/10444/head
steven7851 2016-08-26 07:32:54 +08:00 committed by GitHub
parent 6b18a24e6e
commit b281aad2dc
1 changed files with 43 additions and 44 deletions

View File

@ -3,9 +3,10 @@ from __future__ import unicode_literals
import hashlib import hashlib
import time import time
import uuid
from .common import InfoExtractor from .common import InfoExtractor
from ..utils import (ExtractorError, unescapeHTML) from ..utils import (ExtractorError, unescapeHTML)
from ..compat import (compat_str, compat_basestring) from ..compat import (compat_str, compat_basestring, compat_urllib_parse_urlencode)
class DouyuTVIE(InfoExtractor): class DouyuTVIE(InfoExtractor):
@ -21,7 +22,6 @@ class DouyuTVIE(InfoExtractor):
'description': 're:.*m7show@163\.com.*', 'description': 're:.*m7show@163\.com.*',
'thumbnail': 're:^https?://.*\.jpg$', 'thumbnail': 're:^https?://.*\.jpg$',
'uploader': '7师傅', 'uploader': '7师傅',
'uploader_id': '431925',
'is_live': True, 'is_live': True,
}, },
'params': { 'params': {
@ -37,7 +37,6 @@ class DouyuTVIE(InfoExtractor):
'description': 'md5:746a2f7a253966a06755a912f0acc0d2', 'description': 'md5:746a2f7a253966a06755a912f0acc0d2',
'thumbnail': 're:^https?://.*\.jpg$', 'thumbnail': 're:^https?://.*\.jpg$',
'uploader': 'douyu小漠', 'uploader': 'douyu小漠',
'uploader_id': '3769985',
'is_live': True, 'is_live': True,
}, },
'params': { 'params': {
@ -54,7 +53,6 @@ class DouyuTVIE(InfoExtractor):
'description': 're:.*m7show@163\.com.*', 'description': 're:.*m7show@163\.com.*',
'thumbnail': 're:^https?://.*\.jpg$', 'thumbnail': 're:^https?://.*\.jpg$',
'uploader': '7师傅', 'uploader': '7师傅',
'uploader_id': '431925',
'is_live': True, 'is_live': True,
}, },
'params': { 'params': {
@ -75,19 +73,39 @@ class DouyuTVIE(InfoExtractor):
room_id = self._html_search_regex( room_id = self._html_search_regex(
r'"room_id"\s*:\s*(\d+),', page, 'room id') r'"room_id"\s*:\s*(\d+),', page, 'room id')
config = None room_url = 'http://m.douyu.com/html5/live?roomId=%s' % room_id
room_content = self._download_webpage(room_url, video_id)
room_json = self._parse_json(room_content, video_id, fatal=False)
room = room_json['data']
show_status = room.get('show_status')
# 1 = live, 2 = offline
if show_status == '2':
raise ExtractorError(
'Live stream is offline', expected=True)
flv_json = None
# Douyu API sometimes returns error "Unable to load the requested class: eticket_redis_cache" # Douyu API sometimes returns error "Unable to load the requested class: eticket_redis_cache"
# Retry with different parameters - same parameters cause same errors # Retry with different parameters - same parameters cause same errors
for i in range(5): for i in range(5):
prefix = 'room/%s?aid=android&client_sys=android&time=%d' % ( tt = int(time.time() / 60)
room_id, int(time.time())) did = uuid.uuid4().hex.upper()
auth = hashlib.md5((prefix + '1231').encode('ascii')).hexdigest()
config_page = self._download_webpage( # Decompile core.swf in webpage by ffdec "Search SWFs in memory"
'http://www.douyutv.com/api/v1/%s&auth=%s' % (prefix, auth), # core.swf is encrypted originally, but ffdec can dump memory to get the decrypted one
video_id) # If API changes in the future, just use this way to update
sign_content = '{room_id}{did}A12Svb&%1UUmf@hC{tt}'.format(room_id = room_id, did = did, tt = tt)
sign = hashlib.md5((sign_content).encode('utf-8')).hexdigest()
payload = {'cdn': 'ws', 'rate': '0', 'tt': tt, 'did': did, 'sign': sign}
flv_data = compat_urllib_parse_urlencode(payload)
flv_request_url = 'http://www.douyu.com/lapi/live/getPlay/%s' % room_id
flv_content = self._download_webpage(flv_request_url, video_id, data=flv_data,
headers={'Content-Type': 'application/x-www-form-urlencoded'})
try: try:
config = self._parse_json(config_page, video_id, fatal=False) flv_json = self._parse_json(flv_content, video_id, fatal=False)
except ExtractorError: except ExtractorError:
# Wait some time before retrying to get a different time() value # Wait some time before retrying to get a different time() value
self._sleep(1, video_id, msg_template='%(video_id)s: Error occurs. ' self._sleep(1, video_id, msg_template='%(video_id)s: Error occurs. '
@ -95,54 +113,35 @@ class DouyuTVIE(InfoExtractor):
continue continue
else: else:
break break
if config is None: if flv_json is None:
raise ExtractorError('Unable to fetch API result') raise ExtractorError('Unable to fetch API result')
data = config['data'] flv = flv_json['data']
error_code = config.get('error', 0) error_code = flv_json.get('error', 0)
if error_code is not 0: if error_code is not 0:
error_desc = 'Server reported error %i' % error_code error_desc = 'Server reported error %i' % error_code
if isinstance(data, (compat_str, compat_basestring)): if isinstance(flv, (compat_str, compat_basestring)):
error_desc += ': ' + data error_desc += ': ' + flv
raise ExtractorError(error_desc, expected=True) raise ExtractorError(error_desc, expected=True)
show_status = data.get('show_status') base_url = flv['rtmp_url']
# 1 = live, 2 = offline live_path = flv['rtmp_live']
if show_status == '2':
raise ExtractorError(
'Live stream is offline', expected=True)
base_url = data['rtmp_url'] video_url = '%s/%s' % (base_url, live_path)
live_path = data['rtmp_live']
title = self._live_title(unescapeHTML(data['room_name'])) title = self._live_title(unescapeHTML(room['room_name']))
description = data.get('show_details') description = room.get('notice')
thumbnail = data.get('room_src') thumbnail = room.get('room_src')
uploader = room.get('nickname')
uploader = data.get('nickname')
uploader_id = data.get('owner_uid')
multi_formats = data.get('rtmp_multi_bitrate')
if not isinstance(multi_formats, dict):
multi_formats = {}
multi_formats['live'] = live_path
formats = [{
'url': '%s/%s' % (base_url, format_path),
'format_id': format_id,
'preference': 1 if format_id == 'live' else 0,
} for format_id, format_path in multi_formats.items()]
self._sort_formats(formats)
return { return {
'id': room_id, 'id': room_id,
'display_id': video_id, 'display_id': video_id,
'url': video_url,
'title': title, 'title': title,
'description': description, 'description': description,
'thumbnail': thumbnail, 'thumbnail': thumbnail,
'uploader': uploader, 'uploader': uploader,
'uploader_id': uploader_id,
'formats': formats,
'is_live': True, 'is_live': True,
} }