From 1f9cda0c6a0f7e6959cf5f2b76da65200fae4111 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Fri, 30 Oct 2015 10:11:23 +0800 Subject: [PATCH 01/71] [tumblr] use multiprocessing.JoinableQueue --- tumblr.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tumblr.py b/tumblr.py index 7951cc7..3b02e16 100755 --- a/tumblr.py +++ b/tumblr.py @@ -140,6 +140,7 @@ def __init__(self, queue, lock): def run(self): while True: item = self.queue.get() + self.queue.task_done() if not item: break status = download_run(item) @@ -556,7 +557,7 @@ def main(argv): play(xxx, args) lock = multiprocessing.Lock() - queue = multiprocessing.Queue(maxsize=args.processes) + queue = multiprocessing.JoinableQueue(maxsize=args.processes) thrs = [] for i in range(args.processes): thr = Downloader(queue, lock) @@ -603,6 +604,8 @@ def main(argv): for i in range(args.processes): queue.put(None) + queue.join() + for thr in thrs: thr.join() From 61550d44a445affe4ee2ed0a773c4a80af14bc69 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Fri, 30 Oct 2015 15:13:56 +0800 Subject: [PATCH 02/71] [pan.baidu.com] fix #45 --- pan.baidu.com.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index 8a367f6..e1a5d6b 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -2782,16 +2782,16 @@ def get_web_fileinfo(cm, url): t = t.replace('\\\\', '!@#$%^'*10) t = t.replace('\\', '') t = t.replace('!@#$%^'*10, '\\') - info['fileinfo'] = t + info['fileinfo'] = t info['timestamp'] = re.search(r'timestamp="(\d+)"', cm).group(1) - info['sign'] = re.search(r'downloadsign="(.+?)"', cm).group(1) + info['sign'] = re.search(r'downloadsign="(.+?)"', cm).group(1) else: - info['uk'] = re.search(r'yunData\.MYUK = "(\d+)"', cm).group(1) - info['shareid'] = re.search(r'yunData\.SHARE_ID = "(\d+)"', cm).group(1) - info['bdstoken'] = re.search(r'yunData\.MYBDSTOKEN = "(.*?)"', cm).group(1) - info['fileinfo'] = re.search(r'yunData.FILEINFO = (.+)', cm).group(1)[:-2] + info['uk'] = re.search(r'yunData\.MYUK = "(\d+)"', cm).group(1) + info['shareid'] = re.search(r'yunData\.SHARE_ID = "(\d+)"', cm).group(1) + info['bdstoken'] = re.search(r'yunData\.MYBDSTOKEN = "(.*?)"', cm).group(1) + info['fileinfo'] = re.search(r'yunData.FILEINFO = (.+)', cm).group(1)[:-2] info['timestamp'] = re.search(r'yunData.TIMESTAMP = "(.+?)"', cm).group(1) - info['sign'] = re.search(r'yunData.SIGN = "(.+?)"', cm).group(1) + info['sign'] = re.search(r'yunData.SIGN = "(.+?)"', cm).group(1) return info @@ -3116,6 +3116,9 @@ def handle_command(comd, xxx): ' d url1 url2 ..') sys.exit(1) + # login session + panbaiducom_HOME().init() + if comd == 'p' or comd == 'play': args.play = True enter_password() From c4ceb514284bbe8634a1fb3e5032f0dee486c72c Mon Sep 17 00:00:00 2001 From: PeterDing Date: Tue, 17 Nov 2015 10:23:53 +0800 Subject: [PATCH 03/71] [tumblr] disable urllib3 warnings --- tumblr.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tumblr.py b/tumblr.py index 3b02e16..8effbb2 100755 --- a/tumblr.py +++ b/tumblr.py @@ -10,6 +10,7 @@ import collections import multiprocessing import requests +requests.packages.urllib3.disable_warnings() import argparse import random import time From 567e0284c465bafdd3128fd5954d0d3933f4aa90 Mon Sep 17 00:00:00 2001 From: Virgil Hou Date: Sun, 20 Dec 2015 21:15:30 +0800 Subject: [PATCH 04/71] change vcode image file extension --- pan.baidu.com.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index e1a5d6b..fe5d580 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -394,7 +394,7 @@ def login(self, username, password): t = re.search('codeString=(.+?)&', r.content) codestring = t.group(1) if t else "" vcurl = 'https://passport.baidu.com/cgi-bin/genimage?'+codestring - verifycode = self.save_img(vcurl, 'gif') if codestring != "" else "" + verifycode = self.save_img(vcurl, 'jpg') if codestring != "" else "" data['codestring'] = codestring data['verifycode'] = verifycode #self.save_cookies() From 2ddbe55e5b7eaeb0312b78b9f19654cde5f5a751 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Sat, 26 Dec 2015 17:40:01 +0800 Subject: [PATCH 05/71] [pan.baidu.com] fix relogin failed, see #51 --- pan.baidu.com.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index fe5d580..696b9b3 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -283,7 +283,10 @@ def init(self): sys.exit(1) if not self.check_login(): - print s % (1, 91, ' !! cookie is invalid, please login\n'), u[0] + print s % (1, 91, ' !! cookie is invalid, please login.'), u[0] + del j[u[0]] + with open(cookie_file, 'w') as g: + pk.dump(j, g) sys.exit(1) else: print s % (1, 97, ' no account, please login') @@ -3048,7 +3051,7 @@ def handle_command(comd, xxx): if comd == 'userdelete' or comd == 'ud': if u != 'ALL': if accounts[u]['on'] and len(accounts) > 1: - print s % (1, 91, ' !! %s is online. To delete the account, firstly changing another account' % u) + print s % (1, 91, ' !! %s is online. To delete the account, firstly switching to other account' % u) sys.exit() del accounts[u] else: From 2bee9a9a943618e3eab5c444b88627e06b5d3e2d Mon Sep 17 00:00:00 2001 From: fghshunzi Date: Mon, 15 Feb 2016 18:42:46 +0800 Subject: [PATCH 06/71] Update 91porn.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Url解码 修复无法解析服务器的Bug --- 91porn.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/91porn.py b/91porn.py index 358851c..a3874f5 100755 --- a/91porn.py +++ b/91porn.py @@ -9,6 +9,7 @@ import argparse import random import select +import urllib2 ############################################################ # wget exit status @@ -79,7 +80,7 @@ def get_infos(self): 'name': '%s.mp4' % name, 'file': os.path.join(os.getcwd(), '%s.mp4' % name), 'dir_': os.getcwd(), - 'dlink': dlink + 'dlink': urllib2.unquote(dlink) } if not args.get_url: self.download(infos) From 546d55b80a9f797c334b7e380b349cca3107704d Mon Sep 17 00:00:00 2001 From: PeterDing Date: Fri, 26 Feb 2016 09:42:01 +0800 Subject: [PATCH 07/71] [91porn] unquote dlink at uplevel --- 91porn.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/91porn.py b/91porn.py index a3874f5..c649171 100755 --- a/91porn.py +++ b/91porn.py @@ -74,13 +74,14 @@ def get_infos(self): if r.ok: dlink = re.search( r'file=(http.+?)&', r.content).group(1) + dlink = urllib2.unquote(dlink) name = re.search( r'viewkey=([\d\w]+)', self.url).group(1) infos = { 'name': '%s.mp4' % name, 'file': os.path.join(os.getcwd(), '%s.mp4' % name), 'dir_': os.getcwd(), - 'dlink': urllib2.unquote(dlink) + 'dlink': dlink, } if not args.get_url: self.download(infos) From c67e421bd163f909c92731704afe64ab41e58f80 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Fri, 26 Feb 2016 09:46:02 +0800 Subject: [PATCH 08/71] [xiami] update xiami login --- xiami.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/xiami.py b/xiami.py index bacba28..4961440 100755 --- a/xiami.py +++ b/xiami.py @@ -11,6 +11,7 @@ import argparse import requests import urllib +import hashlib import select from mutagen.id3 import ID3,TRCK,TIT2,TALB,TPE1,APIC,TDRC,COMM,TPOS,USLT from HTMLParser import HTMLParser @@ -177,10 +178,27 @@ def login(self, email, password): 'LoginButton': '登录' } + hds = { + 'Origin': 'http://www.xiami.com', + 'Accept-Encoding': 'gzip, deflate', + 'Accept-Language': 'en-US,en;q=0.8', + 'Upgrade-Insecure-Requests': '1', + 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36', + 'Content-Type': 'application/x-www-form-urlencoded', + 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', + 'Cache-Control': 'max-age=1', + 'Referer': 'http://www.xiami.com/web/login', + 'Connection': 'keep-alive', + } + + cookies = { + '_xiamitoken': hashlib.md5(str(time.time())).hexdigest() + } + url = 'https://login.xiami.com/web/login' for i in xrange(2): - res = ss.post(url, data=data) + res = ss.post(url, headers=hds, data=data, cookies=cookies) if ss.cookies.get('member_auth'): self.save_cookies() return True From b8ff3da75583b3cce513d129881f306429d415d0 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Thu, 31 Mar 2016 15:46:22 +0800 Subject: [PATCH 09/71] [music.163.com] update api --- music.163.com.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/music.163.com.py b/music.163.com.py index fa87467..fca753b 100755 --- a/music.163.com.py +++ b/music.163.com.py @@ -27,7 +27,7 @@ url_dj = "http://music.163.com/api/dj/program/detail?id=%s&ids=%s" url_artist_albums = "http://music.163.com\ /api/artist/albums/%s?offset=0&limit=1000" -url_artist_top_50_songs = "http://music.163.com/artist/%s" +url_artist_top_50_songs = "http://music.163.com/artist?id=%s" # }}} ############################################################ @@ -338,7 +338,7 @@ def download_artist_top_50_songs(self): html = ss.get( url_artist_top_50_songs % self.artist_id).content text = re.search( - r'g_hotsongs = (.+?);', html).group(1) + r'', html).group(1) j = json.loads(text) songids = [i['id'] for i in j] d = modificate_text( From 2a05acff8e1bf1fa089c90afdfb661017de2dd86 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Sat, 21 May 2016 10:47:22 +0800 Subject: [PATCH 10/71] [README] update --- README.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index df09a71..db3e167 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ > *[L]* *[W]* *[LW]* 分别表示,在linux, windows, linux和windows 下通过测试。 + > ***windows用户可在babun (https://github.com/babun/babun) 下运行。*** | | | @@ -21,7 +22,6 @@ *[L]* | [91porn.py](#91porn.py) | 下载或播放91porn | *[L]* | [ThunderLixianExporter.user.js](#ThunderLixianExporter.user.js) | A fork of https://github.com/binux/ThunderLixianExporter - 增加了mpv和mplayer的导出。 | | 待续 | | - --- --- @@ -193,10 +193,13 @@ xm s http://www.xiami.com/artist/23460?spm=a1z1s.6928801.1561534521.115.ShW08b > http://kanoha.org/2011/08/30/xiami-absolute-address/ + > http://www.blackglory.me/xiami-vip-audition-with-no-quality-difference-between-downloading/ + > https://gist.github.com/lepture/1014329 + > 淘宝登录代码: https://github.com/ly0/xiami-tools --- @@ -747,8 +750,10 @@ ls、重命名、移动、删除、复制、使用正则表达式进行文件操 > https://gist.github.com/HououinRedflag/6191023 + > https://github.com/banbanchs/pan-baidu-download/blob/master/bddown_core.py + > https://github.com/houtianze/bypy --- @@ -875,6 +880,7 @@ bt c magnet_link -t be64 > http://blog.chinaunix.net/uid-28450123-id-4051635.html + > http://en.wikipedia.org/wiki/Torrent_file --- @@ -972,6 +978,8 @@ pan115 -p url ### yunpan.360.cn.py - 360网盘的下载 +**!!!脚本已不再维护!!!** + #### 1. 依赖 ``` @@ -1157,6 +1165,7 @@ nm -p url > https://github.com/yanunon/NeteaseCloudMusic/wiki/%E7%BD%91%E6%98%93%E4%BA%91%E9%9F%B3%E4%B9%90API%E5%88%86%E6%9E%90 + > http://s3.music.126.net/s/2/core.js --- @@ -1164,6 +1173,10 @@ nm -p url ### flv_cmd.py - 基于在线服务的视频解析 client - 支持下载、播放 +**!!!脚本已不再维护!!!** + +**请使用 youtube-dl or you-get** + #### 1. 依赖 ``` @@ -1212,8 +1225,10 @@ fl url -p > https://github.com/soimort/you-get + > https://github.com/iambus/youku-lixian + > https://github.com/rg3/youtube-dl --- From f98e4342e773597ad227c3e2e2b7e7e54719a564 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Sat, 28 May 2016 19:12:51 +0800 Subject: [PATCH 11/71] [pan.baidu.com] update api --- pan.baidu.com.py | 155 +++++++++++++++++++++++++---------------------- 1 file changed, 84 insertions(+), 71 deletions(-) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index 696b9b3..2a2c015 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -3,6 +3,7 @@ import os import sys +import functools import requests requests.packages.urllib3.disable_warnings() # disable urllib3's warnings https://urllib3.readthedocs.org/en/latest/security.html#insecurerequestwarning from requests_toolbelt import MultipartEncoder @@ -108,14 +109,13 @@ save_share_path = os.path.join(os.path.expanduser('~'), '.bp.ss.pickle') headers = { - "Accept":"text/html,application/xhtml+xml,application/xml; " \ - "q=0.9,image/webp,*/*;q=0.8", - "Accept-Encoding":"text/html", + "Accept": "application/json, text/javascript, text/html, */*; q=0.01", + "Accept-Encoding":"gzip, deflate, sdch", "Accept-Language":"en-US,en;q=0.8,zh-CN;q=0.6,zh;q=0.4,zh-TW;q=0.2", - "Content-Type":"application/x-www-form-urlencoded", - "Referer":"http://www.baidu.com/", - "User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 " \ - "(KHTML, like Gecko) Chrome/44.0.2403.89 Safari/537.36" + "Referer":"http://pan.baidu.com/disk/home", + "X-Requested-With": "XMLHttpRequest", + "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36", + "Connection": "keep-alive", } ss = requests.session() @@ -217,11 +217,14 @@ def print_process_bar(point, total, slice_size, sys.stdout.flush() return now + class panbaiducom_HOME(object): def __init__(self): self._download_do = self._play_do if args.play else self._download_do self.ondup = 'overwrite' self.accounts = self._check_cookie_file() + self.dsign = None + self.timestamp = None self.highlights = [] if any([args.tails, args.heads, args.includes]): @@ -304,10 +307,7 @@ def save_img(url, ext): return input_code def check_login(self): - #print s % (1, 97, '\n -- check_login') - url = 'http://pan.baidu.com/api/quota' - j = ss.get(url).json() - if j['errno'] != 0: + if not ss.cookies.get('BDUSS'): print s % (1, 91, ' -- check_login fail\n') return False else: @@ -354,10 +354,10 @@ def login(self, username, password): # Construct post body data = { "staticpage": "https://www.baidu.com/cache/user/html/v3Jump.html", - "charset": "UTF-8", + "charset": "utf-8", "token": token, - "tpl": "mn", - "subpro": "", + "tpl": "netdisk", + "subpro": "netdisk_web", "apiver": "v3", "tt": int(time.time()), "codestring": "", @@ -366,18 +366,19 @@ def login(self, username, password): "isPhone": "", "detect": "1", "quick_user": "0", - "logintype": "dialogLogin", - "logLoginType": "pc_loginDialog", + "logintype": "basicLogin", + "logLoginType": "pc_loginBasic", "idc": "", "loginmerge": "true", "splogin": "rate", "username": username, "password": password_encoded, + "mem_pass": "on", "verifycode": "", "rsakey": str(rsakey), "crypttype": "12", - "ppui_logintime": "155983", - "callback": "parent.bd__pcbs__pld7nd", + "ppui_logintime": "32221", + "callback": "parent.bd__pcbs__ahhlgk", } while True: @@ -389,7 +390,8 @@ def login(self, username, password): # Callback for verify code if we need #codestring = r.content[r.content.index('(')+1:r.content.index(')')] errno = re.search(r'err_no=(\d+)', r.content).group(1) - if errno == '0' or ss.cookies.get('BDUSS'): + if ss.cookies.get('BDUSS'): + ss.get("http://pan.baidu.com/disk/home") break elif errno in ('257', '3', '6'): print s % (1, 91, ' ! Error %s:' % errno), \ @@ -570,7 +572,9 @@ def _get_path(self, url): return url def _get_quota(self): - url = 'http://pan.baidu.com/api/quota' + url = 'http://pan.baidu.com/api/quota?checkexpire=1&checkfree=1&bdstoken={}&channel=chunlei&web=1&app_id=250528&clienttype=0'.format(self._get_bdstoken()) + + ss.get('http://pan.baidu.com/disk/home') r = ss.get(url) j = r.json() if j['errno'] != 0: @@ -623,9 +627,10 @@ def _get_dsign(self): url = 'http://pan.baidu.com/disk/home' r = ss.get(url) html = r.content - sign1 = re.search(r'sign1 = \'(.+?)\';', html).group(1) - sign3 = re.search(r'sign3 = \'(.+?)\';', html).group(1) - timestamp = re.search(r'timestamp = \'(.+?)\';', html).group(1) + + sign1 = re.search(r'"sign1":"(.+?)"', html).group(1) + sign3 = re.search(r'"sign3":"(.+?)"', html).group(1) + timestamp = re.search(r'"timestamp":(\d+)', html).group(1) # following javascript code from http://pan.baidu.com/disk/home #yunData.sign2 = function s(j, r) { @@ -688,36 +693,32 @@ def sign2(j, r): self.dsign = sign2(sign3, sign1) self.timestamp = timestamp - def _get_dlink(self, i): - if not hasattr(self, 'dsign'): - self._get_dsign() + def _get_dlink(self, fs_id): + self._get_dsign() - while True: - params = { - "channel": "chunlei", - "clienttype": 0, - "web": 1, - #"bdstoken": self._get_bdstoken() - } + params = { + "channel": "chunlei", + "clienttype": 0, + "app_id": "250528", + "web": 1, + "bdstoken": self._get_bdstoken(), + "sign": self.dsign, + "timestamp": self.timestamp, + "fidlist": '[{}]'.format(fs_id), + "type": "dlink", + } - data = { - "sign": self.dsign, - "timestamp": self.timestamp, - "fidlist": "[%s]" % i['fs_id'], - "type": "dlink" - } + url = 'http://pan.baidu.com/api/download' + r = ss.get(url, params=params) + j = r.json() + if j['errno'] == 0: + dlink = j['dlink'][0]['dlink'].encode('utf8') + # dlink = re.sub(r'prisign=.+?(&|$)', r'prisign=unknow\1', dlink) + # dlink = dlink.replace('chkbd=0', 'chkbd=1') + # dlink = dlink.replace('chkv=0', 'chkv=1') + dlink = fast_pcs_server(dlink) + return dlink - url = 'http://pan.baidu.com/api/download' - r = ss.post(url, params=params, data=data) - j = r.json() - if j['errno'] == 0: - dlink = j['dlink'][0]['dlink'].encode('utf8') - dlink = re.sub(r'prisign=.+?(&|$)', r'prisign=unknow\1', dlink) - dlink = dlink.replace('chkbd=0', 'chkbd=1') - dlink = dlink.replace('chkv=0', 'chkv=1') - return dlink - else: - self._get_dsign() def _get_dlink2(self, i): j = self._meta([i['path'].encode('utf8')], dlink=1) @@ -752,7 +753,7 @@ def download(self, paths): base_dir = '' if os.path.split(path)[0] == '/' \ else os.path.split(path)[0] - meta = self._meta([path], dlink=1) + meta = self._meta([path], dlink=0) if meta: if meta['info'][0]['isdir']: dir_loop = [path] @@ -794,8 +795,7 @@ def download(self, paths): t = t[1:] if t[0] == '/' else t t = os.path.join(os.getcwd(), t) - if not i.has_key('dlink'): - i['dlink'] = self._get_dlink2(i) + i['dlink'] = self._get_dlink(i['fs_id']) infos = { 'file': t, @@ -822,10 +822,10 @@ def download(self, paths): 'file': t, 'path': meta['info'][0]['path'].encode('utf8'), 'dir_': os.path.split(t)[0], - #'dlink': self._get_dlink(meta['info'][0]), + 'dlink': self._get_dlink(meta['info'][0]['fs_id']), 'm3u8': self._get_m3u8(meta['info'][0]) \ if 'm3' in args.type_ else None, - 'dlink': meta['info'][0]['dlink'].encode('utf8'), + # 'dlink': meta['info'][0]['dlink'].encode('utf8'), 'name': meta['info'][0]['server_filename'].encode('utf8'), 'size': meta['info'][0]['size'], } @@ -861,6 +861,8 @@ def _download_do(infos): print s % (1, 93, ' !! 百度8秒 !!') return + cookie = 'Cookie: ' + '; '.join([ + k + '=' + v for k, v in ss.cookies.get_dict().items()]) if args.aria2c: quiet = ' --quiet=true' if args.quiet else '' taria2c = ' -x %s -s %s' % (args.aria2c, args.aria2c) @@ -871,22 +873,21 @@ def _download_do(infos): cmd = 'aria2c -c -k 1M%s%s%s ' \ '-o "%s.tmp" -d "%s" ' \ '--user-agent "netdisk;5.3.1.3;PC;PC-Windows;5.1.2600;WindowsBaiduYunGuanJia" ' \ + '--header "%s" ' \ '"%s"' \ % (quiet, taria2c, tlimit, infos['name'], - infos['dir_'], infos['dlink']) + infos['dir_'], cookie, infos['dlink']) else: - cookie = 'Cookie: ' + '; '.join([ - k + '=' + v for k, v in ss.cookies.get_dict().items()]) quiet = ' -q' if args.quiet else '' tlimit = ' --limit-rate %s' % args.limit if args.limit else '' cmd = 'wget -c%s%s ' \ '-O "%s.tmp" ' \ - '--user-agent "%s" ' \ + '--user-agent "netdisk;5.3.1.3;PC;PC-Windows;5.1.2600;WindowsBaiduYunGuanJia" ' \ '--header "Referer:http://pan.baidu.com/disk/home" ' \ '--header "%s" ' \ '"%s"' \ % (quiet, tlimit, infos['file'], - headers['User-Agent'], cookie, infos['dlink']) + cookie, infos['dlink']) status = os.system(cmd) exit = True @@ -933,9 +934,9 @@ def _play_do(infos): k + '=' + v for k, v in ss.cookies.get_dict().items()]) quiet = ' --really-quiet' if args.quiet else '' cmd = 'mpv%s --no-ytdl --cache-default 20480 --cache-secs 120 ' \ - '--http-header-fields "User-Agent:%s" ' \ '--http-header-fields "%s" ' \ - '--http-header-fields "Referer:http://pan.baidu.com/disk/home" "%s"' \ + '--http-header-fields "%s" ' \ + '"%s"' \ % (quiet, headers['User-Agent'], cookie, infos['dlink']) os.system(cmd) @@ -977,8 +978,10 @@ def _meta(self, file_list, dlink=0): "method": "filemetas", "dlink": dlink, "blocks": 0, # 0 or 1 - #"bdstoken": self._get_bdstoken() + "bdstoken": self._get_bdstoken() } + + ss.get('http://pan.baidu.com/disk/home') url = 'http://pan.baidu.com/api/filemetas' i = 0 j = {} @@ -1685,17 +1688,27 @@ def save_inbox_share(self, url, remotepath, infos=None): ####################################################################### # for finding files - def _search(self, keyword, directory): + def _search(self, keyword, directory, page=1, num=10000): + p = { - "channel": "chunlei", - "clienttype": 0, - "web": 1, - "key": keyword, - "dir": directory if directory else "", - #"timeStamp": "0.15937364846467972", - #"bdstoken": self._get_bdstoken(), + 'recursion': '0', + 'order': 'time', + 'desc': '1', + 'showempty': '1', + 'web': '0', + 'page': page, + 'dir': directory, + 'num': num, + 'key': keyword, + 't': str(random.random()), + 'bdstoken': self._get_bdstoken(), + 'channel': 'chunlei', + 'web': '1', + 'app_id': '250528', + 'clienttype': '0', } + ss.get('http://pan.baidu.com/disk/home') if args.recursive: p['recursion'] = 1 url = 'http://pan.baidu.com/api/search' r = ss.get(url, params=p) From 2cb777fcebcd21980c56ba84e60e49619ffd90fe Mon Sep 17 00:00:00 2001 From: PeterDing Date: Sat, 28 May 2016 19:26:34 +0800 Subject: [PATCH 12/71] [pan.baidu.com] num < 1000 at _search --- pan.baidu.com.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index 2a2c015..0bcffc5 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -1688,10 +1688,10 @@ def save_inbox_share(self, url, remotepath, infos=None): ####################################################################### # for finding files - def _search(self, keyword, directory, page=1, num=10000): + def _search(self, keyword, directory, page=1, num=1000): p = { - 'recursion': '0', + 'recursion': 0, 'order': 'time', 'desc': '1', 'showempty': '1', @@ -1708,9 +1708,9 @@ def _search(self, keyword, directory, page=1, num=10000): 'clienttype': '0', } - ss.get('http://pan.baidu.com/disk/home') if args.recursive: p['recursion'] = 1 url = 'http://pan.baidu.com/api/search' + ss.get('http://pan.baidu.com/disk/home') r = ss.get(url, params=p) j = r.json() if j['errno'] == 0: From 75e38b4c176baef6871bf52cf46e477ac1e55c55 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Sun, 29 May 2016 01:54:10 +0800 Subject: [PATCH 13/71] [pan.baidu.com] update many 1. add panbaiducom_HOME._request for handle web request 2. update check_login 3. no use rsa for loginning 4. get bdstoken from 'pan.baidu.com/disk/home' 5. use pcs api for dlink 6. other api update 7. save cookies on login --- pan.baidu.com.py | 253 +++++++++++++++++++++++++++-------------------- 1 file changed, 148 insertions(+), 105 deletions(-) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index 0bcffc5..fedff73 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -241,6 +241,19 @@ def __init__(self): if 'ec' in args.type_ or 'dc' in args.type_ or args.comd == 'dc': import_shadowsocks() + def _request(self, method, url, action, **kwargs): + i = 0 + while i < 3: + i += 1 + response = ss.request(method, url, **kwargs) + if not (response.ok is True and response.status_code == 200): + continue + else: + return response + + print s % (1, 91, ' ! [{}] Server error'.format(action)) + sys.exit() + @staticmethod def _check_cookie_file(): def correct_do(): @@ -307,7 +320,9 @@ def save_img(url, ext): return input_code def check_login(self): - if not ss.cookies.get('BDUSS'): + html_string = self._request('GET', 'http://pan.baidu.com/disk/home', 'check_login').content + + if '"loginstate":1' not in html_string: print s % (1, 91, ' -- check_login fail\n') return False else: @@ -339,68 +354,81 @@ def login(self, username, password): '401007': '手机号关联了其他帐号,请选择登录' } + self._request('GET', 'http://www.baidu.com', 'login') + # Get token - token = self._get_bdstoken() + # token = self._get_bdstoken() + resp = self._request('GET', 'https://passport.baidu.com/v2/api/?getapi&tpl=netdisk' + '&apiver=v3&tt={}&class=login&logintype=basicLogin'.format(int(time.time())), + 'login') + + _json = json.loads(resp.content.replace('\'', '"')) + if _json['errInfo']['no'] != "0": + print s % (1, 91, ' ! Can\'t get token') + sys.exit(1) + + token = _json['data']['token'] + code_string = _json['data']['codeString'] # get publickey - url = 'https://passport.baidu.com/v2/getpublickey?token=%s' % token - r = ss.get(url) - j = json.loads(r.content.replace('\'', '"')) - pubkey = j['pubkey'] - key = rsa.PublicKey.load_pkcs1_openssl_pem(pubkey) - password_encoded = base64.b64encode(rsa.encrypt(password, key)) - rsakey = j['key'] + # url = ('https://passport.baidu.com/v2/getpublickey?&token={}' + # '&tpl=netdisk&apiver=v3&tt={}').format(token, int(time.time())) + # r = ss.get(url) + # j = json.loads(r.content.replace('\'', '"')) + # pubkey = j['pubkey'] + # key = rsa.PublicKey.load_pkcs1_openssl_pem(pubkey) + # password_encoded = base64.b64encode(rsa.encrypt(password, key)) + # rsakey = j['key'] # Construct post body - data = { - "staticpage": "https://www.baidu.com/cache/user/html/v3Jump.html", - "charset": "utf-8", - "token": token, - "tpl": "netdisk", - "subpro": "netdisk_web", - "apiver": "v3", - "tt": int(time.time()), - "codestring": "", - "safeflg": "0", - "u": "https://www.baidu.com/", - "isPhone": "", - "detect": "1", - "quick_user": "0", - "logintype": "basicLogin", - "logLoginType": "pc_loginBasic", - "idc": "", - "loginmerge": "true", - "splogin": "rate", - "username": username, - "password": password_encoded, - "mem_pass": "on", - "verifycode": "", - "rsakey": str(rsakey), - "crypttype": "12", - "ppui_logintime": "32221", - "callback": "parent.bd__pcbs__ahhlgk", - } - + verifycode = '' while True: + data = { + "staticpage": "http://pan.baidu.com/res/static/thirdparty/pass_v3_jump.html", + "charset": "utf-8", + "token": token, + "tpl": "netdisk", + "subpro": "", + "apiver": "v3", + "tt": int(time.time()), + "codestring": code_string, + "safeflg": "0", + "u": "http://pan.baidu.com/", + "isPhone": "", + "quick_user": "0", + "logintype": "basicLogin", + "logLoginType": "pc_loginBasic", + "idc": "", + "loginmerge": "true", + "username": username, + "password": password, + "verifycode": verifycode, + "mem_pass": "on", + "rsakey": "", + "crypttype": "", + "ppui_logintime": "2602", + "callback": "parent.bd__pcbs__ahhlgk", + } + # Post! # XXX : do not handle errors url = 'https://passport.baidu.com/v2/api/?login' r = ss.post(url, data=data) # Callback for verify code if we need - #codestring = r.content[r.content.index('(')+1:r.content.index(')')] + #code_string = r.content[r.content.index('(')+1:r.content.index(')')] errno = re.search(r'err_no=(\d+)', r.content).group(1) if ss.cookies.get('BDUSS'): - ss.get("http://pan.baidu.com/disk/home") + # ss.get("http://pan.baidu.com/disk/home") break elif errno in ('257', '3', '6'): print s % (1, 91, ' ! Error %s:' % errno), \ login_error_msg[errno] t = re.search('codeString=(.+?)&', r.content) - codestring = t.group(1) if t else "" - vcurl = 'https://passport.baidu.com/cgi-bin/genimage?'+codestring - verifycode = self.save_img(vcurl, 'jpg') if codestring != "" else "" - data['codestring'] = codestring + code_string = t.group(1) if t else "" + vcurl = 'https://passport.baidu.com/cgi-bin/genimage?' + code_string + verifycode = self.save_img(vcurl, 'jpg') if code_string != "" else "" + data['codestring'] = code_string data['verifycode'] = verifycode #self.save_cookies() else: @@ -434,8 +462,20 @@ def _get_bdstoken(self): if hasattr(self, 'bdstoken'): return self.bdstoken - self.bdstoken = md5.new(str(time.time())).hexdigest() - return self.bdstoken + resp = self._request('GET', 'http://pan.baidu.com/disk/home', + '_get_bdstoken') + + html_string = resp.content + + mod = re.search(r'"bdstoken":"(.+?)"', html_string) + if mod: + self.bdstoken = mod.group(1) + return self.bdstoken + else: + print s % (1, 91, ' ! Can\'t get bdstoken') + sys.exit(1) + + # self.bdstoken = md5.new(str(time.time())).hexdigest() #def _sift(self, fileslist, name=None, size=None, time=None, head=None, tail=None, include=None, exclude=None): def _sift(self, fileslist, **arguments): @@ -572,11 +612,11 @@ def _get_path(self, url): return url def _get_quota(self): - url = 'http://pan.baidu.com/api/quota?checkexpire=1&checkfree=1&bdstoken={}&channel=chunlei&web=1&app_id=250528&clienttype=0'.format(self._get_bdstoken()) + url = 'http://pan.baidu.com/api/quota' - ss.get('http://pan.baidu.com/disk/home') - r = ss.get(url) - j = r.json() + resp = self._request('GET', url, '_get_quota') + + j = resp.json() if j['errno'] != 0: print s % (1, 92, ' !! Error at _get_quota') sys.exit(1) @@ -600,14 +640,15 @@ def _get_file_list(self, order, desc, dir_, num, all=True): "desc": 1, ## reversely "order": order, ## sort by name, or size, time "_": int(time.time()*1000), - #"bdstoken": self._get_bdstoken(), + "bdstoken": self._get_bdstoken(), } if not desc: del p['desc'] url = 'http://pan.baidu.com/api/list' infos = [] while True: - r = ss.get(url, params=p, headers=theaders) + # r = ss.get(url, params=p, headers=theaders) + r = ss.get(url, params=p) j = r.json() if j['errno'] != 0: print s % (1, 91, ' error: _get_file_list'), '--', j @@ -624,8 +665,11 @@ def _get_file_list(self, order, desc, dir_, num, all=True): return j def _get_dsign(self): + # if self.dsign is not None: + # return None + url = 'http://pan.baidu.com/disk/home' - r = ss.get(url) + r = self._request('GET', url, '_get_dsign') html = r.content sign1 = re.search(r'"sign1":"(.+?)"', html).group(1) @@ -693,32 +737,42 @@ def sign2(j, r): self.dsign = sign2(sign3, sign1) self.timestamp = timestamp - def _get_dlink(self, fs_id): - self._get_dsign() + def _get_dlink(self, path): + dlink = ('http://c.pcs.baidu.com/rest/2.0/pcs/file?method=download' + '&app_id=250528&path={}').format(urllib.quote(path)) + dlink = fast_pcs_server(dlink) + return dlink - params = { - "channel": "chunlei", - "clienttype": 0, - "app_id": "250528", - "web": 1, - "bdstoken": self._get_bdstoken(), - "sign": self.dsign, - "timestamp": self.timestamp, - "fidlist": '[{}]'.format(fs_id), - "type": "dlink", - } - - url = 'http://pan.baidu.com/api/download' - r = ss.get(url, params=params) - j = r.json() - if j['errno'] == 0: - dlink = j['dlink'][0]['dlink'].encode('utf8') - # dlink = re.sub(r'prisign=.+?(&|$)', r'prisign=unknow\1', dlink) - # dlink = dlink.replace('chkbd=0', 'chkbd=1') - # dlink = dlink.replace('chkv=0', 'chkv=1') - dlink = fast_pcs_server(dlink) - return dlink + def _get_dlink3(self, fs_id): + while True: + dsign, timestamp = self._get_dsign() + + params = { + "channel": "chunlei", + "clienttype": 0, + "app_id": "250528", + "web": 1, + # "bdstoken": self._get_bdstoken(), + "sign": self.dsign, + "timestamp": self.timestamp, + "fidlist": '[{}]'.format(fs_id), + "type": "dlink", + } + url = 'http://pan.baidu.com/api/download' + r = ss.get(url, params=params) + j = r.json() + print(j) + if j['errno'] == 0: + dlink = j['dlink'][0]['dlink'].encode('utf8') + # dlink = re.sub(r'prisign=.+?(&|$)', r'prisign=unknow\1', dlink) + # dlink = dlink.replace('chkbd=0', 'chkbd=1') + # dlink = dlink.replace('chkv=0', 'chkv=1') + dlink = fast_pcs_server(dlink) + return dlink + else: + print s % (1, 91, ' !! Error at _get_dlink, can\'t get dlink') + continue def _get_dlink2(self, i): j = self._meta([i['path'].encode('utf8')], dlink=1) @@ -795,7 +849,7 @@ def download(self, paths): t = t[1:] if t[0] == '/' else t t = os.path.join(os.getcwd(), t) - i['dlink'] = self._get_dlink(i['fs_id']) + i['dlink'] = self._get_dlink(i['path'].encode('utf8')) infos = { 'file': t, @@ -822,7 +876,7 @@ def download(self, paths): 'file': t, 'path': meta['info'][0]['path'].encode('utf8'), 'dir_': os.path.split(t)[0], - 'dlink': self._get_dlink(meta['info'][0]['fs_id']), + 'dlink': self._get_dlink(meta['info'][0]['path'].encode('utf8')), 'm3u8': self._get_m3u8(meta['info'][0]) \ if 'm3' in args.type_ else None, # 'dlink': meta['info'][0]['dlink'].encode('utf8'), @@ -872,22 +926,22 @@ def _download_do(infos): #'--header "Referer:http://pan.baidu.com/disk/home " ' \ cmd = 'aria2c -c -k 1M%s%s%s ' \ '-o "%s.tmp" -d "%s" ' \ - '--user-agent "netdisk;5.3.1.3;PC;PC-Windows;5.1.2600;WindowsBaiduYunGuanJia" ' \ + '--user-agent "%s" ' \ '--header "%s" ' \ '"%s"' \ % (quiet, taria2c, tlimit, infos['name'], - infos['dir_'], cookie, infos['dlink']) + infos['dir_'], headers['User-Agent'], cookie, infos['dlink']) else: quiet = ' -q' if args.quiet else '' tlimit = ' --limit-rate %s' % args.limit if args.limit else '' cmd = 'wget -c%s%s ' \ '-O "%s.tmp" ' \ - '--user-agent "netdisk;5.3.1.3;PC;PC-Windows;5.1.2600;WindowsBaiduYunGuanJia" ' \ + '--user-agent "%s" ' \ '--header "Referer:http://pan.baidu.com/disk/home" ' \ '--header "%s" ' \ '"%s"' \ % (quiet, tlimit, infos['file'], - cookie, infos['dlink']) + headers['User-Agent'], cookie, infos['dlink']) status = os.system(cmd) exit = True @@ -972,6 +1026,7 @@ def _make_dir(self, dir_): return ENoError def _meta(self, file_list, dlink=0): + p = { "channel": "chunlei", "app_id": "250528", @@ -981,7 +1036,7 @@ def _meta(self, file_list, dlink=0): "bdstoken": self._get_bdstoken() } - ss.get('http://pan.baidu.com/disk/home') + # ss.get('http://pan.baidu.com/disk/home') url = 'http://pan.baidu.com/api/filemetas' i = 0 j = {} @@ -990,7 +1045,8 @@ def _meta(self, file_list, dlink=0): if fl: data = {'target': json.dumps(fl)} try: - r = ss.post(url, params=p, data=data) + r = self._request('POST', url, '_meta', params=p, data=data) + # r = ss.post(url, params=p, data=data) js = r.json() if js['errno'] == 0 and i == 0: if dlink: @@ -1691,27 +1747,14 @@ def save_inbox_share(self, url, remotepath, infos=None): def _search(self, keyword, directory, page=1, num=1000): p = { - 'recursion': 0, - 'order': 'time', - 'desc': '1', - 'showempty': '1', - 'web': '0', - 'page': page, - 'dir': directory, - 'num': num, + 'recursion': '', 'key': keyword, - 't': str(random.random()), - 'bdstoken': self._get_bdstoken(), - 'channel': 'chunlei', - 'web': '1', - 'app_id': '250528', - 'clienttype': '0', + 'dir': directory, } if args.recursive: p['recursion'] = 1 url = 'http://pan.baidu.com/api/search' - ss.get('http://pan.baidu.com/disk/home') - r = ss.get(url, params=p) + r = self._request('GET', url, '_search', params=p) j = r.json() if j['errno'] == 0: return j['list'] @@ -3475,10 +3518,10 @@ def handle_command(comd, xxx): else: print s % (2, 91, ' !! 命令错误\n') - if 'x' in locals(): - x.save_cookies(on=1, tocwd=True) - elif 'px' in locals(): - px.save_cookies(on=1, tocwd=True) + # if 'x' in locals(): + # x.save_cookies(on=1, tocwd=True) + # elif 'px' in locals(): + # px.save_cookies(on=1, tocwd=True) def main(argv): handle_signal() From ed2f5678e9ecb8a78a811b5b532dd69c53ae0a6e Mon Sep 17 00:00:00 2001 From: PeterDing Date: Tue, 31 May 2016 15:15:32 +0800 Subject: [PATCH 14/71] [pan.baidu.com] save cookies after every requestion --- pan.baidu.com.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index fedff73..d7527a7 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -251,6 +251,8 @@ def _request(self, method, url, action, **kwargs): else: return response + self.save_cookies() + print s % (1, 91, ' ! [{}] Server error'.format(action)) sys.exit() @@ -640,7 +642,7 @@ def _get_file_list(self, order, desc, dir_, num, all=True): "desc": 1, ## reversely "order": order, ## sort by name, or size, time "_": int(time.time()*1000), - "bdstoken": self._get_bdstoken(), + # "bdstoken": self._get_bdstoken(), } if not desc: del p['desc'] url = 'http://pan.baidu.com/api/list' @@ -1028,12 +1030,12 @@ def _make_dir(self, dir_): def _meta(self, file_list, dlink=0): p = { - "channel": "chunlei", - "app_id": "250528", + # "channel": "chunlei", + # "app_id": "250528", "method": "filemetas", "dlink": dlink, "blocks": 0, # 0 or 1 - "bdstoken": self._get_bdstoken() + # "bdstoken": self._get_bdstoken() } # ss.get('http://pan.baidu.com/disk/home') @@ -3523,6 +3525,8 @@ def handle_command(comd, xxx): # elif 'px' in locals(): # px.save_cookies(on=1, tocwd=True) + + def main(argv): handle_signal() From 8c6c4e21891b0ae7279c4cdfd07056206547751d Mon Sep 17 00:00:00 2001 From: PeterDing Date: Mon, 20 Jun 2016 10:52:59 +0800 Subject: [PATCH 15/71] [music] [Fix] fix some bugs --- bt.py | 1 + music.163.com.py | 10 ++++++++-- xiami.py | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/bt.py b/bt.py index ab4d562..63a74df 100755 --- a/bt.py +++ b/bt.py @@ -235,6 +235,7 @@ def trans(tpath): dd = bencode.bdecode(string) except Exception as e: print s % (1, 91, ' !! torrent is wrong:'), e + return None info = bencode.bencode(dd['info']) hh = sha1(info).hexdigest() print '# %s' % tpath diff --git a/music.163.com.py b/music.163.com.py index fca753b..1392f25 100755 --- a/music.163.com.py +++ b/music.163.com.py @@ -122,6 +122,7 @@ def get_durl(self, i): durl = u'http://m2.music.126.net/%s/%s.mp3' \ % (edfsId, dfsId) return durl, q[0] + return None, None def get_cover(self, info): if info['album_name'] == self.cover_id: @@ -364,12 +365,14 @@ def display_infos(self, i): print ' >>', s % (2, 92, 'http://music.163.com/song/%s' \ % i['song_id']) print ' >>', s % (2, 97, 'MP3-Quality'), ':', \ - s % (1, 92, q[i['mp3_quality']]) + s % (1, 92, str(q.get(i['mp3_quality']))) print '' def play(self, amount_songs, n=None): for i in self.song_infos: self.display_infos(i) + if not i['durl']: + continue cmd = 'mpv --really-quiet --audio-display no %s' % i['durl'] os.system(cmd) timeout = 1 @@ -401,7 +404,7 @@ def download(self, amount_songs, n=None): continue if not args.undownload: q = {'h': 'High', 'm': 'Middle', 'l': 'Low'} - mp3_quality = q[i['mp3_quality']] + mp3_quality = str(q.get(i['mp3_quality'])) if n == None: print(u'\n ++ 正在下载: #%s/%s# %s\n' \ u' ++ mp3_quality: %s' \ @@ -412,6 +415,9 @@ def download(self, amount_songs, n=None): u' ++ mp3_quality: %s' \ % (n, amount_songs, col, s % (1, 91, mp3_quality))) + if not i['durl']: + continue + file_name_for_wget = file_name.replace('`', '\`') cmd = 'wget -c -nv -U "%s" -O "%s.tmp" %s' \ % (headers['User-Agent'], file_name_for_wget, i['durl']) diff --git a/xiami.py b/xiami.py index 4961440..07bd707 100755 --- a/xiami.py +++ b/xiami.py @@ -575,7 +575,7 @@ def get_songs(self, album_id, song_id=None): for i in t] # find count of songs that be played. - t = re.findall(r'(.*?)<', c) + t = re.findall(r'class="song_hot_bar"> Date: Sun, 30 Oct 2016 21:08:06 +0800 Subject: [PATCH 20/71] [pan.baidu.com] [Fix] fix save_share with password, #89 --- pan.baidu.com.py | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index 5c22edd..4d2f2d9 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -1454,7 +1454,7 @@ def save_datas(self, path, infos): ################################################################## # for saving shares - def _share_transfer(self, info): + def _share_transfer(self, surl, info): meta = self._meta([info['remotepath'].encode('utf8')]) if not meta: self._make_dir(info['remotepath'].encode('utf8')) @@ -1481,14 +1481,14 @@ def _share_transfer(self, info): self._get_bdstoken())) theaders = { - 'Cookie': ' '.join(['{}={};'.format(k, v) for k, v in ss.cookies.get_dict().items()]), + 'Cookie': '; '.join(['{}={}'.format(k, v) for k, v in ss.cookies.get_dict().items()]), 'Origin': 'https://pan.baidu.com', 'Accept-Encoding': 'gzip, deflate, br', 'Accept-Language': 'zh-CN,zh;q=0.8,en;q=0.6,zh-TW;q=0.4', 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36', 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 'Accept': '*/*', - 'Referer': 'https://pan.baidu.com/', + 'Referer': surl, 'X-Requested-With': 'XMLHttpRequest', 'Connection': 'keep-alive', } @@ -1574,7 +1574,7 @@ def save_share(self, url, remotepath, infos=None): while True: print s % (1, 97, ' ++ transfer:'), info['path'] - result = self._share_transfer(info) + result = self._share_transfer(url, info) if result['errno'] == 0: break elif result['errno'] == 12 or result['errno'] == -33: @@ -1599,18 +1599,35 @@ def save_share(self, url, remotepath, infos=None): @staticmethod def _secret_or_not(url): ss.headers['Referer'] = 'http://pan.baidu.com' - r = ss.get(url) + r = ss.get(url, headers=headers) + if r.status_code != 200 and r.status_code != 302: + print('cookies', ss.cookies.get_dict()) + ss.headers['Cookie'] = ';'.join(['{}={}'.format(k, v) for k, v in ss.cookies.get_dict().items()]) + r = ss.get(url, headers=headers, cookies=r.cookies) + if 'init' in r.url: if not args.secret: secret = raw_input(s % (2, 92, " 请输入提取密码: ")) else: secret = args.secret - data = 'pwd=%s' % secret - url = "%s&t=%d" % ( - r.url.replace('init', 'verify'), \ - int(time.time()) + data = 'pwd=%s&vcode=&vcode_str=' % secret + query = 'bdstoken=null&channel=chunlei&clienttype=0&web=1&app_id=250528' + url = "%s&t=%d&%s" % ( + r.url.replace('init', 'verify'), + int(time.time()*1000), + query ) - r = ss.post(url, data=data) + theaders = { + 'Accept-Encoding': 'gzip, deflate', + 'Accept-Language': 'zh-CN,zh;q=0.8,en;q=0.6,zh-TW;q=0.4', + 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36', + 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', + 'Accept': '*/*', + 'X-Requested-With': 'XMLHttpRequest', + 'Cookie': 'BAIDUID=0F38C66B2C9AC2FC887BD3FEB059F5AC:FG=1; PANWEB=1', + 'Connection': 'keep-alive', + } + r = ss.post(url, data=data, headers=theaders) if r.json()['errno']: print s % (2, 91, " !! 提取密码错误\n") sys.exit(1) @@ -3242,6 +3259,7 @@ def handle_command(comd, xxx): ) else: infos = None + if '/inbox/' in xxx[0]: url = xxx[0] x.save_inbox_share(url, remotepath, infos=infos) From aa3cacc88ef5cb59a0c8c9cb18ff93dd83583d54 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Fri, 2 Dec 2016 10:58:03 +0800 Subject: [PATCH 21/71] [xiami.py] [Update] new rules --- xiami.py | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/xiami.py b/xiami.py index 4084a41..21649f8 100755 --- a/xiami.py +++ b/xiami.py @@ -419,22 +419,22 @@ def modified_id3(self, file_name, info): def url_parser(self, urls): for url in urls: if '/collect/' in url: - self.collect_id = re.search(r'/collect/(\d+)', url).group(1) + self.collect_id = re.search(r'/collect/(\w+)', url).group(1) #print(s % (2, 92, u'\n -- 正在分析精选集信息 ...')) self.download_collect() elif '/album/' in url: - self.album_id = re.search(r'/album/(\d+)', url).group(1) + self.album_id = re.search(r'/album/(\w+)', url).group(1) #print(s % (2, 92, u'\n -- 正在分析专辑信息 ...')) self.download_album() elif '/artist/' in url or 'i.xiami.com' in url: def get_artist_id(url): html = ss.get(url).text - artist_id = re.search(r'artist_id = \'(\d+)\'', html).group(1) + artist_id = re.search(r'artist_id = \'(\w+)\'', html).group(1) return artist_id - self.artist_id = re.search(r'/artist/(\d+)', url).group(1) \ + self.artist_id = re.search(r'/artist/(\w+)', url).group(1) \ if '/artist/' in url else get_artist_id(url) code = raw_input(' >> a # 艺术家所有专辑.\n' \ ' >> r # 艺术家 radio\n' \ @@ -451,12 +451,12 @@ def get_artist_id(url): print(s % (1, 92, u' --> Over')) elif '/song/' in url: - self.song_id = re.search(r'/song/(\d+)', url).group(1) + self.song_id = re.search(r'/song/(\w+)', url).group(1) #print(s % (2, 92, u'\n -- 正在分析歌曲信息 ...')) self.download_song() elif '/u/' in url: - self.user_id = re.search(r'/u/(\d+)', url).group(1) + self.user_id = re.search(r'/u/(\w+)', url).group(1) code = raw_input(' >> m # 该用户歌曲库.\n' \ ' >> c # 最近在听\n' \ ' >> s # 分享的音乐\n' @@ -513,7 +513,7 @@ def get_artist_id(url): self.hack_luoo(url) elif 'sid=' in url: - _mod = re.search(r'sid=([\d+,]+\d)', url) + _mod = re.search(r'sid=([\w+,]+\w)', url) if _mod: song_ids = _mod.group(1).split(',') self.download_songs(song_ids) @@ -529,7 +529,7 @@ def get_songs(self, album_id, song_id=None): t = re.search(r'"v:itemreviewed">(.+?)<', html1).group(1) album_name = modificate_text(t) - t = re.search(r'"/artist/\d+.+?>(.+?)<', html1).group(1) + t = re.search(r'"/artist/\w+.+?>(.+?)<', html1).group(1) artist_name = modificate_text(t) t = re.findall(u'(\d+)年(\d+)月(\d+)', html1) @@ -569,10 +569,11 @@ def get_songs(self, album_id, song_id=None): z = len(str(len(tracks))) # find all song_ids and song_names - t = re.findall(r'(.+?)(.+?)> ' + amount_songs + u' 首歌曲将要下载.')) \ @@ -698,7 +702,7 @@ def download_artist_albums(self): while True: html = ss.get( url_artist_albums % (self.artist_id, str(ii))).text - t = re.findall(r'/album/(\d+)"', html) + t = re.findall(r'/album/(\w+)"', html) if album_ids == t: break album_ids = t if album_ids: @@ -716,7 +720,7 @@ def download_artist_top_20_songs(self): html = ss.get(url_artist_top_song % self.artist_id).text song_ids = re.findall(r'/song/(.+?)" title', html) artist_name = re.search( - r'

(.+?)<', html).group(1) + r'

(.+?)<', html).group(1) d = modificate_text(artist_name + u' - top 20') dir_ = os.path.join(os.getcwdu(), d) self.dir_ = modificate_file_name_for_wget(dir_) @@ -734,7 +738,7 @@ def download_artist_top_20_songs(self): def download_artist_radio(self): html = ss.get(url_artist_top_song % self.artist_id).text artist_name = re.search( - r'

(.+?)<', html).group(1) + r'

(.+?)<', html).group(1) d = modificate_text(artist_name + u' - radio') dir_ = os.path.join(os.getcwdu(), d) self.dir_ = modificate_file_name_for_wget(dir_) From 7c8c16cb0d885bebeada8132c9409982f68eb4ef Mon Sep 17 00:00:00 2001 From: PeterDing Date: Sun, 18 Dec 2016 19:43:50 +0800 Subject: [PATCH 22/71] [pan.baidu.com.py] use aget --- pan.baidu.com.py | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index 4d2f2d9..de3366c 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -741,7 +741,9 @@ def sign2(j, r): def _get_dlink(self, path): dlink = ('http://c.pcs.baidu.com/rest/2.0/pcs/file?method=download' - '&app_id=250528&path={}').format(urllib.quote(path)) + '&app_id=250528&path={}&ver=2.0&clienttype=1').format( + urllib.quote(path)) + dlink = fast_pcs_server(dlink) return dlink @@ -919,20 +921,25 @@ def _download_do(infos): cookie = 'Cookie: ' + '; '.join([ k + '=' + v for k, v in ss.cookies.get_dict().items()]) - if args.aria2c: + user_agent = "netdisk;5.3.1.3;PC;PC-Windows;5.1.2600;WindowsBaiduYunGuanJia" + # user_agent = "netdisk;7.15.1;HUAWEI+G750-T01;android-android;4.2.2" + # user_agent = headers['User-Agent'] + + if args.aget_s: quiet = ' --quiet=true' if args.quiet else '' - taria2c = ' -x %s -s %s' % (args.aria2c, args.aria2c) - tlimit = ' --max-download-limit %s' % args.limit if args.limit else '' #'--user-agent "netdisk;4.4.0.6;PC;PC-Windows;6.2.9200;WindowsBaiduYunGuanJia" ' \ #'--user-agent "netdisk;5.3.1.3;PC;PC-Windows;5.1.2600;WindowsBaiduYunGuanJia" ' \ #'--header "Referer:http://pan.baidu.com/disk/home " ' \ - cmd = 'aria2c -c -k 1M%s%s%s ' \ - '-o "%s.tmp" -d "%s" ' \ - '--user-agent "%s" ' \ - '--header "%s" ' \ + cmd = 'aget -k %s -s %s ' \ + '-o "%s.tmp" ' \ + '-H "User-Agent: %s" ' \ + '-H "Content-Type: application/x-www-form-urlencoded" ' \ + '-H "Connection: Keep-Alive" ' \ + '-H "Accept-Encoding: gzip" ' \ + '-H "%s" ' \ '"%s"' \ - % (quiet, taria2c, tlimit, infos['name'], - infos['dir_'], headers['User-Agent'], cookie, infos['dlink']) + % (args.aget_k, args.aget_s, infos['file'], + user_agent, cookie, infos['dlink']) else: quiet = ' -q' if args.quiet else '' tlimit = ' --limit-rate %s' % args.limit if args.limit else '' @@ -948,9 +955,9 @@ def _download_do(infos): status = os.system(cmd) exit = True if 'ie' in args.type_: - if status == 2 and not args.aria2c: + if status == 2 and not args.aget_s: pass - elif status == (7 << 8) and args.aria2c: + elif status == (7 << 8) and args.aget_s: pass else: exit = False @@ -2917,7 +2924,7 @@ def get_infos(self): r = ss.post(url, data=data) j = r.json() if not j['errno']: - dlink = fast_pcs_server(j['list']['dlink'].encode('utf8')) + dlink = fast_pcs_server(j['list'][0]['dlink'].encode('utf8')) self.infos['dlink'] = dlink if args.play: panbaiducom_HOME._play_do(self.infos) @@ -3008,8 +3015,10 @@ def handle_args(argv): p = argparse.ArgumentParser(description='about pan.baidu.com.' \ ' 用法见 https://github.com/PeterDing/iScript') p.add_argument('xxx', type=str, nargs='*', help='命令对象.') - p.add_argument('-a', '--aria2c', action='store', default=None, \ - type=int, help='aria2c分段下载数量') + p.add_argument('-a', '--aget_s', action='store', default=None, \ + type=int, help='aget 分段下载数量') + p.add_argument('-k', '--aget_k', action='store', default='200K', \ + type=str, help='aget 分段大小') p.add_argument('-p', '--play', action='store_true', help='play with mpv') p.add_argument('-v', '--view', action='count', help='view details') p.add_argument('-V', '--VERIFY', action='store_true', help='verify') From ed829740e8495e1758c1328f58a9f5d6dd0eb69f Mon Sep 17 00:00:00 2001 From: PeterDing Date: Sun, 18 Dec 2016 20:12:42 +0800 Subject: [PATCH 23/71] [pan.baidu.com.py] -g for --aget_s --- pan.baidu.com.py | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index de3366c..e4ec652 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -940,6 +940,18 @@ def _download_do(infos): '"%s"' \ % (args.aget_k, args.aget_s, infos['file'], user_agent, cookie, infos['dlink']) + elif args.aria2c: + quiet = ' --quiet=true' if args.quiet else '' + taria2c = ' -x %s -s %s' % (args.aria2c, args.aria2c) + tlimit = ' --max-download-limit %s' % args.limit if args.limit else '' + cmd = 'aria2c -c -k 1M%s%s%s ' \ + '-o "%s.tmp" -d "%s" ' \ + '--user-agent "%s" ' \ + '--header "%s" ' \ + '"%s"' \ + % (quiet, taria2c, tlimit, infos['name'], + infos['dir_'], headers['User-Agent'], + cookie, infos['dlink']) else: quiet = ' -q' if args.quiet else '' tlimit = ' --limit-rate %s' % args.limit if args.limit else '' @@ -955,9 +967,9 @@ def _download_do(infos): status = os.system(cmd) exit = True if 'ie' in args.type_: - if status == 2 and not args.aget_s: + if status == 2 and not args.aria2c: pass - elif status == (7 << 8) and args.aget_s: + elif status == (7 << 8) and args.aria2c: pass else: exit = False @@ -2987,6 +2999,11 @@ def do4(self, paths): panbaiducom_HOME._download_do(self.infos) break +def assert_download_tools(): + for tool in ('wget', 'aget', 'aria2c'): + if ' ' in os.popen('which %s' % tool).read(): + print s % (1, 91, ' !!! aria2 is not installed') + def sighandler(signum, frame): print s % (1, 91, " !! Signal:"), signum if args.comd in ('u', 'upload'): @@ -3015,7 +3032,9 @@ def handle_args(argv): p = argparse.ArgumentParser(description='about pan.baidu.com.' \ ' 用法见 https://github.com/PeterDing/iScript') p.add_argument('xxx', type=str, nargs='*', help='命令对象.') - p.add_argument('-a', '--aget_s', action='store', default=None, \ + p.add_argument('-a', '--aria2c', action='store', default=None, \ + type=int, help='aria2c 分段下载数量') + p.add_argument('-g', '--aget_s', action='store', default=None, \ type=int, help='aget 分段下载数量') p.add_argument('-k', '--aget_k', action='store', default='200K', \ type=str, help='aget 分段大小') @@ -3206,7 +3225,10 @@ def handle_command(comd, xxx): # login session panbaiducom_HOME().init() - if comd == 'p' or comd == 'play': args.play = True + if comd == 'p' or comd == 'play': + args.play = True + else: + assert_download_tools() enter_password() From 2718dbb84dc0fae3ab1c3e24b80fe67c8d2da655 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Sun, 18 Dec 2016 20:13:05 +0800 Subject: [PATCH 24/71] [README] [Update] --- README.md | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index db3e167..da2aed9 100644 --- a/README.md +++ b/README.md @@ -214,6 +214,8 @@ wget aria2 (~ 1.18) +aget # 需要 python >= 3.5, 安装 pip3 install aget + python2-rsa python2-pyasn1 @@ -254,6 +256,8 @@ pan.baidu.com.py 是一个百度网盘的命令行客户端。 下载工具默认为wget, 可用参数-a num选用aria2 +**支持用 aget 加速下载, 用法见下** + 下载的文件,保存在当前目录下。 下载默认为非递归,递归下载加 -R @@ -435,7 +439,11 @@ jca 或 jobclearall # 清除 *全部任务* #### 参数: ``` --a num, --aria2c num aria2c分段下载数量: eg: -a 10 +-a num, --aria2c num aria2c 分段下载数量: eg: -a 10 +-g num, --aget_s num aget 分段下载数量: eg: -g 100 +-k num, --aget_k size aget 分段大小: eg: -k 200K + -k 1M + -k 2M -p, --play play with mpv -P password, --passwd password 分享密码,加密密码 -y, --yes yes # 用于 rmre, mvre, cpre, rnre !!慎用 @@ -567,11 +575,17 @@ bp d 'http://pan.baidu.com/share/link?shareid=1622654699&uk=1026372002&fid=21126 # 下载别人加密分享的*单个文件*,密码参数-s bp d http://pan.baidu.com/s/1i3FVlw5 -s vuej -# 用aria2下载 +# 用aria2 下载 bp d http://pan.baidu.com/s/1i3FVlw5 -s vuej -a 5 bp d /movie/her.mkv -a 4 bp d url -s [secret] -a 10 +# 用 aget 下载 +bp d http://pan.baidu.com/s/1i3FVlw5 -s vuej -g 100 +bp d /movie/her.mkv -g 100 -k 200K +bp d url -s [secret] -g 100 -k 100K +如果下载速度很慢,可以试试加大 -g, 减小 -k, -k 一般在 100K ~ 300K 之间合适 + # 下载并解码 ## 默认加密方法为 aes-256-cfb bp d /path/to/encrypted_file -t dc [-P password] # 覆盖加密文件 (默认) From bdaae4d2e1748f6d5642c0b2ae35453eb84e2e0e Mon Sep 17 00:00:00 2001 From: PeterDing Date: Sun, 25 Dec 2016 10:29:48 +0800 Subject: [PATCH 25/71] [pan.baidu.com.py] [Fix] turn on 'save_cookies', #95 --- pan.baidu.com.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index e4ec652..e9ee4f8 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -3569,10 +3569,10 @@ def handle_command(comd, xxx): else: print s % (2, 91, ' !! 命令错误\n') - # if 'x' in locals(): - # x.save_cookies(on=1, tocwd=True) - # elif 'px' in locals(): - # px.save_cookies(on=1, tocwd=True) + if 'x' in locals(): + x.save_cookies(on=1, tocwd=True) + elif 'px' in globals(): + px.save_cookies(on=1, tocwd=True) From 7557e73f9dd175b3248dde64c74030d392c252c7 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Sun, 25 Dec 2016 10:40:55 +0800 Subject: [PATCH 26/71] [pan.baidu.com.py] [Fix] aria2c command --- pan.baidu.com.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index e9ee4f8..dc9d58f 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -944,7 +944,7 @@ def _download_do(infos): quiet = ' --quiet=true' if args.quiet else '' taria2c = ' -x %s -s %s' % (args.aria2c, args.aria2c) tlimit = ' --max-download-limit %s' % args.limit if args.limit else '' - cmd = 'aria2c -c -k 1M%s%s%s ' \ + cmd = 'aria2c -c%s%s%s ' \ '-o "%s.tmp" -d "%s" ' \ '--user-agent "%s" ' \ '--header "%s" ' \ From ac4f426a17768b9d0c34e249bc610c7a854328bb Mon Sep 17 00:00:00 2001 From: PeterDing Date: Sun, 25 Dec 2016 10:53:11 +0800 Subject: [PATCH 27/71] [pan.baidu.com.py] change pcs server hostname --- pan.baidu.com.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index dc9d58f..4699d32 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -740,7 +740,7 @@ def sign2(j, r): self.timestamp = timestamp def _get_dlink(self, path): - dlink = ('http://c.pcs.baidu.com/rest/2.0/pcs/file?method=download' + dlink = ('http://d.pcs.baidu.com/rest/2.0/pcs/file?method=download' '&app_id=250528&path={}&ver=2.0&clienttype=1').format( urllib.quote(path)) From e01a8bb89e0e6de82a785feae01794abe672f25f Mon Sep 17 00:00:00 2001 From: PeterDing Date: Sun, 25 Dec 2016 19:41:23 +0800 Subject: [PATCH 28/71] [pan.baidu.com.py] change fast_pcs_server --- pan.baidu.com.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index 4699d32..aa2d871 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -170,7 +170,7 @@ def fast_pcs_server(j): return j do = lambda dlink: \ - re.sub(r'://[^/]+?/', '://www.baidupcs.com/', dlink) + re.sub(r'://[^/]+?/', '://c.pcs.baidu.com/', dlink) #re.sub(r'://[^/]+?/', '://c.pcs.baidu.com/', dlink) if isinstance(j, dict) and j.get('info') and len(j['info']) > 0: From 73d470583fc16d4e115623c6b363877126386e1a Mon Sep 17 00:00:00 2001 From: PeterDing Date: Sun, 15 Jan 2017 00:57:30 +0800 Subject: [PATCH 29/71] [xiami.py] check new high quality mp3 link --- xiami.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/xiami.py b/xiami.py index 21649f8..0f0fac7 100755 --- a/xiami.py +++ b/xiami.py @@ -910,12 +910,13 @@ def hack_luoo(self, url): self.song_id = j[0]['id'] self.download_song() - def display_infos(self, i, nn, n): + def display_infos(self, i, nn, n, durl): print n, '/', nn print s % (2, 94, i['file_name']) print s % (2, 95, i['album_name']) print 'http://www.xiami.com/song/%s' % i['song_id'] print 'http://www.xiami.com/album/%s' % i['album_id'] + print durl if i['durl_is_H'] == 'h': print s % (1, 97, 'MP3-Quality:'), s % (1, 92, 'High') else: @@ -923,7 +924,7 @@ def display_infos(self, i, nn, n): print '—' * int(os.popen('tput cols').read()) def get_mp3_quality(self, durl): - if 'm3.file.xiami.com' in durl or 'm6.file.xiami.com' in durl: + if 'm3.file.xiami.com' in durl or 'm6.file.xiami.com' in durl or '_h.mp3' in durl: return 'h' else: return 'l' @@ -940,7 +941,7 @@ def play(self, songs, nn=u'1', n=1): mp3_quality = self.get_mp3_quality(durl) i['durl_is_H'] = mp3_quality - self.display_infos(i, nn, n) + self.display_infos(i, nn, n, durl) n = int(n) + 1 cmd = 'mpv --really-quiet ' \ '--cache 8146 ' \ From 44f077c933625da6b42a7798dfb5c652151cba65 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Sun, 22 Jan 2017 23:20:40 +0800 Subject: [PATCH 30/71] [pan.baidu.com.py] [Fix] _get_file_list does not get all file paths, #102 --- pan.baidu.com.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index aa2d871..340664f 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -647,23 +647,23 @@ def _get_file_list(self, order, desc, dir_, num, all=True): if not desc: del p['desc'] url = 'http://pan.baidu.com/api/list' - infos = [] + path_list = [] while True: - # r = ss.get(url, params=p, headers=theaders) r = ss.get(url, params=p) j = r.json() if j['errno'] != 0: print s % (1, 91, ' error: _get_file_list'), '--', j sys.exit(1) else: - infos += j['list'] + path_ls = j['list'] + path_list += path_ls if not all: return j - if len(infos) == num: + if len(path_ls) == num: p['page'] += 1 else: - j['list'] = infos + j['list'] = path_list return j def _get_dsign(self): From 70bf54219a8b83dce6d1cfebf736477ac0268bcb Mon Sep 17 00:00:00 2001 From: PeterDing Date: Sat, 11 Mar 2017 11:22:04 +0800 Subject: [PATCH 31/71] [pan.baidu.com.py] support cookie login. #109 #90 --- README.md | 20 +++++++++++++------- pan.baidu.com.py | 32 +++++++++++++++++++++++++------- 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 1896452..efaaff5 100644 --- a/README.md +++ b/README.md @@ -218,19 +218,14 @@ aria2 (~ 1.18) aget # 需要 python >= 3.5, 安装 pip3 install aget -python2-rsa - -python2-pyasn1 - -python2-requests (https://github.com/kennethreitz/requests) - -requests-toolbelt (https://github.com/sigmavirus24/requests-toolbelt) +pip2 install rsa pyasn1 requests requests-toolbelt mpv (http://mpv.io) # 可选依赖 shadowsocks # 用于加密上传。 # 用 python2 的 pip 安装 +pip2 install shadowsocks # 除了用pip安装包,还可以手动: https://github.com/PeterDing/iScript/wiki/%E6%89%8B%E5%8A%A8%E8%A7%A3%E5%86%B3pan.baidu.com.py%E4%BE%9D%E8%B5%96%E5%8C%85 @@ -248,6 +243,8 @@ pan.baidu.com.py 是一个百度网盘的命令行客户端。 **支持多帐号登录** +**支持cookie登录** + **支持加密上传**, 需要 shadowsocks **cd, ls 功能完全支持** @@ -294,6 +291,7 @@ g login login username login username password +login username cookie # 删除帐号 userdelete 或 ud @@ -512,6 +510,14 @@ bp login username password # 一直用 bp login 即可 ``` +#### cookie 登录: + +1. 用 chrome 登录 pan.baidu.com +2. 在登录后的页面打开 chrome 开发者工具(怎么打开自行google),选择 `Network` ,然后刷新页面。在刷新后的 `Network` 的 `Name` 列表中选中 `list?dir=…` 开头的一项,然后在右侧找到 `Cookie` ,复制 `Cookie` 后面的所有内容。 +3. 用 `pan.baidu.com.py` 登录,`password / cookie:` 处粘贴上面复制的内容。(粘贴后是看不见的)。 + +> 如果使用 cookie 登录,`username` 可以是任意的东西。 + #### 删除帐号: ``` diff --git a/pan.baidu.com.py b/pan.baidu.com.py index 340664f..0e80037 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -218,6 +218,18 @@ def print_process_bar(point, total, slice_size, return now +def is_cookie(cookie): + return 'BDUSS=' in cookie and 'PANPSC=' in cookie and len(cookie) > 150 + + +def parse_cookies(cookie): + cookies = {} + for c in cookie.split('; '): + k, v = c.split('=', 1) + cookies[k] = v + return cookies + + class panbaiducom_HOME(object): def __init__(self): self._download_do = self._play_do if args.play else self._download_do @@ -322,20 +334,26 @@ def save_img(url, ext): return input_code def check_login(self): - html_string = self._request('GET', 'http://pan.baidu.com/disk/home', 'check_login').content + # html_string = self._request('GET', 'http://pan.baidu.com/', 'check_login').content + info = self._meta(['/']) - if '"loginstate":1' not in html_string: + if info and info['errno'] == 0: + return True + else: print s % (1, 91, ' -- check_login fail\n') return False - else: #print s % (1, 92, ' -- check_login success\n') #self.get_dsign() #self.save_cookies() - return True def login(self, username, password): print s % (1, 97, '\n -- login') + if is_cookie(password): + cookies = parse_cookies(password) + ss.cookies.update(cookies) + return + # error_message: at _check_account_exception from # https://github.com/ly0/baidupcsapi/blob/master/baidupcsapi/api.py login_error_msg = { @@ -3110,11 +3128,11 @@ def handle_command(comd, xxx): xh = panbaiducom_HOME() if len(xxx) < 1: - username = raw_input(s % (1, 97, ' username: ')) - password = getpass(s % (1, 97, ' password: ')) + username = raw_input(s % (1, 97, ' username: ')) + password = getpass(s % (1, 97, ' password / cookie: ')) elif len(xxx) == 1: username = xxx[0] - password = getpass(s % (1, 97, ' password: ')) + password = getpass(s % (1, 97, ' password / cookie: ')) elif len(xxx) == 2: username = xxx[0] password = xxx[1] From ecbc24fabe8717cf95971b55f31b4a1be69360ee Mon Sep 17 00:00:00 2001 From: PeterDing Date: Sat, 11 Mar 2017 14:54:55 +0800 Subject: [PATCH 32/71] [README] [Update] --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index efaaff5..70c8acc 100644 --- a/README.md +++ b/README.md @@ -513,7 +513,7 @@ bp login username password #### cookie 登录: 1. 用 chrome 登录 pan.baidu.com -2. 在登录后的页面打开 chrome 开发者工具(怎么打开自行google),选择 `Network` ,然后刷新页面。在刷新后的 `Network` 的 `Name` 列表中选中 `list?dir=…` 开头的一项,然后在右侧找到 `Cookie` ,复制 `Cookie` 后面的所有内容。 +2. 在登录后的页面打开 chrome 开发者工具(怎么打开自行google),选择 `Network` ,然后刷新页面。在刷新后的 `Network` 的 `Name` 列表中选中 `list?dir=…` 开头的一项,然后在右侧找到 `Cookie:` ,复制 `Cookie:` 后面的所有内容。 3. 用 `pan.baidu.com.py` 登录,`password / cookie:` 处粘贴上面复制的内容。(粘贴后是看不见的)。 > 如果使用 cookie 登录,`username` 可以是任意的东西。 From a686eb45c98442a25fe2da28c16e9f43981dd41f Mon Sep 17 00:00:00 2001 From: PeterDing Date: Sat, 11 Mar 2017 18:20:09 +0800 Subject: [PATCH 33/71] [README] [Update] --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 70c8acc..f28abdc 100644 --- a/README.md +++ b/README.md @@ -512,9 +512,11 @@ bp login username password #### cookie 登录: -1. 用 chrome 登录 pan.baidu.com -2. 在登录后的页面打开 chrome 开发者工具(怎么打开自行google),选择 `Network` ,然后刷新页面。在刷新后的 `Network` 的 `Name` 列表中选中 `list?dir=…` 开头的一项,然后在右侧找到 `Cookie:` ,复制 `Cookie:` 后面的所有内容。 -3. 用 `pan.baidu.com.py` 登录,`password / cookie:` 处粘贴上面复制的内容。(粘贴后是看不见的)。 +1. 打开 chrome 隐身模式窗口 +2. 在隐身模式窗口登录 pan.baidu.com +3. 在登录后的页面打开 chrome 开发者工具(怎么打开自行google),选择 `Network` ,然后刷新页面。在刷新后的 `Network` 的 `Name` 列表中选中 `list?dir=…` 开头的一项,然后在右侧找到 `Cookie:` ,复制 `Cookie:` 后面的所有内容。 +4. 用 `pan.baidu.com.py` 登录,`password / cookie:` 处粘贴上面复制的内容。(粘贴后是看不见的)。 +5. 不要退出 pan.baidu.com,只是关闭隐身模式窗口就可以。 > 如果使用 cookie 登录,`username` 可以是任意的东西。 From 58d0d3b4899b4a6c7fd7e5195c468a317143700a Mon Sep 17 00:00:00 2001 From: PeterDing Date: Sat, 8 Apr 2017 10:29:18 +0800 Subject: [PATCH 34/71] [xiami.py] [Update] update new "collect" html5 --- xiami.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xiami.py b/xiami.py index 0f0fac7..72ae035 100755 --- a/xiami.py +++ b/xiami.py @@ -679,11 +679,11 @@ def download_album(self): def download_collect(self): html = ss.get(url_collect % self.collect_id).text html = html.split('

(.+?)<', html).group(1) + collect_name = re.search(r'Codestin Search App', html).group(1) d = modificate_text(title) dir_ = os.path.join(os.getcwdu(), d) self.dir_ = modificate_file_name_for_wget(dir_) - html = ss.get( + html = self._request( 'http://www.xiami.com/chart/data?c=%s&limit=200&type=%s' \ % (self.chart_id, type_)).text song_ids = re.findall(r'/song/(\d+)', html) @@ -869,7 +889,7 @@ def download_chart(self, type_): n += 1 def download_genre(self, url_genre): - html = ss.get(url_genre % (self.genre_id, 1)).text + html = self._request(url_genre % (self.genre_id, 1)).text if '/gid/' in url_genre: t = re.search( r'/genre/detail/gid/%s".+?title="(.+?)"' \ @@ -893,11 +913,11 @@ def download_genre(self, url_genre): self.html = '' self.disc_description_archives = {} n += 1 - html = ss.get(url_genre % (self.chart_id, page)).text + html = self._request(url_genre % (self.chart_id, page)).text page += 1 def download_genre_radio(self, url_genre): - html = ss.get(url_genre % (self.genre_id, 1)).text + html = self._request(url_genre % (self.genre_id, 1)).text if '/gid/' in url_genre: t = re.search( r'/genre/detail/gid/%s".+?title="(.+?)"' \ @@ -916,7 +936,7 @@ def download_genre_radio(self, url_genre): n = 1 while True: - xml = ss.get(url_genre_radio).text + xml = self._request(url_genre_radio).text song_ids = re.findall(r'(\d+)', xml) for i in song_ids: songs = self.get_song(i) @@ -941,7 +961,7 @@ def hack_luoo(self, url): for info in songs_info: url = 'http://www.xiami.com/web/search-songs?key=%s' \ % urllib.quote(' '.join(info)) - r = ss.get(url) + r = self._request(url) j = r.json() if not r.ok or not j: print s % (1, 93, ' !! no find:'), ' - '.join(info) @@ -974,13 +994,15 @@ def get_mp3_quality(self, durl): def play(self, songs, nn=u'1', n=1): if args.play == 2: songs = sorted(songs, key=lambda k: k['song_played'], reverse=True) + for i in songs: - self.record(i['song_id']) + self.record(i['song_id'], i['album_id']) durl = self.get_durl(i['song_id']) if not durl: print s % (2, 91, ' !! Error: can\'t get durl'), i['song_name'] continue + cookies = '; '.join(['%s=%s' % (k, v) for k, v in ss.cookies.items()]) mp3_quality = self.get_mp3_quality(durl) i['durl_is_H'] = mp3_quality self.display_infos(i, nn, n, durl) @@ -990,12 +1012,9 @@ def play(self, songs, nn=u'1', n=1): '--user-agent "%s" ' \ '--http-header-fields "Referer: http://img.xiami.com' \ '/static/swf/seiya/1.4/player.swf?v=%s",' \ - '"Cookie: member_auth=%s" ' \ + '"Cookie: %s" ' \ '"%s"' \ - % (headers['User-Agent'], - int(time.time()*1000), - ss.cookies.get('member_auth'), - durl) + % (headers['User-Agent'], int(time.time()*1000), cookies, durl) os.system(cmd) timeout = 1 ii, _, _ = select.select([sys.stdin], [], [], timeout) @@ -1011,6 +1030,7 @@ def download(self, songs, amount_songs=u'1', n=1): if not os.path.exists(dir_): os.mkdir(dir_) + ii = 1 for i in songs: num = random.randint(0, 100) % 8 @@ -1048,6 +1068,7 @@ def download(self, songs, amount_songs=u'1', n=1): else: print ' |--', s % (1, 97, 'MP3-Quality:'), s % (1, 91, 'Low') + cookies = '; '.join(['%s=%s' % (k, v) for k, v in ss.cookies.items()]) file_name_for_wget = file_name.replace('`', '\`') quiet = ' -q' if args.quiet else ' -nv' cmd = 'wget -c%s ' \ @@ -1056,11 +1077,7 @@ def download(self, songs, amount_songs=u'1', n=1): '/static/swf/seiya/1.4/player.swf?v=%s" ' \ '--header "Cookie: member_auth=%s" ' \ '-O "%s.tmp" %s' \ - % (quiet, headers['User-Agent'], - int(time.time()*1000), - ss.cookies.get('member_auth'), - file_name_for_wget, - durl) + % (quiet, headers['User-Agent'], int(time.time()*1000), cookies, file_name_for_wget, durl) cmd = cmd.encode('utf8') status = os.system(cmd) if status != 0: # other http-errors, such as 302. @@ -1088,7 +1105,7 @@ def _save_do(self, id_, type, tags): "_xiamitoken": ss.cookies['_xiamitoken'], } url = 'http://www.xiami.com/ajax/addtag' - r = ss.post(url, data=data) + r = self._request(url, data=data, method='POST') j = r.json() if j['status'] == 'ok': return 0 @@ -1174,30 +1191,26 @@ def main(argv): email = raw_input(s % (1, 97, ' username: ') \ if comd == 'logintaobao' or comd == 'gt' \ else s % (1, 97, ' email: ')) - password = getpass(s % (1, 97, ' password: ')) + cookies = getpass(s % (1, 97, ' cookies: ')) elif len(xxx) == 1: # for add_member_auth - if '@' not in xxx[0]: - x = xiami() - x.add_member_auth(xxx[0]) - x.check_login() - return - - email = xxx[0] - password = getpass(s % (1, 97, ' password: ')) + if '; ' in xxx[0]: + email = None + cookies = xxx[0] + else: + email = xxx[0] + cookies = getpass(s % (1, 97, ' cookies: ')) elif len(xxx) == 2: email = xxx[0] - password = xxx[1] + cookies = xxx[1] else: - print s % (1, 91, - ' login\n login email\n \ - login email password') + msg = ('login: \n' + 'login cookies') + print s % (1, 91, msg) + return x = xiami() - if comd == 'logintaobao' or comd == 'gt': - x.login_taobao(email, password) - else: - x.login(email, password) + x.add_cookies(cookies) is_signin = x.check_login() if is_signin: print s % (1, 92, ' ++ login succeeds.') From bfa5099403ab86fa8e8a8a00f9145a0cbce02816 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Thu, 10 May 2018 16:51:07 +0800 Subject: [PATCH 46/71] [README.md] update xiami.py usage --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index bb09903..2d8b0a9 100644 --- a/README.md +++ b/README.md @@ -135,9 +135,9 @@ xm login username xm login username password # 手动添加cookie登录 -1. 用浏览器登录后,按F12,然后访问 http://xiami.com/vip -2. 选择‘网络’或network,找到 xiami.com/vip,在其中找到 Cookie: memthod_auth=value -3. value填入 xm g value,再执行。 +1. 用浏览器登录后,按F12,然后访问 https://www.xiami.com/album/123456 +2. 选择‘网络’或network,找到 123456,在其中找到 Cookie: xxx +3. 然后在终端运行 xm g "xxx" # 退出登录 xm signout From 0337e786ac4876ab2c5491c83cc9c545cfb79916 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Fri, 29 Jun 2018 12:01:22 +0800 Subject: [PATCH 47/71] [xiami.py] [Fix] 1. Correcting song file name, not allowing special assic symbols. 2. Adding `params` to `_request`, thank @Harry1993 #135 --- xiami.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/xiami.py b/xiami.py index 6edf3f8..a0ba8e2 100755 --- a/xiami.py +++ b/xiami.py @@ -130,6 +130,7 @@ def __init__(self): self.disc_description_archives = {} self.download = self.play if args.play else self.download + self._is_play = bool(args.play) def init(self): if os.path.exists(cookie_file): @@ -159,11 +160,11 @@ def check_login(self): print s % (1, 91, ' -- login fail, please check email and password\n') return False - def _request(self, url, headers=None, data=None, method='GET', timeout=30, retry=2): + def _request(self, url, headers=None, params=None, data=None, method='GET', timeout=30, retry=2): for _ in range(retry): try: headers = headers or ss.headers - resp = ss.request(method, url, headers=headers, data=data, timeout=timeout) + resp = ss.request(method, url, headers=headers, params=params, data=data, timeout=timeout) except Exception, err: continue @@ -625,7 +626,7 @@ def get_songs(self, album_id, song_id=None): song_info['cd_serial'] = disc song_info['year'] = year song_info['album_pic_url'] = album_pic_url - song_info['song_name'] = song_names[i] + song_info['song_name'] = modificate_text(song_names[i]) song_info['album_name'] = album_name song_info['artist_name'] = artist_name song_info['z'] = z From 63acc9d096b84242a8c83045763021bfee78bf40 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Tue, 24 Jul 2018 19:35:39 +0800 Subject: [PATCH 48/71] [xiami.py] [Update] * Add xiami H5 api and web api. We use web api, because it supports more song's attributions than H5 api. And web api have not need cookies. Album's description is not supported by both web api and H5 api, So we do not take it at mp3's comment. * Display song's length when playing. --- xiami.py | 467 ++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 344 insertions(+), 123 deletions(-) diff --git a/xiami.py b/xiami.py index a0ba8e2..af88373 100755 --- a/xiami.py +++ b/xiami.py @@ -5,8 +5,10 @@ import sys from getpass import getpass import os +import copy import random import time +import datetime import json import argparse import requests @@ -113,6 +115,314 @@ def z_index(song_infos): ######################################################## +class Song(object): + + def __init__(self): + self.__sure() + self.track = 0 + self.year = 0 + self.cd_serial = 0 + self.disc_description = '' + + # z = len(str(album_size)) + self.z = 1 + + def __sure(self): + __dict__ = self.__dict__ + if '__keys' not in __dict__: + __dict__['__keys'] = {} + + def __getattr__(self, name): + __dict__ = self.__dict__ + return __dict__['__keys'].get(name) + + def __setattr__(self, name, value): + __dict__ = self.__dict__ + __dict__['__keys'][name] = value + + def __getitem__(self, key): + return getattr(self, key) + + def __setitem__(self, key, value): + return setattr(self, key, value) + + def feed(self, **kwargs): + for name, value in kwargs.items(): + setattr(self, name, value) + + +class XiamiH5API(object): + + URL = 'http://api.xiami.com/web' + PARAMS = { + 'v': '2.0', + 'app_key': '1', + } + + def __init__(self): + self.cookies = { + 'user_from': '2', + 'XMPLAYER_addSongsToggler': '0', + 'XMPLAYER_isOpen': '0', + '_xiamitoken': hashlib.md5(str(time.time())).hexdigest() + } + self.sess = requests.session() + self.sess.cookies.update(self.cookies) + + def _request(self, url, method='GET', **kwargs): + try: + resp = self.sess.request(method, url, **kwargs) + except Exception, err: + print 'Error:', err + sys.exit() + + return resp + + def _make_params(self, **kwargs): + params = copy.deepcopy(self.PARAMS) + params.update(kwargs) + return params + + def song(self, song_id): + params = self._make_params(id=song_id, r='song/detail') + url = self.URL + resp = self._request(url, params=params, headers=headers) + + info = resp.json()['data']['song'] + pic_url = re.sub('_\d+\.', '.', info['logo']) + song = Song() + song.feed( + song_id=info['song_id'], + song_name=info['song_name'], + album_id=info['album_id'], + album_name=info['album_name'], + artist_id=info['artist_id'], + artist_name=info['artist_name'], + singers=info['singers'], + album_pic_url=pic_url, + comment='http://www.xiami.com/song/' + str(info['song_id']) + ) + return song + + def album(self, album_id): + url = self.URL + params = self._make_params(id=album_id, r='album/detail') + resp = self._request(url, params=params, headers=headers) + + info = resp.json()['data'] + songs = [] + album_id=info['album_id'], + album_name=info['album_name'], + artist_id = info['artist_id'] + artist_name = info['artist_name'] + pic_url = re.sub('_\d+\.', '.', info['album_logo']) + for track, info_n in enumerate(info['songs'], 1): + song = Song() + song.feed( + song_id=info_n['song_id'], + song_name=info_n['song_name'], + album_id=album_id, + album_name=album_name, + artist_id=artist_id, + artist_name=artist_name, + singers=info_n['singers'], + album_pic_url=pic_url, + track=track, + comment='http://www.xiami.com/song/' + str(info_n['song_id']) + ) + songs.append(song) + return songs + + def collect(self, collect_id): + url = self.URL + params = self._make_params(id=collect_id, r='collect/detail') + resp = self._request(url, params=params, headers=headers) + + info = resp.json()['data'] + collect_name = info['collect_name'] + collect_id = info['list_id'] + songs = [] + for info_n in info['songs']: + pic_url = re.sub('_\d+\.', '.', info['album_logo']) + song = Song() + song.feed( + song_id=info_n['song_id'], + song_name=info_n['song_name'], + album_id=info_n['album_id'], + album_name=info_n['album_name'], + artist_id=info_n['artist_id'], + artist_name=info_n['artist_name'], + singers=info_n['singers'], + album_pic_url=pic_url, + comment='http://www.xiami.com/song/' + str(info_n['song_id']) + ) + songs.append(song) + return collect_id, collect_name, songs + + def artist_top_songs(self, artist_id, page=1, limit=20): + url = self.URL + params = self._make_params(id=artist_id, page=page, limit=limit, r='artist/hot-songs') + resp = self._request(url, params=params, headers=headers) + + info = resp.json()['data'] + for info_n in info['songs']: + song_id = info_n['song_id'] + yield self.song(song_id) + + def search_songs(self, keywords, page=1, limit=20): + url = self.URL + params = self._make_params(key=keywords, page=page, limit=limit, r='search/songs') + resp = self._request(url, params=params, headers=headers) + + info = resp.json()['data'] + for info_n in info['songs']: + pic_url = re.sub('_\d+\.', '.', info['album_logo']) + song = Song() + song.feed( + song_id=info_n['song_id'], + song_name=info_n['song_name'], + album_id=info_n['album_id'], + album_name=info_n['album_name'], + artist_id=info_n['artist_id'], + artist_name=info_n['artist_name'], + singers=info_n['singer'], + album_pic_url=pic_url, + comment='http://www.xiami.com/song/' + str(info_n['song_id']) + ) + yield song + + def get_song_id(self, *song_sids): + song_ids = [] + for song_sid in song_sids: + if isinstance(song_sid, int) or song_sid.isdigit(): + song_ids.append(int(song_sid)) + + url = 'https://www.xiami.com/song/playlist/id/{}/cat/json'.format(song_sid) + resp = self._request(url, headers=headers) + info = resp.json() + song_id = int(str(info['data']['trackList'][0]['song_id'])) + song_ids.append(song_id) + return song_ids + + +class XiamiWebAPI(object): + + URL = 'https://www.xiami.com/song/playlist/' + + def __init__(self): + self.sess = requests.session() + + def _request(self, url, method='GET', **kwargs): + try: + resp = self.sess.request(method, url, **kwargs) + except Exception, err: + print 'Error:', err + sys.exit() + + return resp + + def _make_song(self, info): + song = Song() + song.feed( + song_id=info['song_id'], + song_sub_title=info['song_sub_title'], + songwriters=info['songwriters'], + singers=info['singers'], + song_name=info['name'], + + album_id=info['album_id'], + album_name=info['album_name'], + + artist_id=info['artist_id'], + artist_name=info['artist_name'], + + composer=info['composer'], + lyric_url='http:' + info['lyric_url'], + + track=info['track'], + cd_serial=info['cd_serial'], + album_pic_url='http:' + info['album_pic'], + comment='http://www.xiami.com/song/' + str(info['song_id']), + + length=info['length'], + play_count=info['playCount'], + + location=info['location'] + ) + return song + + def _find_z(self, album): + zs = [] + for i, song in enumerate(album[:-1]): + next_song = album[i+1] + + cd_serial = song.cd_serial + next_cd_serial = next_song.cd_serial + + if cd_serial != next_cd_serial: + z = len(str(song.track)) + zs.append(z) + + z = len(str(song.track)) + zs.append(z) + + for song in album: + song.z = zs[song.cd_serial - 1] + + def song(self, song_id): + url = self.URL + 'id/%s/cat/json' % song_id + resp = self._request(url, headers=headers) + + info = resp.json()['data']['trackList'][0] + song = self._make_song(info) + return song + + def songs(self, *song_ids): + url = self.URL + 'id/%s/cat/json' % '%2C'.join(song_ids) + resp = self._request(url, headers=headers) + + info = resp.json()['data'] + songs = [] + for info_n in info['trackList']: + song = self._make_song(info_n) + songs.append(song) + return songs + + def album(self, album_id): + url = self.URL + 'id/%s/type/1/cat/json' % album_id + resp = self._request(url, headers=headers) + + info = resp.json()['data'] + songs = [] + for info_n in info['trackList']: + song = self._make_song(info_n) + songs.append(song) + + self._find_z(songs) + return songs + + def collect(self, collect_id): + url = self.URL + 'id/%s/type/3/cat/json' % collect_id + resp = self._request(url, headers=headers) + + info = resp.json()['data'] + songs = [] + for info_n in info['trackList']: + song = self._make_song(info_n) + songs.append(song) + return songs + + def search_songs(self, keywords): + url = 'https://www.xiami.com/ajax/search-index?key=%s&_=%s' % ( + urllib.quote(keywords), int(time.time() * 1000)) + resp = self._request(url, headers=headers) + + html = resp.content + song_ids = re.findall(r'song/(\d+)', html) + songs = self.songs(*song_ids) + return songs + + class xiami(object): def __init__(self): self.dir_ = os.getcwdu() @@ -132,6 +442,8 @@ def __init__(self): self.download = self.play if args.play else self.download self._is_play = bool(args.play) + self._api = XiamiWebAPI() + def init(self): if os.path.exists(cookie_file): try: @@ -337,7 +649,7 @@ def get_durl(self, id_): encryed_url = t[1:] durl = decry(row, encryed_url) return durl - except Exception as e: + except Exception, e: print s % (1, 91, ' |-- Error, get_durl --'), e time.sleep(5) @@ -360,7 +672,7 @@ def get_cover(self, info): self.cover_data = self._request(url).content if self.cover_data[:5] != '(.+?)<', html1).group(1) - album_name = modificate_text(t) - - t = re.search(r'"/artist/\w+.+?>(.+?)<', html1).group(1) - artist_name = modificate_text(t) - - t = re.findall(u'(\d+)年(\d+)月(\d+)', html1) - year = '-'.join(t[0]) if t else '' - - album_description = '' - t = re.search(u'专辑介绍:(.+?)
', - html2, re.DOTALL) - if t: - t = t.group(1) - t = re.sub(r'<.+?>', '', t) - t = parser.unescape(t) - t = parser.unescape(t) - t = re.sub(r'\s\s+', u'\n', t).strip() - t = re.sub(r'<.+?(http://.+?)".+?>', r'\1', t) - t = re.sub(r'<.+?>([^\n])', r'\1', t) - t = re.sub(r'<.+?>(\r\n|)', u'\n', t) - album_description = t - - t = re.search(r'//(pic\.xiami\.net.+?)"', html).group(1) # issue133 - t = 'http://' + t - album_pic_url = t - - songs = [] - for c in html2.split('class="trackname"')[1:]: - disc = re.search(r'>disc (\d+)', c).group(1) - - t = re.search(r'>disc .+?\[(.+?)\]', c) - disc_description = modificate_text(t.group(1)) if t else '' - - # find track - t = re.findall(r'"trackid">(\d+)', c) - tracks = [i.lstrip('0') for i in t] - z = len(str(len(tracks))) - - # find all song_ids and song_names - t = re.findall(r'(.+?)(?:Album:|专辑:)(.+?)

', cn) # search song at xiami - for info in songs_info: - url = 'http://www.xiami.com/web/search-songs?key=%s' \ - % urllib.quote(' '.join(info)) - r = self._request(url) - j = r.json() - if not r.ok or not j: - print s % (1, 93, ' !! no find:'), ' - '.join(info) + for name, artist, album in songs_info: + name = name.strip() + artist = artist.strip() + album = album.strip() + + songs = self._api.search_songs(name + ' ' + artist) + if not songs: + print s % (1, 93, ' !! no find:'), ' - '.join([name, artist, album]) continue - self.song_id = j[0]['id'] - self.download_song() + + self.make_file_name(songs[0]) + self.download(songs[:1], n=1) def display_infos(self, i, nn, n, durl): length = datetime.datetime.fromtimestamp(i['length']).strftime('%M:%S') From 1cb0272e3dd2fb08ef446159a2c9eb4d309198c3 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Wed, 25 Jul 2018 13:37:34 +0800 Subject: [PATCH 50/71] [xiami.py] [Fix] * fix xiami `save` --- xiami.py | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/xiami.py b/xiami.py index 3b07566..50945d2 100755 --- a/xiami.py +++ b/xiami.py @@ -334,6 +334,12 @@ def _request(self, url, method='GET', **kwargs): def _make_song(self, info): song = Song() + + location=info['location'] + row = location[0] + encryed_url = location[1:] + durl = decry(row, encryed_url) + song.feed( song_id=info['song_id'], song_sub_title=info['song_sub_title'], @@ -358,7 +364,8 @@ def _make_song(self, info): length=info['length'], play_count=info['playCount'], - location=info['location'] + location=info['location'], + location_url=durl ) return song @@ -1339,7 +1346,7 @@ def _save_do(self, id_, type, tags): "shareTo": "all", "_xiamitoken": ss.cookies['_xiamitoken'], } - url = 'http://www.xiami.com/ajax/addtag' + url = 'https://www.xiami.com/ajax/addtag' r = self._request(url, data=data, method='POST') j = r.json() if j['status'] == 'ok': @@ -1351,27 +1358,31 @@ def save(self, urls): tags = args.tags for url in urls: if '/collect/' in url: - collect_id = re.search(r'/collect/(\d+)', url).group(1) + collect_id = re.search(r'/collect/(\w+)', url).group(1) print s % (1, 97, u'\n ++ save collect:'), \ 'http://www.xiami.com/song/collect/' + collect_id result = self._save_do(collect_id, 4, tags) elif '/album/' in url: - album_id = re.search(r'/album/(\d+)', url).group(1) + album_id = re.search(r'/album/(\w+)', url).group(1) + album = self._api.album(album_id) + album_id = album[0].album_id print s % (1, 97, u'\n ++ save album:'), \ - 'http://www.xiami.com/album/' + album_id + 'http://www.xiami.com/album/' + str(album_id) result = self._save_do(album_id, 5, tags) elif '/artist/' in url: - artist_id = re.search(r'/artist/(\d+)', url).group(1) + artist_id = re.search(r'/artist/(\w+)', url).group(1) print s % (1, 97, u'\n ++ save artist:'), \ 'http://www.xiami.com/artist/' + artist_id result = self._save_do(artist_id, 6, tags) elif '/song/' in url: - song_id = re.search(r'/song/(\d+)', url).group(1) + song_id = re.search(r'/song/(\w+)', url).group(1) + song = self._api.song(song_id) + song_id = song.song_id print s % (1, 97, u'\n ++ save song:'), \ - 'http://www.xiami.com/song/' + song_id + 'http://www.xiami.com/song/' + str(song_id) result = self._save_do(song_id, 3, tags) elif '/u/' in url: From 17f674c17c5d47c40c4370233d204e47fe280352 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Wed, 1 Aug 2018 14:45:51 +0800 Subject: [PATCH 51/71] [xiami.py] [Fix] * song_name need to unescape html * Get correct song_ids of artist top list --- xiami.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/xiami.py b/xiami.py index 50945d2..ee7915e 100755 --- a/xiami.py +++ b/xiami.py @@ -22,7 +22,7 @@ url_album = "http://www.xiami.com/album/%s" url_collect = "http://www.xiami.com/collect/ajax-get-list" url_artist_albums = "http://www.xiami.com/artist/album/id/%s/page/%s" -url_artist_top_song = "http://www.xiami.com/artist/top/id/%s" +url_artist_top_song = "http://www.xiami.com/artist/top-%s" url_lib_songs = "http://www.xiami.com/space/lib-song/u/%s/page/%s" url_recent = "http://www.xiami.com/space/charts-recent/u/%s/page/%s" @@ -345,7 +345,7 @@ def _make_song(self, info): song_sub_title=info['song_sub_title'], songwriters=info['songwriters'], singers=info['singers'], - song_name=info['name'], + song_name=parser.unescape(info['name']), album_id=info['album_id'], album_name=info['album_name'], @@ -371,6 +371,8 @@ def _make_song(self, info): def _find_z(self, album): zs = [] + song = album[0] + for i, song in enumerate(album[:-1]): next_song = album[i+1] @@ -996,7 +998,7 @@ def download_artist_albums(self): def download_artist_top_20_songs(self): html = self._request(url_artist_top_song % self.artist_id).text - song_ids = re.findall(r'/song/(.+?)" title', html) + song_ids = re.findall(r'/music/send/id/(\d+)', html) artist_name = re.search( r'

(.+?)<', html).group(1) d = modificate_text(artist_name + u' - top 20') From 6fdc7eff9072dc59810c115d7b89b69e1bcf15e2 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Sat, 11 Aug 2018 21:59:57 +0800 Subject: [PATCH 52/71] [xiami.py] [Update] handling missing songs --- xiami.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/xiami.py b/xiami.py index ee7915e..7709f82 100755 --- a/xiami.py +++ b/xiami.py @@ -393,6 +393,10 @@ def song(self, song_id): url = self.URL + 'id/%s/cat/json' % song_id resp = self._request(url, headers=HEADERS2) + # there is no song + if not resp.json().get('data'): + return None + info = resp.json()['data']['trackList'][0] song = self._make_song(info) return song @@ -401,6 +405,10 @@ def songs(self, *song_ids): url = self.URL + 'id/%s/cat/json' % '%2C'.join(song_ids) resp = self._request(url, headers=HEADERS2) + # there is no song + if not resp.json().get('data'): + return None + info = resp.json()['data'] songs = [] for info_n in info['trackList']: @@ -412,6 +420,10 @@ def album(self, album_id): url = self.URL + 'id/%s/type/1/cat/json' % album_id resp = self._request(url, headers=HEADERS2) + # there is no album + if not resp.json().get('data'): + return None + info = resp.json()['data'] songs = [] for info_n in info['trackList']: @@ -901,6 +913,9 @@ def make_file_name(self, song, cd_serial_auth=False): def get_songs(self, album_id, song_id=None): songs = self._api.album(album_id) + if not songs: + return [] + cd_serial_auth = int(songs[-1]['cd_serial']) > 1 for song in songs: self.make_file_name(song, cd_serial_auth=cd_serial_auth) @@ -911,6 +926,10 @@ def get_songs(self, album_id, song_id=None): def get_song(self, song_id): song = self._api.song(song_id) + + if not song: + return [] + self.make_file_name(song) return [song] @@ -929,6 +948,9 @@ def download_songs(self, song_ids): def download_album(self): songs = self.get_songs(self.album_id) + if not songs: + return + song = songs[0] d = song['album_name'] + ' - ' + song['artist_name'] From 3c4dfad9ed6ff1c265cead44766e8c7095f389c2 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Fri, 7 Sep 2018 10:06:16 +0800 Subject: [PATCH 53/71] [tumblr.py] support proxy, #125 * using proxy: -x 'protocol://address:port' protocol can be http, https, socks5 --- tumblr.py | 39 +++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/tumblr.py b/tumblr.py index 8effbb2..4329c63 100755 --- a/tumblr.py +++ b/tumblr.py @@ -62,6 +62,8 @@ ss = requests.session() ss.headers.update(headers) +PROXY = None + class Error(Exception): def __init__(self, msg): self.msg = msg @@ -119,12 +121,19 @@ def download_run(item): # num = random.randint(0, 7) % 8 # col = s % (1, num + 90, filepath) # print ' ++ download: %s' % col - cmd = ' '.join([ - 'wget', '-c', '-q', '-T', '10', - '-O', '"%s.tmp"' % filepath, - '--user-agent', '"%s"' % headers['User-Agent'], - '"%s"' % item['durl'].replace('http:', 'https:') - ]) + + if PROXY: + cmd = ' '.join([ + 'curl', '-s', '-x', '"%s"' % PROXY, '-o', '"%s.tmp"' % filepath, + '-H', '"User-Agent: %s"' % headers['User-Agent'], + '"%s"' % item['durl'] + ]) + else: + cmd = ' '.join([ + 'curl', '-s', '-o', '"%s.tmp"' % filepath, + '-H', '"User-Agent: %s"' % headers['User-Agent'], + '"%s"' % item['durl'] + ]) status = os.system(cmd) return status, filepath @@ -165,16 +174,20 @@ def _request(self, base_hostname, target, type, params): api_url = '/'.join(['https://api.tumblr.com/v2/blog', base_hostname, target, type]) params['api_key'] = API_KEY + if PROXY: + proxies = {'http': PROXY, 'https': PROXY} + else: + proxies = None while True: try: - res = ss.get(api_url, params=params, timeout=10) + res = ss.get(api_url, params=params, proxies=proxies, timeout=10) json_data = res.json() break except KeyboardInterrupt: sys.exit() except Exception as e: NET_ERRORS.value += 1 # count errors - # print s % (1, 93, '[Error at requests]:'), e + print s % (1, 93, '[Error at requests]:'), e, '\n' time.sleep(5) if json_data['meta']['msg'].lower() != 'ok': raise Error(s % (1, 91, json_data['meta']['msg'])) @@ -506,9 +519,19 @@ def args_handler(argv): help='update new things') p.add_argument('--redownload', action='store_true', help='redownload all things') + p.add_argument('-x', '--proxy', type=str, + help='redownload all things') args = p.parse_args(argv[1:]) xxx = args.xxx + if args.proxy: + if args.proxy[:4] not in ('http', 'sock'): + print s % (1, 91, '[Error]:'), 'proxy must have a protocol:// prefix' + sys.exit(1) + else: + global PROXY + PROXY = args.proxy + if args.redownload: args.update = True return args, xxx From d07ee93f9ba1b9c4e4014a2e9fa2ff8fb314d817 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Fri, 7 Sep 2018 10:12:54 +0800 Subject: [PATCH 54/71] [README.md] [tumblr.py] support proxies --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 2d8b0a9..6c4b9e5 100644 --- a/README.md +++ b/README.md @@ -1301,6 +1301,7 @@ python2-requests (https://github.com/kennethreitz/requests) --update 下载新发布的东西 --redownload 重新遍历所有的东西,如果有漏掉的东西则下载 +--proxy protocol://address:port 设置代理 -f OFFSET, --offset OFFSET 从第offset个开始,只对 -V 有用。 ``` @@ -1314,6 +1315,10 @@ tm是tumblr.py的马甲 (alias tm='python2 /path/to/tumblr.py') tm http://sosuperawesome.tumblr.com tm http://sosuperawesome.tumblr.com -t beautiful +# 下载图片(使用代理) +tm http://sosuperawesome.tumblr.com -x socks5://127.0.0.1:1024 +tm http://sosuperawesome.tumblr.com -t beautiful -x socks5://127.0.0.1:1024 + # 下载单张图片 tm http://sosuperawesome.tumblr.com/post/121467716523/murosvur-on-etsy From ca39081a7265c32c2438b5ec00e33b63ce5ad128 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Mon, 21 Jan 2019 18:06:33 +0800 Subject: [PATCH 55/71] [pan.baidu.com.py] Update params of `transfer` api, #141 --- pan.baidu.com.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index f38df6c..25cbe82 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -1504,8 +1504,8 @@ def _share_transfer(self, surl, info): j = {'errno': 'file has exist'} return j - data = ('filelist=' \ - + urllib.quote_plus('["%s"]' % info['path'].encode('utf8')) \ + data = ('fsidlist=' \ + + urllib.quote_plus('[%s]' % info['fs_id']) \ + '&path=' \ + urllib.quote_plus(info['remotepath'].encode('utf8')) ) @@ -1582,15 +1582,17 @@ def _get_share_infos(self, url, remotepath, infos): j = info['file_list']['list'] isdirs = [x['isdir'] for x in j] paths = [x['path'] for x in j] - z = zip(isdirs, paths) + fs_ids = [x['fs_id'] for x in j] + z = zip(fs_ids, isdirs, paths) if not infos: infos = [ { - 'isdir': x, - 'path': y, + 'fs_id': a, + 'isdir': b, + 'path': c, 'remotepath': remotepath \ if remotepath[-1] != '/' else remotepath[:-1] - } for x, y in z + } for a, b, c in z ] return infos From ddef9c6be7cc1e7e89f3913824d9501700e88143 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Thu, 24 Jan 2019 12:37:40 +0800 Subject: [PATCH 56/71] Remove cookie from headers of `_secret_or_not`, try to fix #142 --- pan.baidu.com.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index 25cbe82..9ca9009 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -1639,8 +1639,8 @@ def save_share(self, url, remotepath, infos=None): def _secret_or_not(url): ss.headers['Referer'] = 'http://pan.baidu.com' r = ss.get(url, headers=headers) + if r.status_code != 200 and r.status_code != 302: - print('cookies', ss.cookies.get_dict()) ss.headers['Cookie'] = ';'.join(['{}={}'.format(k, v) for k, v in ss.cookies.get_dict().items()]) r = ss.get(url, headers=headers, cookies=r.cookies) @@ -1663,8 +1663,8 @@ def _secret_or_not(url): 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 'Accept': '*/*', 'X-Requested-With': 'XMLHttpRequest', - 'Cookie': 'BAIDUID=0F38C66B2C9AC2FC887BD3FEB059F5AC:FG=1; PANWEB=1', 'Connection': 'keep-alive', + 'Referer': 'http://pan.baidu.com' } r = ss.post(url, data=data, headers=theaders) if r.json()['errno']: From 3942e89273c39827adc1451bebc2832f0bed5fcf Mon Sep 17 00:00:00 2001 From: PeterDing Date: Mon, 18 Mar 2019 17:32:24 +0800 Subject: [PATCH 57/71] [pan.baidu.com.py] Update `get_vcode` api --- pan.baidu.com.py | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index 9ca9009..72d35ba 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -2940,20 +2940,40 @@ def get_params(self, path): 'fs_id': j[0]['fs_id'] }) + def get_vcode(self): + url = ( + 'https://pan.baidu.com/api/getvcode' + '?prod=pan' + '&t={}' + '&channel=chunlei' + '&web=1' + '&app_id=250528' + '&bdstoken={}' + ).format(random.random(), self.bdstoken) + + r = ss.get(url) + j = r.json() + return j + def get_infos(self): url = ('https://pan.baidu.com/api/sharedownload?' 'sign={}×tamp={}&bdstoken={}' '&channel=chunlei&clienttype=0&web=1').format( self.sign, self.timestamp, self.bdstoken) - data = ('encrypt=0&product=share' - + '&uk=' + self.uk - + '&primaryid=' + self.shareid - + '&fid_list=' + urllib.quote_plus('["%s"]' % self.infos['fs_id']) - ) + data = { + 'encrypt': '0', + 'product': 'share', + 'uk': self.uk, + 'primaryid': self.shareid, + 'fid_list': urllib.quote_plus('[%s]' % self.infos['fs_id']), + 'path_list': '', + 'vip': '0', + } while True: - r = ss.post(url, data=data) + data_str = '&'.join(['{}={}'.format(k, v) for k, v in data.items()]) + r = ss.post(url, data=data_str) j = r.json() if not j['errno']: dlink = fast_pcs_server(j['list'][0]['dlink'].encode('utf8')) @@ -2964,9 +2984,10 @@ def get_infos(self): panbaiducom_HOME._download_do(self.infos) break else: + j = self.get_vcode() vcode = j['vcode'] input_code = panbaiducom_HOME.save_img(j['img'], 'jpg') - self.params.update({'input': input_code, 'vcode': vcode}) + data.update({'vcode_input': input_code, 'vcode_str': vcode}) def get_infos2(self, path): while True: From 90b786dfe2eab3d7316536ba0357b7c13ac2a3f5 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Mon, 18 Mar 2019 18:24:35 +0800 Subject: [PATCH 58/71] [pan.baidu.com] Fix `verify` error --- pan.baidu.com.py | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index 72d35ba..81ccf8b 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -1637,6 +1637,8 @@ def save_share(self, url, remotepath, infos=None): @staticmethod def _secret_or_not(url): + surl = url.split('?')[0].split('/1')[1].strip('/') + ss.headers['Referer'] = 'http://pan.baidu.com' r = ss.get(url, headers=headers) @@ -1649,12 +1651,17 @@ def _secret_or_not(url): secret = raw_input(s % (2, 92, " 请输入提取密码: ")) else: secret = args.secret + data = 'pwd=%s&vcode=&vcode_str=' % secret - query = 'bdstoken=null&channel=chunlei&clienttype=0&web=1&app_id=250528' - url = "%s&t=%d&%s" % ( - r.url.replace('init', 'verify'), - int(time.time()*1000), - query + url = ( + 'https://pan.baidu.com/share/verify?' + + 'surl=' + surl + + '&t=' + str(int(time.time()*1000)) + + '&channel=chunlei' + + '&web=1' + + '&app_id=250528' + + '&bdstoken=null' + + '&clienttype=0' ) theaders = { 'Accept-Encoding': 'gzip, deflate', @@ -2975,7 +2982,8 @@ def get_infos(self): data_str = '&'.join(['{}={}'.format(k, v) for k, v in data.items()]) r = ss.post(url, data=data_str) j = r.json() - if not j['errno']: + errno = j['errno'] + if errno == 0: dlink = fast_pcs_server(j['list'][0]['dlink'].encode('utf8')) self.infos['dlink'] = dlink if args.play: @@ -2983,6 +2991,9 @@ def get_infos(self): else: panbaiducom_HOME._download_do(self.infos) break + elif errno == 118: + print s % (1, 91, ' !! 没有下载权限!, 请转存网盘后,从网盘地址下载') + sys.exit(1) else: j = self.get_vcode() vcode = j['vcode'] @@ -3008,7 +3019,7 @@ def get_infos2(self, path): panbaiducom_HOME._download_do(self.infos) break else: - print s % (1, ' !! Error at get_infos2, can\'t get dlink') + print s % (1, 91, ' !! Error at get_infos2, can\'t get dlink') def do(self, paths): for path in paths: From 264cc4de11a0bea351098600f26ce1837fe3fa9d Mon Sep 17 00:00:00 2001 From: PeterDing Date: Sat, 14 Sep 2019 12:41:13 +0800 Subject: [PATCH 59/71] [pan.baidu.com.py] Update User-Agent --- pan.baidu.com.py | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index 81ccf8b..581c895 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -114,7 +114,7 @@ "Accept-Language":"en-US,en;q=0.8,zh-CN;q=0.6,zh;q=0.4,zh-TW;q=0.2", "Referer":"http://pan.baidu.com/disk/home", "X-Requested-With": "XMLHttpRequest", - "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36", + "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.75 Safari/537.36", "Connection": "keep-alive", } @@ -758,7 +758,7 @@ def sign2(j, r): self.timestamp = timestamp def _get_dlink(self, path): - dlink = ('http://d.pcs.baidu.com/rest/2.0/pcs/file?method=download' + dlink = ('http://c.pcs.baidu.com/rest/2.0/pcs/file?method=download' '&app_id=250528&path={}&ver=2.0&clienttype=1').format( urllib.quote(path)) @@ -939,25 +939,31 @@ def _download_do(infos): cookie = 'Cookie: ' + '; '.join([ k + '=' + v for k, v in ss.cookies.get_dict().items()]) - user_agent = "netdisk;5.3.1.3;PC;PC-Windows;5.1.2600;WindowsBaiduYunGuanJia" - # user_agent = "netdisk;7.15.1;HUAWEI+G750-T01;android-android;4.2.2" - # user_agent = headers['User-Agent'] + + # Netdisk user agents: + # + # "netdisk;6.7.1.9;PC;PC-Windows;10.0.17763;WindowsBaiduYunGuanJia" + # "netdisk;5.3.1.3;PC;PC-Windows;5.1.2600;WindowsBaiduYunGuanJia" + # "netdisk;7.15.1;HUAWEI+G750-T01;android-android;4.2.2" + # "netdisk;4.4.0.6;PC;PC-Windows;6.2.9200;WindowsBaiduYunGuanJia" + # "netdisk;5.3.1.3;PC;PC-Windows;5.1.2600;WindowsBaiduYunGuanJia" + # + # Recently all downloading requests using above user-agents are limited by baidu + + user_agent = headers['User-Agent'] if args.aget_s: quiet = ' --quiet=true' if args.quiet else '' - #'--user-agent "netdisk;4.4.0.6;PC;PC-Windows;6.2.9200;WindowsBaiduYunGuanJia" ' \ - #'--user-agent "netdisk;5.3.1.3;PC;PC-Windows;5.1.2600;WindowsBaiduYunGuanJia" ' \ - #'--header "Referer:http://pan.baidu.com/disk/home " ' \ - cmd = 'aget -k %s -s %s ' \ + cmd = 'aget ' \ + '"%s" ' \ '-o "%s.tmp" ' \ '-H "User-Agent: %s" ' \ - '-H "Content-Type: application/x-www-form-urlencoded" ' \ + '-H "Referer: http://pan.baidu.com/disk/home" ' \ '-H "Connection: Keep-Alive" ' \ '-H "Accept-Encoding: gzip" ' \ '-H "%s" ' \ - '"%s"' \ - % (args.aget_k, args.aget_s, infos['file'], - user_agent, cookie, infos['dlink']) + '-s %s -k %s' \ + % (infos['dlink'], infos['file'], user_agent, cookie, args.aget_s, args.aget_k) elif args.aria2c: quiet = ' --quiet=true' if args.quiet else '' taria2c = ' -x %s -s %s' % (args.aria2c, args.aria2c) From b9a8edc7539743506e74873e536796fd961838e7 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Sat, 14 Sep 2019 14:05:53 +0800 Subject: [PATCH 60/71] [pan.baidu.com.py] Upload api needs Netdisk user agent --- pan.baidu.com.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index 581c895..83a2f8c 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -118,6 +118,8 @@ "Connection": "keep-alive", } +NETDISK_UA = 'netdisk;8.12.9;;android-android;7.0;JSbridge3.0.0' + ss = requests.session() ss.headers.update(headers) @@ -1143,8 +1145,13 @@ def _rapidupload_file(self, lpath, rpath): "content-crc32" : content_crc32, "ondup" : self.ondup } + + # WARNING: here needs netdist user-agent + theaders = dict(ss.headers) + theaders['User-Agent'] = NETDISK_UA + url = 'https://c.pcs.baidu.com/rest/2.0/pcs/file' - r = ss.post(url, params=p, data=data, verify=VERIFY) + r = ss.post(url, params=p, data=data, verify=VERIFY, headers=theaders) if r.ok: return ENoError else: @@ -1204,8 +1211,13 @@ def _combine_file(self, lpath, rpath): {'block_list': self.upload_datas[lpath]['slice_md5s']} ) } + + # WARNING: here needs netdist user-agent + theaders = dict(ss.headers) + theaders['User-Agent'] = NETDISK_UA + url = 'https://c.pcs.baidu.com/rest/2.0/pcs/file' - r = ss.post(url, params=p, data=data, verify=VERIFY) + r = ss.post(url, params=p, data=data, verify=VERIFY, headers=theaders) if r.ok: return ENoError else: @@ -1232,6 +1244,8 @@ def _upload_slice(self, piece=0, slice=DefaultSliceSize): data = MultipartEncoder(files) theaders = dict(headers) theaders['Content-Type'] = data.content_type + theaders['User-Agent'] = NETDISK_UA + url = 'https://c.pcs.baidu.com/rest/2.0/pcs/file' r = ss.post(url, params=p, data=data, verify=VERIFY, headers=theaders) j = r.json() From c97d7bd521847cc78e53450043ccccfdad475f99 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Fri, 11 Oct 2019 22:01:56 +0800 Subject: [PATCH 61/71] [pan.baidu.com.py] Fix save shared url error --- pan.baidu.com.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index 83a2f8c..6811cbe 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -1659,7 +1659,7 @@ def save_share(self, url, remotepath, infos=None): def _secret_or_not(url): surl = url.split('?')[0].split('/1')[1].strip('/') - ss.headers['Referer'] = 'http://pan.baidu.com' + ss.headers['Referer'] = 'https://pan.baidu.com' r = ss.get(url, headers=headers) if r.status_code != 200 and r.status_code != 302: @@ -1691,12 +1691,14 @@ def _secret_or_not(url): 'Accept': '*/*', 'X-Requested-With': 'XMLHttpRequest', 'Connection': 'keep-alive', - 'Referer': 'http://pan.baidu.com' + 'Sec-Fetch-Mode': 'cors', + 'Referer': 'https://pan.baidu.com/share/init?surl=' + surl } r = ss.post(url, data=data, headers=theaders) if r.json()['errno']: - print s % (2, 91, " !! 提取密码错误\n") + print s % (2, 91, " !! 提取密码错误, %s\n" % r.text) sys.exit(1) + ss.cookies.update(r.cookies.get_dict()) ####################################################################### # for saveing inbox shares @@ -2916,7 +2918,6 @@ def cd_do(path): class panbaiducom(object): @staticmethod def get_web_fileinfo(cm, url): - info = {} if 'shareview' in url: info['uk'] = re.search(r'uk="(\d+)"', cm).group(1) info['shareid'] = re.search(r'shareid="(\d+)"', cm).group(1) From 6594900e2b6318e35b3b70337b1cbb498ecae014 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Fri, 24 Apr 2020 13:50:25 +0800 Subject: [PATCH 62/71] [pan.baidu.com.py] Update mpv arguments --- pan.baidu.com.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index 6811cbe..f855063 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -1033,12 +1033,10 @@ def _play_do(infos): cookie = 'Cookie: ' + '; '.join([ k + '=' + v for k, v in ss.cookies.get_dict().items()]) + user_agent = 'User-Agent: ' + headers['User-Agent'] quiet = ' --really-quiet' if args.quiet else '' - cmd = 'mpv%s --no-ytdl --cache-default 20480 --cache-secs 120 ' \ - '--http-header-fields "%s" ' \ - '--http-header-fields "%s" ' \ - '"%s"' \ - % (quiet, headers['User-Agent'], cookie, infos['dlink']) + cmd = 'mpv%s --no-ytdl --http-header-fields="%s","%s" "%s"' \ + % (quiet, user_agent, cookie, infos['dlink']) os.system(cmd) timeout = 1 From a167b875ee9030e5c1a5e8cfdbdfbf441c02736e Mon Sep 17 00:00:00 2001 From: PeterDing Date: Tue, 23 Jun 2020 10:50:09 +0800 Subject: [PATCH 63/71] Support to setting `appid` --- pan.baidu.com.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index f855063..a126557 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -760,9 +760,11 @@ def sign2(j, r): self.timestamp = timestamp def _get_dlink(self, path): + # use app_id: 778750 + # reference: [3个方法解决百度网盘限速](https://www.runningcheese.com/baiduyun) dlink = ('http://c.pcs.baidu.com/rest/2.0/pcs/file?method=download' - '&app_id=250528&path={}&ver=2.0&clienttype=1').format( - urllib.quote(path)) + '&app_id={}&path={}&ver=2.0&clienttype=1').format( + args.appid, urllib.quote(path)) dlink = fast_pcs_server(dlink) return dlink @@ -956,7 +958,7 @@ def _download_do(infos): if args.aget_s: quiet = ' --quiet=true' if args.quiet else '' - cmd = 'aget ' \ + cmd = 'ag ' \ '"%s" ' \ '-o "%s.tmp" ' \ '-H "User-Agent: %s" ' \ @@ -3109,6 +3111,8 @@ def handle_args(argv): type=int, help='aget 分段下载数量') p.add_argument('-k', '--aget_k', action='store', default='200K', \ type=str, help='aget 分段大小') + p.add_argument('--appid', action='store', default='250528', type=str, \ + help='设置 app-id. 如果无法下载或下载慢, 尝试设置为 778750') p.add_argument('-p', '--play', action='store_true', help='play with mpv') p.add_argument('-v', '--view', action='count', help='view details') p.add_argument('-V', '--VERIFY', action='store_true', help='verify') From 0b16f45a3d4d4f89cbec2a26ca4f1f6da5fbe2e9 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Tue, 23 Jun 2020 10:50:25 +0800 Subject: [PATCH 64/71] Update --- README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6c4b9e5..1c8cb81 100644 --- a/README.md +++ b/README.md @@ -443,6 +443,7 @@ jca 或 jobclearall # 清除 *全部任务* -k num, --aget_k size aget 分段大小: eg: -k 200K -k 1M -k 2M +--appid num 设置 app-id. 如果无法下载或下载慢, 尝试设置为 778750 -p, --play play with mpv -P password, --passwd password 分享密码,加密密码 -y, --yes yes # 用于 rmre, mvre, cpre, rnre !!慎用 @@ -558,7 +559,8 @@ bp cd ... ``` ## 下载、播放速度慢? -如果wiki中的速度解决方法不管用,可以试试加该参数 -t fs +如果无法下载或下载慢, 尝试设置参数 --appid 778750 +bp d /path/file --appid 778750 # 下载当前工作目录 (递归) bp d . -R @@ -780,6 +782,10 @@ ls、重命名、移动、删除、复制、使用正则表达式进行文件操 > https://github.com/houtianze/bypy + +> 3个方法解决百度网盘限速: https://www.runningcheese.com/baiduyun + + --- @@ -907,6 +913,7 @@ bt c magnet_link -t be64 > http://en.wikipedia.org/wiki/Torrent_file + --- From 341e0fcf68dd9e24ac2b26a0b9a7f1a872e965f2 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Sun, 18 Oct 2020 19:57:50 +0800 Subject: [PATCH 65/71] Add option `outdir` --- pan.baidu.com.py | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index a126557..69befff 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -489,7 +489,7 @@ def _get_bdstoken(self): html_string = resp.content - mod = re.search(r'"bdstoken":"(.+?)"', html_string) + mod = re.search(r"bdstoken', '(.+?)'", html_string) if mod: self.bdstoken = mod.group(1) return self.bdstoken @@ -873,7 +873,7 @@ def download(self, paths): t = i['path'].encode('utf8') t = t.replace(base_dir, '') t = t[1:] if t[0] == '/' else t - t = os.path.join(os.getcwd(), t) + t = os.path.join(args.outdir, t) i['dlink'] = self._get_dlink(i['path'].encode('utf8')) @@ -896,7 +896,7 @@ def download(self, paths): elif not meta['info'][0]['isdir']: t = os.path.join( - os.getcwd(), meta['info'][0]['server_filename'].encode('utf8') + args.outdir, meta['info'][0]['server_filename'].encode('utf8') ) infos = { 'file': t, @@ -952,17 +952,18 @@ def _download_do(infos): # "netdisk;4.4.0.6;PC;PC-Windows;6.2.9200;WindowsBaiduYunGuanJia" # "netdisk;5.3.1.3;PC;PC-Windows;5.1.2600;WindowsBaiduYunGuanJia" # + # 'LogStatistic' + # Recently all downloading requests using above user-agents are limited by baidu user_agent = headers['User-Agent'] if args.aget_s: quiet = ' --quiet=true' if args.quiet else '' - cmd = 'ag ' \ + cmd = 'aget ' \ '"%s" ' \ '-o "%s.tmp" ' \ '-H "User-Agent: %s" ' \ - '-H "Referer: http://pan.baidu.com/disk/home" ' \ '-H "Connection: Keep-Alive" ' \ '-H "Accept-Encoding: gzip" ' \ '-H "%s" ' \ @@ -978,7 +979,7 @@ def _download_do(infos): '--header "%s" ' \ '"%s"' \ % (quiet, taria2c, tlimit, infos['name'], - infos['dir_'], headers['User-Agent'], + infos['dir_'], user_agent, cookie, infos['dlink']) else: quiet = ' -q' if args.quiet else '' @@ -990,7 +991,7 @@ def _download_do(infos): '--header "%s" ' \ '"%s"' \ % (quiet, tlimit, infos['file'], - headers['User-Agent'], cookie, infos['dlink']) + user_agent, cookie, infos['dlink']) status = os.system(cmd) exit = True @@ -1001,7 +1002,15 @@ def _download_do(infos): pass else: exit = False - if status != 0: # other http-errors, such as 302. + + content_length_matched = False + saved_path = '%s.tmp' % infos['file'] + if os.path.exists(saved_path): + meta = os.stat(saved_path) + if meta.st_size == infos['size']: + content_length_matched = True + + if status != 0 or not content_length_matched: # other http-errors, such as 302. #wget_exit_status_info = wget_es[status] print('\n\n ---### \x1b[1;91mEXIT STATUS\x1b[0m ==> '\ '\x1b[1;91m%d\x1b[0m ###--- \n\n' % status) @@ -2962,9 +2971,9 @@ def get_params(self, path): self.infos.update({ 'name': j[0]['server_filename'].encode('utf8'), 'file': os.path.join( - os.getcwd(), j[0]['server_filename'].encode('utf8') + args.outdir, j[0]['server_filename'].encode('utf8') ), - 'dir_': os.getcwd(), + 'dir_': args.outdir, 'fs_id': j[0]['fs_id'] }) @@ -3030,8 +3039,8 @@ def get_infos2(self, path): if dlink: self.infos = { 'name': name, - 'file': os.path.join(os.getcwd(), name), - 'dir_': os.getcwd(), + 'file': os.path.join(args.outdir, name), + 'dir_': args.outdir, 'dlink': fast_pcs_server(dlink.group(1)) } if args.play: @@ -3061,8 +3070,8 @@ def do4(self, paths): name = urllib.unquote_plus(t) self.infos = { 'name': name, - 'file': os.path.join(os.getcwd(), name), - 'dir_': os.getcwd(), + 'file': os.path.join(args.outdir, name), + 'dir_': args.outdir, 'dlink': fast_pcs_server(path) } @@ -3113,6 +3122,8 @@ def handle_args(argv): type=str, help='aget 分段大小') p.add_argument('--appid', action='store', default='250528', type=str, \ help='设置 app-id. 如果无法下载或下载慢, 尝试设置为 778750') + p.add_argument('-o', '--outdir', action='store', default=os.getcwd(), \ + type=str, help='保存目录') p.add_argument('-p', '--play', action='store_true', help='play with mpv') p.add_argument('-v', '--view', action='count', help='view details') p.add_argument('-V', '--VERIFY', action='store_true', help='verify') From 94ca2179bba84460ca50fbaffccbc2034f854bab Mon Sep 17 00:00:00 2001 From: PeterDing Date: Sun, 18 Oct 2020 20:01:01 +0800 Subject: [PATCH 66/71] Update --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1c8cb81..5106f7d 100644 --- a/README.md +++ b/README.md @@ -444,6 +444,7 @@ jca 或 jobclearall # 清除 *全部任务* -k 1M -k 2M --appid num 设置 app-id. 如果无法下载或下载慢, 尝试设置为 778750 +-o path, --outdir path 指定下周目录: eg: -o /path/to/directory -p, --play play with mpv -P password, --passwd password 分享密码,加密密码 -y, --yes yes # 用于 rmre, mvre, cpre, rnre !!慎用 From e21f0f12444cfc072001964073af53e4efdf7209 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Mon, 16 Nov 2020 16:21:30 +0800 Subject: [PATCH 67/71] Update `_get_dlink` api using code from BaiduPCS-Go. Using aget-rs --- pan.baidu.com.py | 469 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 464 insertions(+), 5 deletions(-) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index 69befff..86e1124 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -3,6 +3,7 @@ import os import sys +import hashlib import functools import requests requests.packages.urllib3.disable_warnings() # disable urllib3's warnings https://urllib3.readthedocs.org/en/latest/security.html#insecurerequestwarning @@ -102,6 +103,348 @@ ".zoo", ".zpaq", ".zz" ] +PHONE_MODEL_DATABASE = [ + "1501_M02", # 360 F4 + "1503-M02", # 360 N4 + "1505-A01", # 360 N4S + "303SH", # 夏普 Aquos Crystal Xx Mini 303SH + "304SH", # 夏普 Aquos Crystal Xx SoftBank + "305SH", # 夏普 Aquos Crystal Y + "306SH", # 夏普 Aquos Crystal 306SH + "360 Q5 Plus", # 360 Q5 Plus + "360 Q5", # 360 Q5 + "402SH", # 夏普 Aquos Crystal X + "502SH", # 夏普 Aquos Crystal Xx2 + "6607", # OPPO U3 + "A1001", # 一加手机1 + "ASUS_A001", # 华硕 ZenFone 3 Ultra + "ASUS_A001", # 华硕 ZenFone 3 Ultra + "ASUS_Z00ADB", # 华硕 ZenFone 2 + "ASUS_Z00UDB", # 华硕 Zenfone Selfie + "ASUS_Z00XSB", # 华硕 ZenFone Zoom + "ASUS_Z012DE", # 华硕 ZenFone 3 + "ASUS_Z012DE", # 华硕 ZenFone 3 + "ASUS_Z016D", # 华硕 ZenFone 3 尊爵 + "ATH-TL00H", # 华为 荣耀 7i + "Aster T", # Vertu Aster T + "BLN-AL10", # 华为 荣耀 畅玩6X + "BND-AL10", # 荣耀7X + "BTV-W09", # 华为 M3 + "CAM-UL00", # 华为 荣耀 畅玩5A + "Constellation V", # Vertu Constellation V + "D6683", # 索尼 Xperia Z3 Dual TD + "DIG-AL00", # 华为 畅享 6S + "E2312", # 索尼 Xperia M4 Aqua + "E2363 ", # 索尼 Xperia M4 Aqua Dual + "E5363", # 索尼 Xperia C4 + "E5563", # 索尼 Xperia C5 + "E5663", # 索尼 Xperia M5 + "E5823", # 索尼 Xperia Z5 Compact + "E6533", # 索尼 Xperia Z3+ + "E6683", # 索尼 Xperia Z5 + "E6883", # 索尼 Xperia Z5 Premium + "EBEN M2", # 8848 M2 + "EDI-AL10", # 华为 荣耀 Note 8 + "EVA-AL00", # 华为 P9 + "F100A", # 金立 F100 + "F103B", # 金立 F103B + "F3116", # 索尼 Xperia XA + "F3216", # 索尼 Xperia XA Ultra + "F5121 / F5122", # 索尼 Xperia X + "F5321", # 索尼 Xperia X Compact + "F8132", # 索尼 Xperia X Performance + "F8332", # 索尼 Xperia XZ + "FRD-AL00", # 华为 荣耀 8 + "FS8001", # 夏普 C1 + "FS8002", # 夏普 A1 + "G0111", # 格力手机 1 + "G0215", # 格力手机 2 + "G8142", # 索尼Xperia XZ Premium G8142 + "G8342", # 索尼Xperia XZ1 + "GIONEE S9", # 金立 S9 + "GN5001S", # 金立 金钢 + "GN5003", # 金立 大金钢 + "GN8002S", # 金立 M6 Plus + "GN8003", # 金立 M6 + "GN9011", # 金立 S8 + "GN9012", # 金立 S6 Pro + "GRA-A0", # Coolpad Cool Play 6C + "H60-L11", # 华为 荣耀 6 + "HN3-U01", # 华为 荣耀 3 + "HTC D10w", # HTC Desire 10 Pro + "HTC E9pw", # HTC One E9+ + "HTC M10u", # HTC 10 + "HTC M8St", # HTC One M8 + "HTC M9PT", # HTC One M9+ + "HTC M9e", # HTC One M9 + "HTC One A9", # HTC One A9 + "HTC U-1w", # HTC U Ultra + "HTC X9u", # HTC One X9 + "HTC_M10h", # HTC 10 国际版 + "HUAWEI CAZ-AL00", # 华为 Nova + "HUAWEI CRR-UL00", # 华为 Mate S + "HUAWEI GRA-UL10", # 华为 P8 + "HUAWEI MLA-AL10", # 华为 麦芒 5 + "HUAWEI MT7-AL00", # 华为 mate 7 + "HUAWEI MT7-TL00", # 华为 Mate 7 + "HUAWEI NXT-AL10", # 华为 Mate 8 + "HUAWEI P7-L00", # 华为 P7 + "HUAWEI RIO-AL00", # 华为 麦芒 4 + "HUAWEI TAG-AL00", # 华为 畅享 5S + "HUAWEI VNS-AL00", # 华为 G9 + "IUNI N1", # 艾优尼 N1 + "IUNI i1", # 艾优尼 i1 + "KFAPWI", # Amazon Kindle Fire HDX 8.9 + "KFSOWI", # Amazon Kindle Fire HDX 7 + "KFTHWI", # Amazon Kindle Fire HD + "KIW-TL00H", # 华为 荣耀 畅玩5X + "KNT-AL10", # 华为 荣耀 V8 + "L55t", # 索尼 Xperia Z3 + "L55u", # 索尼 Xperia Z3 + "LEX626", # 乐视 乐S3 + "LEX720", # 乐视 乐Pro3 + "LG-D858", # LG G3 + "LG-H818", # LG G4 + "LG-H848", # LG G5 SE + "LG-H868", # LG G5 + "LG-H968", # LG V10 + "LON-AL00", # 华为 Mate 9 Pro + "LON-AL00-PD", # 华为 Mate 9 Porsche Design + "LT18i", # Sony Ericsson Xperia Arc S + "LT22i", # Sony Ericsson Xperia P + "LT26i", # Sony Ericsson Xperia S + "LT26ii", # Sony Ericsson Xperia SL + "LT26w", # Sony Ericsson Xperia Acro S + "Le X520", # 乐视 乐2 + "Le X620", # 乐视 乐2Pro + "Le X820", # 乐视 乐Max2 + "Lenovo A3580", # 联想 黄金斗士 A8 畅玩 + "Lenovo A7600-m", # 联想 黄金斗士 S8 + "Lenovo A938t", # 联想 黄金斗士 Note8 + "Lenovo K10e70", # 联想 乐檬K10 + "Lenovo K30-T", # 联想 乐檬 K3 + "Lenovo K32C36", # 联想 乐檬3 + "Lenovo K50-t3s", # 联想 乐檬 K3 Note + "Lenovo K52-T38", # 联想 乐檬 K5 Note + "Lenovo K52e78", # Lenovo K5 Note + "Lenovo P2c72", # 联想 P2 + "Lenovo X3c50", # 联想 乐檬 X3 + "Lenovo Z90-3", # 联想 VIBE Shot大拍 + "M040", # 魅族 MX 2 + "M1 E", # 魅蓝 E + "M2-801w", # 华为 M2 + "M2017", # 金立 M2017 + "M3", # EBEN M3 + "M355", # 魅族 MX 3 + "MHA-AL00", # 华为 Mate 9 + "MI 4LTE", # 小米手机4 + "MI 4S", # 小米手机4S + "MI 5", # 小米手机5 + "MI 5s Plus", # 小米手机5s Plus + "MI 5s", # 小米手机5s + "MI MAX", # 小米Max + "MI Note Pro", # 小米Note顶配版 + "MI PAD 2", # 小米平板 2 + "MIX", # 小米MIX + "MLA-UL00", # 华为 G9 Plus + "MP1503", # 美图 M6 + "MP1512", # 美图 M6s + "MT27i", # Sony Ericsson Xperia Sola + "MX4 Pro", # 魅族 MX 4 Pro + "MX4", # 魅族 MX 4 + "MX5", # 魅族 MX 5 + "MX6", # 魅族 MX 6 + "Meitu V4s", # 美图 V4s + "Meizu M3 Max", # 魅蓝max + "Meizu U20", # 魅蓝U20 + "Mi 5", + "Mi 6", + "Mi A1", # MI androidone + "Mi Note 2", # 小米Note2 + "MiTV2S-48", # 小米电视2s + "Moto G (4)", # 摩托罗拉 G⁴ Plus + "N1", # Nokia N1 + "NCE-AL00", # 华为 畅享 6 + "NTS-AL00", # 华为 荣耀 Magic + "NWI-AL10", # nova2s + "NX508J", # 努比亚 Z9 + "NX511J", # 努比亚 小牛4 Z9 Mini + "NX512J", # 努比亚 大牛 Z9 Max + "NX513J", # 努比亚 My 布拉格 + "NX513J", # 努比亚 布拉格S + "NX523J", # 努比亚 Z11 Max + "NX529J", # 努比亚 小牛5 Z11 Mini + "NX531J", # 努比亚 Z11 + "NX549J", # 努比亚 小牛6 Z11 MiniS + "NX563J", # 努比亚Z17 + "Nexus 4", + "Nexus 5X", + "Nexus 6", + "Nexus 6P", + "Nexus 7", + "Nexus 9", + "Nokia_X", # Nokia X + "Nokia_XL_4G", # Nokia XL + "ONE A2001", # 一加手机2 + "ONE E1001", # 一加手机X + "ONEPLUS A5010", # 一加5T + "OPPO A53", # OPPO A53 + "OPPO A59M", # OPPO A59 + "OPPO A59s", # OPPO A59s + "OPPO R11", + "OPPO R7", # OPPO R7 + "OPPO R7Plus", # OPPO R7Plus + "OPPO R7S", # OPPO R7S + "OPPO R7sPlus", # OPPO R7sPlus + "OPPO R9 Plustm A", # OPPO R9Plus + "OPPO R9s Plus", # OPPO R9s Plus + "OPPO R9s", + "OPPO R9s", # OPPO R9s + "OPPO R9tm", # OPPO R9 + "PE-TL10", # 华为 荣耀 6 Plus + "PLK-TL01H", # 华为 荣耀 7 + "Pro 5", # 魅族 Pro 5 + "Pro 6", # 魅族 Pro 6 + "Pro 6s", # 魅族 Pro 6s + "RM-1010", # Nokia Lumia 638 + "RM-1018", # Nokia Lumia 530 + "RM-1087", # Nokia Lumia 930 + "RM-1090", # Nokia Lumia 535 + "RM-867", # Nokia Lumia 920 + "RM-875", # Nokia Lumia 1020 + "RM-887", # Nokia Lumia 720 + "RM-892", # Nokia Lumia 925 + "RM-927", # Nokia Lumia 929 + "RM-937", # Nokia Lumia 1520 + "RM-975", # Nokia Lumia 635 + "RM-977", # Nokia Lumia 630 + "RM-984", # Nokia Lumia 830 + "RM-996", # Nokia Lumia 1320 + "Redmi 3S", # 红米3s + "Redmi 4", # 小米 红米4 + "Redmi 4A", # 小米 红米4A + "Redmi Note 2", # 小米 红米Note2 + "Redmi Note 3", # 小米 红米Note3 + "Redmi Note 4", # 小米 红米Note4 + "Redmi Pro", # 小米 红米Pro + "S3", # 佳域S3 + "SCL-TL00H", # 华为 荣耀 4A + "SD4930UR", # Amazon Fire Phone + "SH-03G", # 夏普 Aquos Zeta SH-03G + "SH-04F", # 夏普 Aquos Zeta SH-04F + "SHV31", # 夏普 Aquos Serie Mini SHV31 + "SM-A5100", # Samsung Galaxy A5 + "SM-A7100", # Samsung Galaxy A7 + "SM-A8000", # Samsung Galaxy A8 + "SM-A9000", # Samsung Galaxy A9 + "SM-A9100", # Samsung Galaxy A9 高配版 + "SM-C5000", # Samsung Galaxy C5 + "SM-C5010", # Samsung Galaxy C5 Pro + "SM-C7000", # Samsung Galaxy C7 + "SM-C7010", # Samsung Galaxy C7 Pro + "SM-C9000", # Samsung Galaxy C9 Pro + "SM-G1600", # Samsung Galaxy Folder + "SM-G5500", # Samsung Galaxy On5 + "SM-G6000", # Samsung Galaxy On7 + "SM-G7100", # Samsung Galaxy On7(2016) + "SM-G7200", # Samsung Galasy Grand Max + "SM-G9198", # Samsung 领世旗舰Ⅲ + "SM-G9208", # Samsung Galaxy S6 + "SM-G9250", # Samsung Galasy S7 Edge + "SM-G9280", # Samsung Galaxy S6 Edge+ + "SM-G9300", # Samsung Galaxy S7 + "SM-G9350", # Samsung Galaxy S7 Edge + "SM-G9500", # Samsung Galaxy S8 + "SM-G9550", # Samsung Galaxy S8+ + "SM-G9600", # Samsung Galaxy S9 + "SM-G960F", # Galaxy S9 Dual SIM + "SM-G9650", # Samsung Galaxy S9+ + "SM-G965F", # Galaxy S9+ Dual SIM + "SM-J3109", # Samsung Galaxy J3 + "SM-J3110", # Samsung Galaxy J3 Pro + "SM-J327A", # Samsung Galaxy J3 Emerge + "SM-J5008", # Samsung Galaxy J5 + "SM-J7008", # Samsung Galaxy J7 + "SM-N9108V", # Samsung Galasy Note4 + "SM-N9200", # Samsung Galaxy Note5 + "SM-N9300", # Samsung Galaxy Note 7 + "SM-N935S", # Samsung Galaxy Note Fan Edition + "SM-N9500", # Samsung Galasy Note8 + "SM-W2015", # Samsung W2015 + "SM-W2016", # Samsung W2016 + "SM-W2017", # Samsung W2017 + "SM705", # 锤子 T1 + "SM801", # 锤子 T2 + "SM901", # 锤子 M1 + "SM919", # 锤子 M1L + "ST18i", # Sony Ericsson Xperia Ray + "ST25i", # Sony Ericsson Xperia U + "STV100-1", # 黑莓Priv + "Signature Touch", # Vertu Signature Touch + "TA-1000", # Nokia 6 + "TA-1000", # HMD Nokia 6 + "TA-1041", # Nokia 7 + "VERTU Ti", # Vertu Ti + "VIE-AL10", # 华为 P9 Plus + "VIVO X20", + "VIVO X20A", + "W909", # 金立 天鉴 W909 + "X500", # 乐视 乐1S + "X608", # 乐视 乐1 + "X800", # 乐视 乐1Pro + "X900", # 乐视 乐Max + "XT1085", # 摩托罗拉 X + "XT1570", # 摩托罗拉 X Style + "XT1581", # 摩托罗拉 X 极 + "XT1585", # 摩托罗拉 Droid Turbo 2 + "XT1635", # 摩托罗拉 Z Play + "XT1635-02", # 摩托罗拉 Z Play + "XT1650", # 摩托罗拉 Z + "XT1650-05", # 摩托罗拉 Z + "XT1706", # 摩托罗拉 E³ POWER + "YD201", # YotaPhone2 + "YD206", # YotaPhone2 + "YQ60", # 锤子 坚果 + "ZTE A2015", # 中兴 AXON 天机 + "ZTE A2017", # 中兴 AXON 天机 7 + "ZTE B2015", # 中兴 AXON 天机 MINI + "ZTE BV0720", # 中兴 Blade A2 + "ZTE BV0730", # 中兴 Blade A2 Plus + "ZTE C2016", # 中兴 AXON 天机 MAX + "ZTE C2017", # 中兴 AXON 天机 7 MAX + "ZTE G720C", # 中兴 星星2号 + "ZUK Z2121", # ZUK Z2 Pro + "ZUK Z2131", # ZUK Z2 + "ZUK Z2151", # ZUK Edge + "ZUK Z2155", # ZUK Edge L + "m030", # 魅族mx + "m1 metal", # 魅蓝metal + "m1 note", # 魅蓝 Note + "m1", # 魅蓝 + "m2 note", # 魅蓝 Note 2 + "m2", # 魅蓝 2 + "m3 note", # 魅蓝 Note 3 + "m3", # 魅蓝 3 + "m3s", # 魅蓝 3S + "m9", # 魅族m9 + "marlin", # Google Pixel XL + "sailfish", # Google Pixel + "vivo V3Max", # vivo V3Max + "vivo X6D", # vivo X6 + "vivo X6PlusD", # vivo X6Plus + "vivo X6S", # vivo X6S + "vivo X6SPlus", # vivo X6SPlus + "vivo X7", # vivo X7 + "vivo X7Plus", # vivo X7Plus + "vivo X9", # vivo X9 + "vivo X9Plus", # vivo X9Plus + "vivo Xplay5A 金", # vivo Xplay5 + "vivo Xplay6", # vivo Xplay6 + "vivo Y66", # vivo Y66 + "vivo Y67", # vivo Y67 + "z1221", # ZUK Z1 +] + s = '\x1b[%s;%sm%s\x1b[0m' # terminual color template cookie_file = os.path.join(os.path.expanduser('~'), '.bp.cookies') @@ -123,6 +466,39 @@ ss = requests.session() ss.headers.update(headers) +def to_md5(buff): + assert isinstance(buff, (str, unicode)) + if isinstance(buff, unicode): + buff = buff.encode('utf-8') + return hashlib.md5(buff).hexdigest() + + +def to_sha1(buff): + assert isinstance(buff, (str, unicode)) + if isinstance(buff, unicode): + buff = buff.encode('utf-8') + return hashlib.sha1(buff).hexdigest() + +# 根据key计算出imei +def sum_IMEI(key): + hs = 53202347234687234 + for k in key: + hs += (hs << 5) + ord(k) + hs %= int(1e15) + if hs < int(1e14): + hs += int(1e14) + return str(int(hs)) + +# 根据key, 从 PHONE_MODEL_DATABASE 中取出手机型号 +def get_phone_model(key): + if len(PHONE_MODEL_DATABASE) <= 0: + return "S3" + hs = 2134 + for k in key: + hs += (hs << 4) + ord(k) + hs %= len(PHONE_MODEL_DATABASE) + return PHONE_MODEL_DATABASE[hs] + def import_shadowsocks(): try: global encrypt @@ -239,6 +615,7 @@ def __init__(self): self.accounts = self._check_cookie_file() self.dsign = None self.timestamp = None + self.user_id = None self.highlights = [] if any([args.tails, args.heads, args.includes]): @@ -309,6 +686,8 @@ def init(self): user = u[0] self.user = user self.cwd = j[user]['cwd'] if j[user].get('cwd') else '/' + self.user_id = j[user].get('user_id') + self.bduss = j[user]['cookies']['BDUSS'] ss.cookies.update(j[user]['cookies']) else: print s % (1, 91, ' !! no account is online, please login or userchange') @@ -320,6 +699,10 @@ def init(self): with open(cookie_file, 'w') as g: pk.dump(j, g) sys.exit(1) + + if not self.user_id: + info = self._user_info(self.bduss) + self.user_id = info['user']['id'] else: print s % (1, 97, ' no account, please login') sys.exit(1) @@ -465,6 +848,7 @@ def save_cookies(self, username=None, on=0, tocwd=False): accounts[username]['cookies'] = \ accounts[username].get('cookies', ss.cookies.get_dict()) accounts[username]['on'] = on + accounts[username]['user_id'] = self.user_id quota = self._get_quota() capacity = '%s/%s' % (sizeof_fmt(quota['used']), sizeof_fmt(quota['total'])) accounts[username]['capacity'] = capacity @@ -499,6 +883,44 @@ def _get_bdstoken(self): # self.bdstoken = md5.new(str(time.time())).hexdigest() + def _user_info(self, bduss): + timestamp = str(int(time.time())) + model = get_phone_model(bduss) + phoneIMEIStr = sum_IMEI(bduss) + + data = { + 'bdusstoken': bduss + '|null', + 'channel_id': '', + 'channel_uid': '', + 'stErrorNums': '0', + 'subapp_type': 'mini', + 'timestamp': timestamp + '922', + } + data['_client_type'] = '2' + data['_client_version'] = '7.0.0.0' + data['_phone_imei'] = phoneIMEIStr + data['from'] = 'mini_ad_wandoujia' + data['model'] = model + data['cuid'] = to_md5( + bduss + '_' + data['_client_version'] + '_' + data['_phone_imei'] + '_' + data['from'] + ).upper() + '|' + phoneIMEIStr[::-1] + data['sign'] = to_md5( + ''.join([k + '=' + data[k] for k in sorted(data.keys())]) + 'tiebaclient!!!' + ).upper() + + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Cookie': 'ka=open', + 'net': '1', + 'User-Agent': 'bdtb for Android 6.9.2.1', + 'client_logid': timestamp + '416', + 'Connection': 'Keep-Alive', + } + + resp = requests.post('http://tieba.baidu.com/c/s/login', headers=headers, data=data) + info = resp.json() + return info + #def _sift(self, fileslist, name=None, size=None, time=None, head=None, tail=None, include=None, exclude=None): def _sift(self, fileslist, **arguments): """ @@ -760,6 +1182,35 @@ def sign2(j, r): self.timestamp = timestamp def _get_dlink(self, path): + bduss = self.bduss + uid = self.user_id + + timestamp = str(int(time.time() * 1000)) + devuid = '0|' + to_md5(bduss).upper() + + enc = to_sha1(bduss) + rand = to_sha1( + enc + str(uid) + 'ebrcUYiuxaZv2XGu7KIYKxUrqfnOfpDF' + str(timestamp) + devuid + ) + + url = ( + 'https://pcs.baidu.com/rest/2.0/pcs/file?app_id=' + args.appid \ + + '&method=locatedownload&ver=2' \ + + '&path=' + urllib.quote(path) + '&time=' \ + + timestamp + '&rand=' + rand + '&devuid=' + devuid + ) + + headers = dict(ss.headers) + headers['User-Agent'] = 'netdisk;2.2.51.6;netdisk;10.0.63;PC;android-android' + resp = self._request('GET', url, '_get_dlink', headers=headers) + info = resp.json() + if info.get('urls'): + return info['urls'][0]['url'].encode('utf8') + else: + print s % (1, 91, ' !! Error at _get_dlink, can\'t get dlink') + sys.exit(1) + + def _get_dlink4(self, path): # use app_id: 778750 # reference: [3个方法解决百度网盘限速](https://www.runningcheese.com/baiduyun) dlink = ('http://c.pcs.baidu.com/rest/2.0/pcs/file?method=download' @@ -956,11 +1407,12 @@ def _download_do(infos): # Recently all downloading requests using above user-agents are limited by baidu - user_agent = headers['User-Agent'] + # user_agent = headers['User-Agent'] + user_agent = 'netdisk;2.2.51.6;netdisk;10.0.63;PC;android-android' if args.aget_s: quiet = ' --quiet=true' if args.quiet else '' - cmd = 'aget ' \ + cmd = 'ag ' \ '"%s" ' \ '-o "%s.tmp" ' \ '-H "User-Agent: %s" ' \ @@ -976,18 +1428,25 @@ def _download_do(infos): cmd = 'aria2c -c%s%s%s ' \ '-o "%s.tmp" -d "%s" ' \ '--user-agent "%s" ' \ + '--header "Connection: Keep-Alive" ' \ + '--header "Accept-Encoding: gzip" ' \ '--header "%s" ' \ '"%s"' \ % (quiet, taria2c, tlimit, infos['name'], infos['dir_'], user_agent, cookie, infos['dlink']) else: + if infos['size'] >= 100 * OneM: + print '\x1b[1;91mWarning\x1b[0m: '\ + '\x1b[1;91m%s\x1b[0m\n\n' % "File size is large, please use aget or aria2 to download\naget: https://github.com/PeterDing/aget-rs\naria2: https://github.com/aria2/aria2" + quiet = ' -q' if args.quiet else '' tlimit = ' --limit-rate %s' % args.limit if args.limit else '' cmd = 'wget -c%s%s ' \ '-O "%s.tmp" ' \ - '--user-agent "%s" ' \ - '--header "Referer:http://pan.baidu.com/disk/home" ' \ + '--header "User-Agent: %s" ' \ + '--header "Connection: Keep-Alive" ' \ + '--header "Accept-Encoding: gzip" ' \ '--header "%s" ' \ '"%s"' \ % (quiet, tlimit, infos['file'], @@ -3120,7 +3579,7 @@ def handle_args(argv): type=int, help='aget 分段下载数量') p.add_argument('-k', '--aget_k', action='store', default='200K', \ type=str, help='aget 分段大小') - p.add_argument('--appid', action='store', default='250528', type=str, \ + p.add_argument('--appid', action='store', default='778750', type=str, \ help='设置 app-id. 如果无法下载或下载慢, 尝试设置为 778750') p.add_argument('-o', '--outdir', action='store', default=os.getcwd(), \ type=str, help='保存目录') From 25a4c334c691a508c9ab473c9a4d41676a770517 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Mon, 16 Nov 2020 16:22:04 +0800 Subject: [PATCH 68/71] Update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5106f7d..7171cc0 100644 --- a/README.md +++ b/README.md @@ -213,7 +213,7 @@ wget aria2 (~ 1.18) -aget # 需要 python >= 3.5, 安装 pip3 install aget +aget-rs (https://github.com/PeterDing/aget-rs/releases) pip2 install rsa pyasn1 requests requests-toolbelt From 65131cda47750cda281164cd04de5ed7e1a2785e Mon Sep 17 00:00:00 2001 From: PeterDing Date: Thu, 10 Dec 2020 18:52:36 +0800 Subject: [PATCH 69/71] Update `_get_bdstoken`; fix the error of mpv playing m3u8 --- pan.baidu.com.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index 86e1124..a13c270 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -873,7 +873,8 @@ def _get_bdstoken(self): html_string = resp.content - mod = re.search(r"bdstoken', '(.+?)'", html_string) + open('/tmp/t/sss', 'w').write(html_string) + mod = re.search(r'bdstoken[\'":\s]+([0-9a-f]{32})', html_string) if mod: self.bdstoken = mod.group(1) return self.bdstoken @@ -1205,7 +1206,8 @@ def _get_dlink(self, path): resp = self._request('GET', url, '_get_dlink', headers=headers) info = resp.json() if info.get('urls'): - return info['urls'][0]['url'].encode('utf8') + dlink = info['urls'][0]['url'].encode('utf8') + return dlink else: print s % (1, 91, ' !! Error at _get_dlink, can\'t get dlink') sys.exit(1) @@ -1417,7 +1419,6 @@ def _download_do(infos): '-o "%s.tmp" ' \ '-H "User-Agent: %s" ' \ '-H "Connection: Keep-Alive" ' \ - '-H "Accept-Encoding: gzip" ' \ '-H "%s" ' \ '-s %s -k %s' \ % (infos['dlink'], infos['file'], user_agent, cookie, args.aget_s, args.aget_k) @@ -1505,8 +1506,14 @@ def _play_do(infos): k + '=' + v for k, v in ss.cookies.get_dict().items()]) user_agent = 'User-Agent: ' + headers['User-Agent'] quiet = ' --really-quiet' if args.quiet else '' - cmd = 'mpv%s --no-ytdl --http-header-fields="%s","%s" "%s"' \ - % (quiet, user_agent, cookie, infos['dlink']) + cmd = 'mpv%s --no-ytdl --http-header-fields="%s","%s" ' \ + % (quiet, user_agent, cookie) + + if infos.get('m3u8'): + # https://github.com/mpv-player/mpv/issues/6928#issuecomment-532198445 + cmd += ' --stream-lavf-o-append="protocol_whitelist=file,http,https,tcp,tls,crypto,hls,applehttp" ' + + cmd += "%s" % infos['dlink'] os.system(cmd) timeout = 1 @@ -3301,7 +3308,7 @@ def _share(self, paths, pwd=None): r = ss.post(url, params=params, data=data) j = r.json() - if j['errno'] != 0: + if not j.get('shorturl'): print s % (1, 91, ' !! Error at _share'), j sys.exit(1) else: From a2af64e8dfcf9906f4c145ce84a1fe2682eab6c1 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Mon, 14 Dec 2020 14:58:39 +0800 Subject: [PATCH 70/71] Remove unused code --- pan.baidu.com.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index a13c270..2617123 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -873,7 +873,6 @@ def _get_bdstoken(self): html_string = resp.content - open('/tmp/t/sss', 'w').write(html_string) mod = re.search(r'bdstoken[\'":\s]+([0-9a-f]{32})', html_string) if mod: self.bdstoken = mod.group(1) From 556ad287dc3b0879a539549fed776f3ef2ef4a1a Mon Sep 17 00:00:00 2001 From: PeterDing Date: Thu, 21 Jan 2021 16:12:50 +0800 Subject: [PATCH 71/71] Update --- README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7171cc0..81fa0af 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,8 @@ -## iScript +# iScript + +## pan.baidu.com.py 已经重构,不再维护 + +[**BaiduPCS-Py**](https://github.com/PeterDing/BaiduPCS-Py) 是 pan.baidu.com.py 的重构版,运行在 Python >= 3.6 [![Join the chat at https://gitter.im/PeterDing/iScript](https://badges.gitter.im/PeterDing/iScript.svg)](https://gitter.im/PeterDing/iScript?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) @@ -206,6 +210,10 @@ xm s http://www.xiami.com/artist/23460?spm=a1z1s.6928801.1561534521.115.ShW08b ### pan.baidu.com.py - 百度网盘的下载、离线下载、上传、播放、转存、文件操作 +**pan.baidu.com.py 已经重构,不再维护** + +[**BaiduPCS-Py**](https://github.com/PeterDing/BaiduPCS-Py) 是 pan.baidu.com.py 的重构版,运行在 Python >= 3.6 + #### 1. 依赖 ```