Skip to content

Commit

Permalink
Merge pull request #33 from ZSAIm/zsaim
Browse files Browse the repository at this point in the history
修复下载腾讯视频的不稳定性问题
  • Loading branch information
ZSAIm authored May 4, 2019
2 parents 0ec8778 + 7a665a0 commit 94c19eb
Show file tree
Hide file tree
Showing 19 changed files with 425 additions and 161 deletions.
6 changes: 3 additions & 3 deletions GUIEventBinder.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ def bindEvent():
class File:
@staticmethod
def bineEvent():
items = ('parse', 'settings', 'exit')
items = ('logs', 'settings', 'exit')
FrameDownloader.MenuBar.batchBind(FrameDownloader.MenuBar.File, gui.frame_downloader.menu_bar.file, items)

@staticmethod
def parse(event):
pass
def logs(event):
gui.dialog_dllog.ShowModal()

@staticmethod
def settings(event):
Expand Down
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# 爱奇艺、哔哩哔哩、腾讯视频解析器 (IQIYI-BILIBILI-parser)
# 爱奇艺、哔哩哔哩、腾讯视频解析器 (IQIYI-BILIBILI-TENCENT-parser)

爱奇艺、哔哩哔哩、(腾讯视频)解析下载器。【支持导入会员Cookie下载会员视频】

Expand All @@ -7,9 +7,12 @@


## 更新说明
* **2019/05/04**
* 修复腾讯视频导入Cookie无效问题。
* 添加下载器进行组下载,将M3U8碎片化以单文件下载。

* **2019/05/03**
* 加入支持下载腾讯视频。(由于FFMPEG不支持SAMPLE-AES-CTR解密,所以该程序无法下载电影视频,只支持解析出M3U8)。
*(由于FFMPEG不支持SAMPLE-AES-CTR解密,所以该程序无法下载电影类加密的视频,只支持解析出M3U8,注意需要会员的是需要导入会员Cookie)。
* 加入支持下载腾讯视频。 *(由于FFMPEG不支持SAMPLE-AES-CTR解密,所以该程序无法下载电影类加密的视频,只支持解析出M3U8,注意需要会员的视频是需要导入会员Cookie)。
* 修复bilibili的flv格式合并错误问题。

* **2019/04/28**
Expand Down
36 changes: 36 additions & 0 deletions core/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,34 @@ def getVideoLegalTitle(self):



class BasicUserCookie:
def __init__(self):
self.extra_info = {}

def extract(self, cookie_str):

rex_query = re.compile('((\w+)=(.+?))[;$\s"]')
ele_query = rex_query.findall(cookie_str + ' ')
for i in ele_query:
self.checkQuery(i[0])

def checkQuery(self, query):
if not query:
return
key, value = [i.strip().strip('"').strip("'") for i in splitvalue(query)]
self.extra_info[key] = value

def dumps(self):
_list = ['%s=%s' % (i[0], i[1]) for i in self.extra_info.items()]
return '; '.join(_list)

def extract_headers(self, headers_list):
if headers_list:
for i in headers_list:
self.checkQuery(i.split(';')[0])



class BasicUrlGroup:
def __init__(self, _init_items=None):
self._members = []
Expand Down Expand Up @@ -307,3 +335,11 @@ def __getattr__(self, item):
return object.__getattribute__(self, item)


def matchParse(url, quality, features):
global WEBSITE
res = WEBSITE.parse(url, [quality])
for i in res:
if i.matchFeature(features):
return i

return None
118 changes: 68 additions & 50 deletions core/tencent.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@

import os
from core.common import BasicParser, BasicRespond, BasicVideoInfo, BasicAudioInfo, \
CONTENT_TYPE_MAP, make_query, dict_get_key, format_byte, BasicUrlGroup
from core.common import BasicParser, BasicRespond, BasicVideoInfo, BasicAudioInfo, BasicUserCookie, \
CONTENT_TYPE_MAP, make_query, dict_get_key, format_byte, BasicUrlGroup, raw_decompress
from bs4 import BeautifulSoup
import re
import json
import PyJSCaller
import time
from urllib.parse import splittype, splithost, urlencode, unquote, splitvalue, splitquery
from urllib.parse import splittype, splithost, urlencode, unquote, splitvalue, splitquery, quote
import CommonVar as cv


Expand Down Expand Up @@ -185,16 +185,31 @@ def saveCookie(self, new_cookie_str):
def parse(self, url, quality):

text = self.request(url)
# bs4 = BeautifulSoup(text, features='html.parser')
bs4 = BeautifulSoup(text, features='html.parser')
video_info_rex = re.compile('var VIDEO_INFO\s*=\s*({.+?})\s*</script>')


video_info = json.loads(video_info_rex.search(text).group(1))

s1, s2 = splittype(url)
host, path = splithost(s2)
seg_path = path.split('/')
if seg_path[-2] == 'cover':
url = bs4.find('link', rel='canonical').get('href')
# text = self.request(url)
# video_info_rex = re.compile('var VIDEO_INFO\s*=\s*({.+?})\s*</script>')

# video_info = json.loads(video_info_rex.search(text).group(1))
s1, s2 = splittype(url)
host, path = splithost(s2)
seg_path = path.split('/')

vid = seg_path[-1].split('.html')[0]


# scripts = bs4.findAll('script')
title = video_info['title']

self.auth_refresh()
if self.cookie_str:
self.auth_refresh()

res_videos = []
for i in quality:
Expand All @@ -203,11 +218,7 @@ def parse(self, url, quality):
getckey, createGUID, setdocument = sess.require('getckey', 'createGUID', 'setdocument')
setdocument(url)

s1, s2 = splittype(url)
host, path = splithost(s2)
seg_path = path.split('/')

vid = seg_path[-1].split('.html')[0]

tm = int(time.time())

Expand Down Expand Up @@ -240,7 +251,7 @@ def parse(self, url, quality):
'cKey': ckey.getValue(),
'guid': guid.getValue(),
'defn': i,
'logintoken': str(new_token).replace("'", '"')
"logintoken": new_token
}

req_vinfoparam = vinfoparam.copy()
Expand All @@ -250,11 +261,12 @@ def parse(self, url, quality):

req_data = str(req_globalparams).replace("'", '"').encode('utf-8')
text = self.request(method='POST', url='https://vd.l.qq.com/proxyhttp', data=req_data)

# urlencode()
res_json = json.loads(text)
res_json['vinfo'] = json.loads(res_json['vinfo'])

res_videos.append(TencentRespond(self, res_json, res_json, BasicVideoInfo(url, title, i)))
if res_json['vinfo'].get('vl'):
res_videos.append(TencentRespond(self, res_json, res_json, BasicVideoInfo(url, title, i)))



Expand All @@ -269,33 +281,38 @@ def auth_refresh(self):
new_url = make_query(AUTH_REFRESH_URL, add_params)

res = self.requestRaw(url=new_url)
raw = res.read()
text = raw_decompress(raw, res.info())

res.close()
res_josn = json.loads(text[text.index('{'):])

if res_josn['errcode'] != 0:
raise Exception('导入的cookie不正确,返回:', res_josn['msg'])

self.user.extract_headers(res.info().get_all('set-cookie'))
new_cookie_str = self.user.dumps()
self.saveCookie(new_cookie_str)
self.loadCookie(new_cookie_str)
return True



class TencentUser:
class TencentUser(BasicUserCookie):
def __init__(self):
BasicUserCookie.__init__(self)
self.main_login = ''
self.openid = ''
self.appid = ''
self.access_token = ''
self.vuserid = ''
self.vusession = ''

self.extra_info = {}

def extract(self, cookie_str):

eles = cookie_str.split(';')
for i in eles:
self.checkQuery(i)

def checkQuery(self, query):
key, value = [i.strip() for i in splitvalue(query)]
BasicUserCookie.checkQuery(self, query)
if not query:
return
key, value = [i.strip().strip('"').strip("'") for i in splitvalue(query)]
if key == 'main_login':
self.main_login = value
elif 'openid' in key:
Expand All @@ -308,37 +325,27 @@ def checkQuery(self, query):
self.vuserid = value
elif 'vusession' in key:
self.vusession = value
else:
self.extra_info[key] = value


def dumps(self):
if self.main_login == 'qq':
add_info = {
'main_login': self.main_login,
'vqq_openid': self.openid,
'vqq_appid': self.appid,
'vqq_access_token': self.access_token,
'vqq_vuserid': self.vuserid,
'vqq_vusession': self.vusession,
}
elif self.main_login == 'wx':
add_info = {
'main_login': self.main_login,
'openid': self.openid,
'appid': self.appid,
'access_token': self.access_token,
'vuserid': self.vuserid,
'vusession': self.vusession,
}
else:
raise AttributeError('unknown "main_login"')

add_info.update(self.extra_info)
_list = ['%s=%s' % (i[0], i[1]) for i in add_info.items()]
_list = []
for key, value in self.extra_info.items():
if key == 'main_login':
value = self.main_login
elif 'openid' in key:
value = self.openid
elif 'appid' in key:
value = self.appid
elif 'access_token' in key:
value = self.access_token
elif 'vuserid' in key:
value = self.vuserid
elif 'vusession' in key:
value = self.vusession
_list.append('%s=%s' % (key, value))
return '; '.join(_list)

def extract_headers(self, headers_list):

if headers_list:
for i in headers_list:
self.checkQuery(i.split(';')[0])
Expand Down Expand Up @@ -403,6 +410,8 @@ def getConcatMethod(self):
return cv.MER_CONCAT_DEMUXER




def init():
global TENCENT
TENCENT = Tencent()
Expand All @@ -428,6 +437,15 @@ def parse(url, qualitys):
return TENCENT.parse(url, [QUALITY[i] for i in qualitys])


def matchParse(url, quality, features):
global TENCENT
res = TENCENT.parse(url, [quality])
for i in res:
if i.matchFeature(features):
return i

return None


if __name__ == '__main__':
init()
Expand Down
45 changes: 33 additions & 12 deletions flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@
import nbdler
from zipfile import ZipFile
from core.common import BasicUrlGroup

import traceback
import io

TOOL_REQ_URL = {
'ffmpeg': 'https://ffmpeg.zeranoe.com/builds/win64/static/ffmpeg-3.2-win64-static.zip',
Expand Down Expand Up @@ -199,9 +200,17 @@ def __(sel_res):
except (socket.timeout, URLError, SSLError):
wx.CallAfter(UndoneJob.timeout)
else:
cv.SEL_RES = sel_res
wx.CallAfter(__, sel_res)
if not sel_res:
wx.CallAfter(UndoneJob.empty)
else:
cv.SEL_RES = sel_res
wx.CallAfter(__, sel_res)

@staticmethod
def empty():
dlg = wx.MessageDialog(gui.frame_parse, u'数据返回为空。', u'错误', wx.OK | wx.ICON_ERROR)
dlg.ShowModal()
dlg.Destroy()


@staticmethod
Expand Down Expand Up @@ -259,12 +268,22 @@ def empty():
dlg.ShowModal()
dlg.Destroy()

@staticmethod
def exception(msg):
wx.MessageDialog(gui.frame_parse, msg, u'解析异常', wx.OK | wx.ICON_ERROR).ShowModal()


@staticmethod
def _parse(url, qualitys):
try:
res = parser.parse(url, qualitys)
except (socket.timeout, URLError, SSLError):
wx.CallAfter(FrameParser.ButtonParse.timeout)
except Exception as e:
msg = traceback.format_exc()
# print(traceback.format_exc())
wx.CallAfter(FrameParser.ButtonParse.exception, msg)

else:
if not res:
wx.CallAfter(FrameParser.ButtonParse.empty)
Expand Down Expand Up @@ -295,15 +314,15 @@ def appendItem(res):
gui.frame_parse.SetTitle(res[0].getVideoLegalTitle())


class ButtonPath:
"""Frame Parser Button-[Path] Handler"""
@staticmethod
def handle():
dlg = wx.DirDialog(gui.frame_parse, style=wx.FD_DEFAULT_STYLE)
if dlg.ShowModal() == wx.ID_OK:
gui.frame_parse.textctrl_path.SetValue(dlg.GetPath())
cv.FILEPATH = dlg.GetPath()
dlg.Destroy()
# class ButtonPath:
# """Frame Parser Button-[Path] Handler"""
# @staticmethod
# def handle():
# dlg = wx.DirDialog(gui.frame_parse, style=wx.FD_DEFAULT_STYLE)
# if dlg.ShowModal() == wx.ID_OK:
# gui.frame_parse.textctrl_path.SetValue(dlg.GetPath())
# cv.FILEPATH = dlg.GetPath()
# dlg.Destroy()


class MenuCopyLink:
Expand Down Expand Up @@ -466,6 +485,8 @@ class FrameDownload:
"""Frame Download Handler"""
@staticmethod
def handle():
# io
# gui.dialog_dllog.start_logger()
gui.frame_parse.Hide()
FrameDownload.Download.handle()

Expand Down
Loading

0 comments on commit 94c19eb

Please sign in to comment.