From d80b2959f991abdf814014861f0d93305db9e23e Mon Sep 17 00:00:00 2001 From: PeterDing Date: Mon, 9 Mar 2015 18:58:41 +0800 Subject: [PATCH 001/158] update; fix --- README.md | 2 - pan.baidu.com.py | 154 ++++++++++++++++++++++++----------------------- tumblr.py | 2 +- 3 files changed, 81 insertions(+), 77 deletions(-) diff --git a/README.md b/README.md index e613273..374513c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ ## iScript -包含项目: - > *[L]* *[W]* *[LW]* 分别表示,在linux, windows, linux和windows 下通过测试。 > ***windows用户可在babun (https://github.com/babun/babun) 下运行。*** diff --git a/pan.baidu.com.py b/pan.baidu.com.py index 0652898..6f5fe1f 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -735,13 +735,6 @@ def download(self, paths): print s % (1, 91, ' !! path is not existed.\n'), \ ' --------------\n ', path - def do_share(self, paths, pwd): - - for path in paths: - path = [path] - self._share(path, pwd) - - @staticmethod def _download_do(infos): ## make dirs @@ -858,70 +851,6 @@ def _make_dir(self, dir_): else: return ENoError - - def _share(self, paths, pwd=None): - """ - 创建一个文件的分享链接 - :param fs_ids: 要分享的文件fid列表 - :type fs_ids: list - :param pwd: 分享密码,没有则没有密码 - :type pwd: str - :return: requests.Response 对象 - .. note:: - 返回正确 - { - "errno": 0, - "request_id": 请求识别号, - "shareid": 分享识别号, - "link": "分享地址", - "shorturl": "段网址", - "ctime": 创建时间, - "premis": false - } - """ - params = { - 'app_id': 250528, - 'channel': 'chunlei', - 'clienttype': 0, - 'web': 1, - 'bdstoken': self._get_bdstoken(), - } - - meta = self._meta(paths) - - fs_ids = [i['fs_id'] for i in meta['info']] - - if pwd: - data = { - 'fid_list': json.dumps(fs_ids), - 'schannel': 4, - 'channel_list': '[]', - 'pwd': pwd, - } - else: - data = { - 'fid_list': json.dumps(fs_ids), - 'schannel': 0, - 'channel_list': '[]' - } - - url = 'http://pan.baidu.com/share/set' - r = ss.post(url, params=params, data=data) - j = r.json() - - if j['errno'] != 0: - print s % (1, 91, ' !! Error at _share'), j - sys.exit(1) - else: - print '分享创建成功,文件:%s' % paths - print '创建时间:%s' % (time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(j.get('ctime')))) - print '链接地址:%s' % (j.get('shorturl').encode('utf8')) - print '密码:%s' % pwd - if 0 == meta['info'][0]['isdir']: - print 'MD5:%s' % (meta['info'][0]['md5']) - return ENoError - - def _meta(self, file_list, dlink=0): p = { "channel": "chunlei", @@ -2507,6 +2436,75 @@ def mkdir(self, paths): else: print s % (1, 91, ' !! Error: file exists.'), path + + ############################################################ + # for share + def _share(self, paths, pwd=None): + """ + 创建一个文件的分享链接 + :param fs_ids: 要分享的文件fid列表 + :type fs_ids: list + :param pwd: 分享密码,没有则没有密码 + :type pwd: str + :return: requests.Response 对象 + .. note:: + 返回正确 + { + "errno": 0, + "request_id": 请求识别号, + "shareid": 分享识别号, + "link": "分享地址", + "shorturl": "段网址", + "ctime": 创建时间, + "premis": false + } + """ + meta = self._meta(paths) + fs_ids = [i['fs_id'] for i in meta['info']] + + params = { + 'app_id': 250528, + 'channel': 'chunlei', + 'clienttype': 0, + 'web': 1, + 'bdstoken': self._get_bdstoken(), + } + + if pwd: + data = { + 'fid_list': json.dumps(fs_ids), + 'schannel': 4, + 'channel_list': '[]', + 'pwd': pwd, + } + else: + data = { + 'fid_list': json.dumps(fs_ids), + 'schannel': 0, + 'channel_list': '[]' + } + + url = 'http://pan.baidu.com/share/set' + r = ss.post(url, params=params, data=data) + j = r.json() + + if j['errno'] != 0: + print s % (1, 91, ' !! Error at _share'), j + sys.exit(1) + else: + print '\n 链接地址:', s % (1, 92, j.get('shorturl').encode('utf8')) + if pwd: print ' 密码:', s % (1, 91, pwd) + #if 0 == meta['info'][0]['isdir']: + #print 'MD5:%s' % (meta['info'][0]['md5']) + return ENoError + + def share(self, paths, pwd): + for path in paths: + path = [path] + self._share(path, pwd) + + + class panbaiducom(object): @staticmethod def get_web_fileinfo(cm, url): @@ -2804,9 +2802,18 @@ def main(argv): if len(xxx) < 1: print s % (1,91, ' !! S path1 path2 \n share path1 path2 \n ..') sys.exit(1) + + pwd = args.passwd + if pwd: + if not re.match(r'^[a-z0-9]{4}$', pwd): + from string import lowercase, digits + print s % (1, 91, ' !! passwd is wrong and will be randomly choiced.' \ + '\n passwd is 4 symbols and choiced from %s%s' \ + % (lowercase, digits)) + pwd = ''.join(random.sample(lowercase + digits, 4)) + paths = xxx paths1 = [] - for path in paths: if path[0] == '/': paths1.append(path) @@ -2815,8 +2822,7 @@ def main(argv): if paths1: x = panbaiducom_HOME() x.init() - pwd = args.passwd if args.passwd else None - x.do_share(paths1,pwd) + x.share(paths1, pwd) elif comd == 'd' or comd == 'download' \ or comd == 'p' or comd == 'play': diff --git a/tumblr.py b/tumblr.py index 54579ba..04c9cbd 100755 --- a/tumblr.py +++ b/tumblr.py @@ -183,7 +183,7 @@ def run(i): col = s % (1, num + 90, i['filepath']) print ' ++ download: %s' % col cmd = [ - 'wget', '-c', '-q', + 'wget', '-c', '-q', '-o', '/dev/null', '-O', '%s.tmp' % i['filepath'], '--user-agent', '"%s"' % headers['User-Agent'], '%s' % i['durl'] From ec967dbcdfad81f1c973df4fac1e1f89b93dac11 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Mon, 9 Mar 2015 23:11:37 +0800 Subject: [PATCH 002/158] #24 - fix 91porn.py --- 91porn.py | 4 ++-- README.md | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/91porn.py b/91porn.py index d48a2af..53c84be 100755 --- a/91porn.py +++ b/91porn.py @@ -77,7 +77,7 @@ def get_infos(self): 'dir_': os.getcwd(), 'dlink': dlink } - if not args.url: + if not args.get_url: self.download(infos) else: print dlink @@ -147,7 +147,7 @@ def main(url): help='download with aria2c') p.add_argument('-p', '--play', action='store_true', \ help='play with mpv') - p.add_argument('-u', '--url', action='store_true', \ + p.add_argument('-u', '--get_url', action='store_true', \ help='print download_url without download') args = p.parse_args() main(args.url) diff --git a/README.md b/README.md index 374513c..0fde505 100644 --- a/README.md +++ b/README.md @@ -1082,6 +1082,8 @@ \# pn 是91porn.py的马甲 (alias pn='python2 /path/to/91porn.py') + 下载: + pn url # 91porn.com(或其镜像) 视频的url 播放: From 143446724968e67c37f6bc40f0173c048bd52474 Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年3月17日 15:51:26 +0800 Subject: [PATCH 003/158] missing me --- tumblr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tumblr.py b/tumblr.py index 04c9cbd..54579ba 100755 --- a/tumblr.py +++ b/tumblr.py @@ -183,7 +183,7 @@ def run(i): col = s % (1, num + 90, i['filepath']) print ' ++ download: %s' % col cmd = [ - 'wget', '-c', '-q', '-o', '/dev/null', + 'wget', '-c', '-q', '-O', '%s.tmp' % i['filepath'], '--user-agent', '"%s"' % headers['User-Agent'], '%s' % i['durl'] From a0d341b00fc5b7baa205afeb4fa2e68030cbddc3 Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年3月17日 15:55:27 +0800 Subject: [PATCH 004/158] =?UTF-8?q?=E7=99=BE=E5=BA=A6=E4=BA=91=E7=96=91?= =?UTF-8?q?=E4=BC=BC=E8=A7=A3=E5=B0=81=EF=BC=8C=E7=99=BE=E5=BA=A6=E7=BD=91?= =?UTF-8?q?=E7=9B=98=E5=86=85=E5=85=AB=E7=A7=92=E8=A7=86=E9=A2=91=E9=83=A8?= =?UTF-8?q?=E5=88=86=E6=81=A2=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0fde505..b05b6c5 100644 --- a/README.md +++ b/README.md @@ -570,11 +570,13 @@ magnet 和 torrent 的相互转换 - ~~过滤敏.感.词功能用于净网时期的 baidu, xunlei~~ + 过滤敏.感.词功能用于净网时期的 baidu, xunlei - **8.30日后,无法使用。 见 http://tieba.baidu.com/p/3265467666** + ~~8.30日后,无法使用。 见 http://tieba.baidu.com/p/3265467666~~ - ~~**!! 注意:过滤后生成的torrent在百度网盘只能用一次,如果需要再次使用,则需用 -n 改顶层目录名**~~ + [**百度云疑似解封,百度网盘内八秒视频部分恢复**](http://fuli.ba/baiduyunhuifuguankan.html) + + **!! 注意:过滤后生成的torrent在百度网盘只能用一次,如果需要再次使用,则需用 -n 改顶层目录名** 磁力连接转种子,用的是 From 096545be4035d1704832a38bb310175a140c79a1 Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年3月21日 19:17:11 +0800 Subject: [PATCH 005/158] fix --- 115.py | 6 +++--- music.163.com.py | 21 +++++++++++---------- pan.baidu.com.py | 9 +++++---- tumblr.py | 4 ++-- 4 files changed, 21 insertions(+), 19 deletions(-) diff --git a/115.py b/115.py index b66ca7c..e3bcffd 100755 --- a/115.py +++ b/115.py @@ -441,11 +441,11 @@ def main(argv): account = raw_input(s % (1, 97, ' account: ')) password = getpass(s % (1, 97, 'password: ')) elif len(xxx[1:]) == 1: - account = xxx[0] + account = xxx[1] password = getpass(s % (1, 97, ' password: ')) elif len(xxx[1:]) == 2: - account = xxx[0] - password = xxx[1] + account = xxx[1] + password = xxx[2] else: print s % (1, 91, ' login\n login account\n \ login account password') diff --git a/music.163.com.py b/music.163.com.py index 85592c6..cfc77c5 100755 --- a/music.163.com.py +++ b/music.163.com.py @@ -102,8 +102,6 @@ def __init__(self, url): self.url = url self.song_infos = [] self.dir_ = os.getcwd().decode('utf8') - self.template_wgets = 'wget -c -T 5 -nv -U "%s" -O' \ - % headers['User-Agent'] + ' "%s.tmp" %s' self.playlist_id = '' self.dj_id = '' @@ -371,9 +369,11 @@ def display_infos(self, i): def play(self, amount_songs, n=None): for i in self.song_infos: - durl = i['durl'] + r = ss.get(i['durl'], allow_redirects=False) + real_durl = r.headers['location'] self.display_infos(i) - os.system('mpv --really-quiet --audio-display no %s' % durl) + cmd = 'mpv --really-quiet --audio-display no "%s"' % real_durl + os.system(cmd) timeout = 1 ii, _, _ = select.select([sys.stdin], [], [], timeout) if ii: @@ -405,7 +405,6 @@ def download(self, amount_songs, n=None): if not args.undownload: q = {'h': 'High', 'm': 'Middle', 'l': 'Low'} mp3_quality = q[i['mp3_quality']] - durl = i['durl'] if n == None: print(u'\n ++ 正在下载: #%s/%s# %s\n' \ u' ++ mp3_quality: %s' \ @@ -416,16 +415,18 @@ def download(self, amount_songs, n=None): u' ++ mp3_quality: %s' \ % (n, amount_songs, col, s % (1, 91, mp3_quality))) - wget = self.template_wgets \ - % (file_name_for_wget, durl) - wget = wget.encode('utf8') - status = os.system(wget) + + r = ss.get(i['durl'], allow_redirects=False) + real_durl = r.headers['location'] + cmd = 'wget -c -nv -O "%s.tmp" %s' % (file_name_for_wget, real_durl) + cmd = cmd.encode('utf8') + status = os.system(cmd) if status != 0: # other http-errors, such as 302. wget_exit_status_info = wget_es[status] print('\n\n ----### \x1b[1;91mERROR\x1b[0m ==> \x1b[1;91m%d ' \ '(%s)\x1b[0m ###--- \n\n' \ % (status, wget_exit_status_info)) - print s % (1, 91, ' ===> '), wget + print s % (1, 91, ' ===> '), cmd sys.exit(1) else: os.rename('%s.tmp' % file_name, file_name) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index 6f5fe1f..a0afee2 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -1670,6 +1670,7 @@ def warn(comd, display=True): if not warn('play', display=True): return paths = [i['path'].encode('utf8') for i in infos] self._download_do = self._play_do + args.play = True self.download(paths) elif comd == 'rnr' or comd == 'rnre': if len(pipe) < 3: @@ -1718,7 +1719,7 @@ def warn(comd, display=True): def _ls_display(self, infos, dir_=None): if dir_: - print dir_ + ':' + print (dir_ + ':').encode('utf8') for info in infos: self._find_display(info) print '' @@ -1746,9 +1747,9 @@ def _ls_directory(self, order, desc, path): directorys[y:y] = subdirs y += 1 - if args.type_ == 'du': print 'd', s % ( - 1, 91, sizeof_fmt(sum_size) - ), sum_size, directorys[0] + if args.type_ == 'du': + print 'd', s % ( 1, 91, sizeof_fmt(sum_size)), \ + sum_size, directorys[0] def ls(self, order, desc, paths): for path in paths: diff --git a/tumblr.py b/tumblr.py index 54579ba..7654e25 100755 --- a/tumblr.py +++ b/tumblr.py @@ -39,8 +39,8 @@ "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":"https://api.tumblr.com/console//calls/blog/posts", - "User-Agent":"Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.36 \ - (KHTML, like Gecko) Chrome/32.0.1700.77 Safari/537.36" + "User-Agent":"Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.36 " \ + "(KHTML, like Gecko) Chrome/32.0.1700.77 Safari/537.36" } ss = requests.session() From b5018baf26f41494bf11e13662b7cc57591c0b77 Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年3月26日 13:18:30 +0800 Subject: [PATCH 006/158] disable urllib3's warnings --- 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 a0afee2..443da66 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -2500,11 +2500,7 @@ def _share(self, paths, pwd=None): return ENoError def share(self, paths, pwd): - for path in paths: - path = [path] - self._share(path, pwd) - - + self._share(paths, pwd) class panbaiducom(object): @staticmethod @@ -2795,6 +2791,10 @@ def main(argv): ' u localpath1 localpath2 .. remotepath') sys.exit(1) global px + + # disable urllib3's warnings https://urllib3.readthedocs.org/en/latest/security.html#insecurerequestwarning + requests.packages.urllib3.disable_warnings() + px = panbaiducom_HOME() px.init() px.upload(xxx[:-1], xxx[-1]) From 510ebf200b188ab646d2768d10f07671960c2086 Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年3月31日 08:43:15 +0800 Subject: [PATCH 007/158] add -t ie, ignore error at download --- README.md | 1 + pan.baidu.com.py | 19 +++++++++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index b05b6c5..45c5a02 100644 --- a/README.md +++ b/README.md @@ -349,6 +349,7 @@ -f number, --from_ number 从第几个开始(用于download, play),eg: p /video -f 42 -t ext, --type_ ext 类型参数, 用 "," 分隔 eg: + d -t ie # ignore error, 忽略除Ctrl-C以外的下载错误 p -t m3 # 播放流媒体(m3u8) s -t c # 连续转存 (如果转存出错,再次运行命令 # 可以从出错的地方开始,用于转存大量文件时) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index 443da66..1c6e973 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -3,7 +3,6 @@ import os import sys -from getpass import getpass import requests from requests_toolbelt import MultipartEncoder import urllib @@ -222,6 +221,7 @@ def check_login(self): return True def login(self, username, password): + from getpass import getpass print s % (1, 97, '\n -- login') # error_message: at _check_account_exception from @@ -772,13 +772,20 @@ def _download_do(infos): % (quiet, tlimit, infos['file'], infos['dlink']) status = os.system(cmd) + exit = True + if 'ie' in args.type_.split(','): + if status == 2 and not args.aria2c: + pass + elif status == (7 << 8) and args.aria2c: + pass + else: + exit = False if status != 0: # other http-errors, such as 302. - wget_exit_status_info = wget_es[status] - print('\n\n ---### \x1b[1;91mERROR\x1b[0m ==> '\ - '\x1b[1;91m%d (%s)\x1b[0m ###--- \n\n' \ - % (status, wget_exit_status_info)) + #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) print s % (1, 91, ' ===> '), cmd - sys.exit(1) + if exit: sys.exit(1) else: os.rename('%s.tmp' % infos['file'], infos['file']) From 4c88374f87593eb0a2146a6a88707b3a269c9be0 Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年3月31日 09:11:55 +0800 Subject: [PATCH 008/158] add -pp, play by amount of played --- README.md | 3 ++- xiami.py | 17 +++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 45c5a02..baba1e9 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,8 @@ 参数: - -p, --play play with mpv + -p, --play 按顺序播放 + -pp 按歌曲被播放的次数,从高到低播放 -l, --low 低品质mp3 -d, --undescription 不加入disk的描述 -f num, --from_ num 从第num个开始 diff --git a/xiami.py b/xiami.py index 1fc92c0..ed2b398 100755 --- a/xiami.py +++ b/xiami.py @@ -519,23 +519,30 @@ def get_songs(self, album_id, song_id=None): 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'(.+?) Date: Wed, 1 Apr 2015 13:52:02 +0800 Subject: [PATCH 009/158] fix --- xiami.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/xiami.py b/xiami.py index ed2b398..92a15cd 100755 --- a/xiami.py +++ b/xiami.py @@ -1059,8 +1059,13 @@ def main(argv): g = open(cookie_file, 'w') g.close() - elif comd == 'd' or comd == 'download' \ - or comd == 'p' or comd == 'play': + elif comd == 'd' or comd == 'download': + urls = xxx + x = xiami() + x.init() + x.url_parser(urls) + + elif comd == 'p' or comd == 'play': if not args.play: args.play = 1 urls = xxx x = xiami() From 3557ff016557f1e72371448694ed65774e5da406 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Fri, 3 Apr 2015 08:56:08 +0800 Subject: [PATCH 010/158] fix; change sort to sorted at _sift --- pan.baidu.com.py | 87 +++++++++++++++++++++++------------------------- 1 file changed, 42 insertions(+), 45 deletions(-) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index 1c6e973..f3afb99 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -221,7 +221,6 @@ def check_login(self): return True def login(self, username, password): - from getpass import getpass print s % (1, 97, '\n -- login') # error_message: at _check_account_exception from @@ -340,44 +339,39 @@ def _sift(self, fileslist, **arguments): """ # for shuffle - if 's' in args.type_.split(','): + if 's' in args.type_: random.shuffle(fileslist) return fileslist - def sort(reverse, arg, fileslist=fileslist): - tdict = {fileslist[i][arg] : i for i in xrange(len(fileslist))} - keys = tdict.keys() - keys.sort(reverse=reverse) - indexs = [tdict[i] for i in keys] - fileslist = [fileslist[i] for i in indexs] - return fileslist - # for time - if arguments.get('name'): + elif arguments.get('name'): reverse = None if arguments['name'] == 'reverse': reverse = True elif arguments['name'] == 'no_reverse': reverse = False - fileslist = sort(reverse, 'server_filename') + fileslist = sorted(fileslist, key=lambda k: k['server_filename'], + reverse=reverse) # for size - if arguments.get('size'): + elif arguments.get('size'): reverse = None if arguments['size'] == 'reverse': reverse = True elif arguments['size'] == 'no_reverse': reverse = False - fileslist = sort(reverse, 'size') + fileslist = sorted(fileslist, key=lambda k: k['size'], + reverse=reverse) # for time - if arguments.get('time'): + elif arguments.get('time'): reverse = None if arguments['time'] == 'reverse': reverse = True elif arguments['time'] == 'no_reverse': reverse = False - fileslist = sort(reverse, 'server_mtime') + fileslist = sorted(fileslist, key=lambda k: k['server_mtime'], + reverse=reverse) # for head, tail, include, exclude heads = args.heads @@ -437,7 +431,6 @@ def sort(reverse, arg, fileslist=fileslist): dirs = [i for i in fileslist if i['isdir']] t, tt = [], [] - args.type_ = args.type_ if args.type_ else '' if 'e' in args.type_: for i in dirs: d = i['path'].encode('utf8') @@ -446,16 +439,16 @@ def sort(reverse, arg, fileslist=fileslist): t.append(i) else: tt.append(i) - if 'e' in args.type_.split(','): dirs = t - if 'ne' in args.type_.split(','): dirs = tt + if 'e' in args.type_: dirs = t + if 'ne' in args.type_: dirs = tt files = [i for i in fileslist if not i['isdir']] if arguments.get('desc') == 1: dirs.reverse() files.reverse() - if 'f' in args.type_.split(','): + if 'f' in args.type_: fileslist = files - elif 'd' in args.type_.split(','): + elif 'd' in args.type_: fileslist = dirs else: fileslist = dirs + files @@ -673,7 +666,7 @@ def download(self, paths): and os.path.splitext( i['server_filename'] )[-1].lower() in mediatype] - if 's' in args.type_.split(','): + if 's' in args.type_: j['list'] = self._sift(j['list']) if args.heads or args.tails or args.includes \ @@ -705,7 +698,7 @@ def download(self, paths): 'dir_': os.path.split(t)[0], 'dlink': i['dlink'].encode('utf8'), 'm3u8': self._get_m3u8(i) \ - if 'm3' in args.type_.split(',') else None, + if 'm3' in args.type_ else None, 'name': i['server_filename'].encode('utf8'), 'size': i['size'], 'nn': nn, @@ -724,7 +717,7 @@ def download(self, paths): 'dir_': os.path.split(t)[0], #'dlink': self._get_dlink(meta['info'][0]), 'm3u8': self._get_m3u8(meta['info'][0]) \ - if 'm3' in args.type_.split(',') else None, + if 'm3' in args.type_ else None, 'dlink': meta['info'][0]['dlink'].encode('utf8'), 'name': meta['info'][0]['server_filename'].encode('utf8'), 'size': meta['info'][0]['size'], @@ -773,7 +766,7 @@ def _download_do(infos): status = os.system(cmd) exit = True - if 'ie' in args.type_.split(','): + if 'ie' in args.type_: if status == 2 and not args.aria2c: pass elif status == (7 << 8) and args.aria2c: @@ -1048,7 +1041,7 @@ def _upload_file(self, lpath, rpath): 'remotepaths': set() } - if args.type_ and 'e' in args.type_: + if 'e' in args.type_: path = os.path.join(rpath, os.path.basename(lpath)) meta = self._meta([path]) if meta: @@ -1144,7 +1137,7 @@ def _upload_file(self, lpath, rpath): print s % (1, 92, ' |-- RapidUpload: Success.\n') break else: - if args.type_ and 'r' in args.type_.split(','): # only rapidupload + if 'r' in args.type_: # only rapidupload print s % (1, 91, ' |-- can\'t be RapidUploaded\n') break print s % (1, 93, ' |-- can\'t be RapidUploaded, ' \ @@ -1330,7 +1323,7 @@ def _get_share_infos(self, url, remotepath, infos): def save_share(self, url, remotepath, infos=None): infos = self._get_share_infos(url, remotepath, infos) - if args.type_ == 'c': + if 'c' in args.type_: save_share_datas = {} if os.path.exists(save_share_path): f = open(save_share_path, 'rb') @@ -1361,7 +1354,7 @@ def save_share(self, url, remotepath, infos=None): print s % (1, 91, ' !! Error at save_share, errno:'), result time.sleep(5) - if args.type_ == 'c': + if 'c' in args.type_: save_share_datas[url] = infos self.save_datas(save_share_path, save_share_datas) @@ -1590,10 +1583,10 @@ def _highlight_string_zones(self, string, highlights, highlight_color, \ return t def _find_display(self, info): - if 'f' in args.type_.split(','): + if 'f' in args.type_: if info['isdir']: return - elif 'd' in args.type_.split(','): + elif 'd' in args.type_: if not info['isdir']: return else: @@ -1703,7 +1696,7 @@ def warn(comd, display=True): else: print s % (1, 91, ' !! command is supported by download, play, rnre, rm, mv') - if 'all' in args.type_.split(','): + if 'all' in args.type_: for user in self.accounts: cookie = self.accounts[user]['cookies'] ss.cookies.clear() @@ -1745,7 +1738,7 @@ def _ls_directory(self, order, desc, path): if args.heads or args.tails or args.includes or args.excludes \ or args.type_: tinfos = self._sift(infos) - if args.type_ != 'du': + if 'du' not in args.type_: self._ls_display(tinfos, dir_) else: sum_size += sum([i['size'] for i in tinfos]) @@ -1754,7 +1747,7 @@ def _ls_directory(self, order, desc, path): directorys[y:y] = subdirs y += 1 - if args.type_ == 'du': + if 'du' in args.type_: print 'd', s % ( 1, 91, sizeof_fmt(sum_size)), \ sum_size, directorys[0] @@ -1765,7 +1758,7 @@ def ls(self, order, desc, paths): if meta['info'][0]['isdir']: self._ls_directory(order, desc, path) else: - if args.type_ == 'du': args.view = True + if 'du' in args.type_: args.view = True self._ls_display(meta['info'][0]) else: print s % (1, 91, ' !! path is not existed.\n'), \ @@ -1912,7 +1905,7 @@ def _rnre_do(self, foo, bar, infos): if args.recursive and info['isdir']: continue base_dir, old_filename = os.path.split(info['path']) - if 'bd64' in args.type_.split(','): + if 'bd64' in args.type_: told_filename, ext = os.path.splitext(old_filename) if not told_filename.endswith('.base64'): continue codestr = told_filename[:-7] @@ -1972,9 +1965,9 @@ def _rnre_do(self, foo, bar, infos): print s % (1, 92, ' ++ aborted.') def _rmcre_do(self, type, infos, todir=None): - if 'd' in args.type_.split(','): + if 'd' in args.type_: infos = [i for i in infos if i['isdir']] - if 'f' in args.type_.split(','): + if 'f' in args.type_: infos = [i for i in infos if not i['isdir']] if not infos: return @@ -2093,7 +2086,7 @@ def _get_magnet_info(self, url): return j['magnet_info'], '' def _get_selected_idx(self, infos): - types = args.type_.split(',') + types = args.type_ if not args.type_: return [] #if 'a' in types: return [str(i+1) for i in xrange(len(infos))] if 'a' in types: return [] @@ -2711,8 +2704,12 @@ def main(argv): global VERIFY comd = argv[1] args = p.parse_args(argv[2:]) + if args.type_: + args.type_ = args.type_.split(',') + else: + args.type_ = [] VERIFY = args.VERIFY - if (comd == 'rnr' or comd == 'rnre') and 'bd64' not in args.type_.split(','): + if (comd == 'rnr' or comd == 'rnre') and 'bd64' not in args.type_: if len(argv[2:]) < 3: print s % (1, 91, " !! 参数错误\n rnr foo bar /path/to") sys.exit(1) @@ -2723,6 +2720,7 @@ def main(argv): ####################################################### if comd == 'login' or comd == 'g': + from getpass import getpass x = panbaiducom_HOME() if len(xxx) < 1: @@ -2987,7 +2985,7 @@ def main(argv): elif comd == 'l' or comd == 'ln': x.ls('name', None, xxx) elif comd == 'du': - args.type_ = 'du' + args.type_.append('du') x.ls('name', None, xxx) elif comd == 'll' or comd == 'lnn': x.ls('name', 1, xxx) @@ -3012,8 +3010,7 @@ def main(argv): sys.exit(1) if args.recursive \ - and (not 'f' in args.type_.split(',') \ - and not 'd' in args.type_.split(',')): + and (not 'f' in args.type_ and not 'd' in args.type_): print s % (1, 91, ' !! you don\'t choose "-t f" or "-t d", it will delete all files and directorys matched.') ipt = raw_input(s % (1, 93, ' are your sure? [y/N] ')) if ipt != 'y': @@ -3021,7 +3018,7 @@ def main(argv): sys.exit() if comd == 'rnr' or comd == 'rnre': - if 'bd64' in args.type_.split(','): + if 'bd64' in args.type_: foo, bar = '', '' dirs = xxx else: @@ -3090,7 +3087,7 @@ def main(argv): ' a url1 url2 .. [directory] [-t {m,d,p,a}]') sys.exit(1) - args.type_ = 'm' if not args.type_ else args.type_ # default args.type_ + if not args.type_: args.type_.append('m') # default is mediatype if xxx[-1].startswith('/') and not xxx[0].startswith('/'): remotepath = xxx[-1] if xxx[-1][-1] == '/' else xxx[-1] + '/' From 55323f90eb8c6e6261e76dac59d046ae23122a91 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Tue, 7 Apr 2015 11:50:15 +0800 Subject: [PATCH 011/158] supporting encryption uploading and decryption with shadowsocks --- README.md | 47 +++++++++++- pan.baidu.com.py | 190 ++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 207 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index baba1e9..fd655d1 100644 --- a/README.md +++ b/README.md @@ -42,8 +42,6 @@ xiami.py 是一个虾米音乐的命令行(CLI)客户端。提供登录、下载、播放、收藏的功能。 - **从2014年9月30日起,需要下载或播放高品质音乐的用户,按虾米要求,要绑定taobao账号,并用淘宝账号登陆** - 初次使用需要登录 xm login (原xiami账号) **支持淘宝账户** xm logintaobao @@ -52,6 +50,8 @@ **vip账户**支持高品质音乐的下载和播放。 + **原虾米vip用户如果不能获得高品质音乐,请用关联的淘宝帐号登录。** + 下载的MP3默认添加id3 tags,保存在当前目录下。 cookies保存在 ~/.Xiami.cookies。 @@ -170,6 +170,13 @@ mplayer # 我的linux上mpv播放wmv出错,换用mplayer + # 可选依赖 + shadowsocks # 用于加密上传。 + # 用 python2 的 pip 安装 + + # 除了用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 + 2. 使用说明 pan.baidu.com.py 是一个百度网盘的命令行客户端。 @@ -178,6 +185,8 @@ **支持多帐号登录** + **支持加密上传**, 需要 shadowsocks + 他人分享的网盘连接,只支持单个的下载。 下载工具默认为wget, 可用参数-a num选用aria2 @@ -235,6 +244,9 @@ # d /path/to/download -R 递归下载 *download文件夹* 到当前目录(cwd) # d /path/to/download/ -R 递归下载 *download文件夹中的文件* 到当前目录(cwd) + # 下载并解密 + d /path/to/download -R -t dc -P password [-m aes-256-cfb] + # 文件操作 md 或 mkdir path1 path2 .. 创建文件夹 rn 或 rename path new_path 重命名 @@ -350,6 +362,9 @@ -f number, --from_ number 从第几个开始(用于download, play),eg: p /video -f 42 -t ext, --type_ ext 类型参数, 用 "," 分隔 eg: + d -t dc # 下载并解密,覆盖加密文件(默认) + d -t dc,no # 下载并解密,不覆盖加密文件 + dc -t no # 解密,不覆盖加密文件 d -t ie # ignore error, 忽略除Ctrl-C以外的下载错误 p -t m3 # 播放流媒体(m3u8) s -t c # 连续转存 (如果转存出错,再次运行命令 @@ -360,12 +375,15 @@ l -t e,d # 空文件夹 f -t all # 搜索所有账户 a -t m,d,p,a + u -t ec # encrypt, 加密上传, 默认加前缀 + u -t ec,np # encrypt, 加密上传, 不加前缀 u -t r # 只进行 rapidupload u -t e # 如果云端已经存在则不上传(不比对md5) u -t r,e -t s # shuffle,乱序 -l amount, --limit amount 下载速度限制,eg: -l 100k - -m {o,c}, --uploadmode {o,c} 上传模式: o # 重新上传. c # 连续上传. + -m {o,c}, --mode {o,c} 模式: o # 重新上传. c # 连续上传. + 加密方法: https://github.com/shadowsocks/shadowsocks/wiki/Encryption -R, --recursive 递归, 用于download, play, ls, find, rmre, rnre, rmre, cpre -H HEADS, --head HEADS 匹配开头的字符,eg: -H Head1 Head2 .. -T TAILS, --tail TAILS 匹配结尾的字符,eg: -T Tail1 Tail2 .. @@ -430,6 +448,20 @@ bp d /movie/her.mkv -a 4 bp d url -s [secret] -a 10 + # 下载并解码 + ## 默认加密方法为 aes-256-cfb + bp d /path/to/encrypted_file -t dc -P password # 覆盖加密文件 + bp d /path/to/encrypted_file -t dc,no # 不覆盖加密文件 + ## 设置加密方法 + bp d /path/to/encrypted_file -t dc -P password -m 'rc4-md5' + bp d /path/to/directory -t dc -P password -m 'rc4-md5' + + 解码已下载的加密文件: + + bp dc /local/to/encrypted_file -P password -m 'aes-256-cfb' + bp dc /local/to/encrypted_file -P password + bp dc /local/to/directory -P password + 播放: bp p /movie/her.mkv @@ -493,6 +525,15 @@ bp u ~/Documents ~/Videos ~/Documents /backup -t r,e # 以上两种模式 + 加密上传: + + bp u ~/{p1,p2,p3} -t ec -P password # 默认加密方法 'aes-256-cfb' + bp u ~/{p1,p2,p3} -t ec -P password -m 'rc4-md5' + + # 注意: + # 上传后的文件名会默认加上前缀 encrypted_ + # 不加前缀用 -t ec,np + 转存: bp s url remotepath [-s secret] diff --git a/pan.baidu.com.py b/pan.baidu.com.py index f3afb99..a49c9a3 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -37,6 +37,17 @@ MaxSlicePieces = 1024 ENoError = 0 +CIPHERS = [ + "aes-256-cfb", "aes-128-cfb", "aes-192-cfb", + "aes-256-ofb", "aes-128-ofb", "aes-192-ofb", + "aes-128-ctr", "aes-192-ctr", "aes-256-ctr", + "aes-128-cfb8", "aes-192-cfb8", "aes-256-cfb8", + "aes-128-cfb1", "aes-192-cfb1", "aes-256-cfb1", + "bf-cfb", "camellia-128-cfb", "camellia-192-cfb", + "camellia-256-cfb", "cast5-cfb", "chacha20", + "idea-cfb", "rc2-cfb", "rc4-md5", "salsa20", "seed-cfb" +] + ############################################################ # wget exit status wget_es = { @@ -108,6 +119,16 @@ ss = requests.session() ss.headers.update(headers) +def import_shadowsocks(): + try: + global encrypt + from shadowsocks import encrypt + except ImportError: + print s % (1, 93, ' !! you don\'t install shadowsocks for python2.') + print s % (1, 97, ' install shadowsocks:') + print s % (1, 92, ' pip2 install shadowsocks') + sys.exit(1) + # https://stackoverflow.com/questions/1094841/reusable-library-to-get-human-readable-version-of-file-size def sizeof_fmt(num): for x in ['B','KB','MB','GB']: @@ -135,18 +156,22 @@ class panbaiducom_HOME(object): def __init__(self): self._download_do = self._play_do if args.play else self._download_do self.ondup = 'overwrite' - self.highlights = [] self.accounts = self._check_cookie_file() - for tail in args.tails: - self.highlights.append({'text': tail.decode('utf8', 'ignore'), - 'is_regex': 0}) - for head in args.heads: - self.highlights.append({'text': head.decode('utf8', 'ignore'), - 'is_regex': 0}) - for include in args.includes: - self.highlights.append({'text': include.decode('utf8', 'ignore'), - 'is_regex': 1}) + if any([args.tails, args.heads, args.includes]): + self.highlights = [] + for tail in args.tails: + self.highlights.append({'text': tail.decode('utf8', 'ignore'), + 'is_regex': 0}) + for head in args.heads: + self.highlights.append({'text': head.decode('utf8', 'ignore'), + 'is_regex': 0}) + for include in args.includes: + self.highlights.append({'text': include.decode('utf8', 'ignore'), + 'is_regex': 1}) + + if 'ec' in args.type_ or 'dc' in args.type_ or args.comd == 'dc': + import_shadowsocks() @staticmethod def _check_cookie_file(): @@ -706,6 +731,8 @@ def download(self, paths): } nn += 1 self._download_do(infos) + if 'dc' in args.type_: + self.decrypt([infos['file']]) elif not meta['info'][0]['isdir']: t = os.path.join( @@ -723,6 +750,8 @@ def download(self, paths): 'size': meta['info'][0]['size'], } self._download_do(infos) + if 'dc' in args.type_: + self.decrypt([infos['file']]) else: print s % (1, 91, ' !! path is not existed.\n'), \ @@ -922,10 +951,22 @@ def _upload_one_file(self, lpath, rpath): "app_id": "250528", "ondup": self.ondup, "dir": rpath, - "filename": os.path.basename(lpath), "BDUSS": ss.cookies['BDUSS'], } - files = {'file': ('file', open(lpath, 'rb'), '')} + + if self.toEncrypt: + fl = self._cipherer.encrypt(open(lpath, 'rb').read()) + file = b'__' + bytes(DefaultSliceSize) + b'__' + fl + file = cStringIO.StringIO(file) + if 'np' not in args.type_: + p['filename'] = 'encrypted_' + os.path.basename(lpath) + else: + p['filename'] = os.path.basename(lpath) + else: + file = open(lpath, 'rb') + p['filename'] = os.path.basename(lpath) + + files = {'file': ('file', file, '')} data = MultipartEncoder(files) theaders = headers theaders['Content-Type'] = data.content_type @@ -943,9 +984,14 @@ def _combine_file(self, lpath, rpath): "method": "createsuperfile", "app_id": "250528", "ondup": self.ondup, - "path": os.path.join(rpath, os.path.basename(lpath)), "BDUSS": ss.cookies['BDUSS'], } + + if self.toEncrypt and 'np' not in args.type_: + p['path'] = 'encrypted_' + os.path.join(rpath, os.path.basename(lpath)), + else: + p['path'] = os.path.join(rpath, os.path.basename(lpath)), + data = { 'param': json.dumps( {'block_list': self.upload_datas[lpath]['slice_md5s']} @@ -958,7 +1004,7 @@ def _combine_file(self, lpath, rpath): else: return r.json() - def _upload_slice(self): + def _upload_slice(self, piece=0, slice=DefaultSliceSize): p = { "method": "upload", "app_id": "250528", @@ -966,7 +1012,14 @@ def _upload_slice(self): "BDUSS": ss.cookies['BDUSS'], } - file = cStringIO.StringIO(self.__slice_block) + if self.toEncrypt: + __slice_block = self._cipherer.encrypt(self.__slice_block) + if piece == 0: + __slice_block = b'__' + bytes(slice) + b'__' + __slice_block + else: + __slice_block = self.__slice_block + + file = cStringIO.StringIO(__slice_block) files = {'file': ('file', file, '')} data = MultipartEncoder(files) theaders = headers @@ -1001,9 +1054,9 @@ def _get_pieces_slice(self): return pieces, slice - def _get_upload_function(self, rapidupload_is_fall=False): + def _get_upload_function(self, rapidupload_is_fail=False): if self.__current_file_size> MinRapidUploadFileSize: - if not rapidupload_is_fall: + if not rapidupload_is_fail and not self.toEncrypt: return '_rapidupload_file' else: if self.__current_file_size <= DefaultSliceSize: @@ -1041,6 +1094,12 @@ def _upload_file(self, lpath, rpath): 'remotepaths': set() } + if self.toEncrypt: + self._init_cipherer() + self.upload_datas[lpath]['is_over'] = False + self.upload_datas[lpath]['remotepaths'] = set() + self.upload_datas[lpath]['slice_md5s'] = [] + if 'e' in args.type_: path = os.path.join(rpath, os.path.basename(lpath)) meta = self._meta([path]) @@ -1054,7 +1113,7 @@ def _upload_file(self, lpath, rpath): self.upload_datas[lpath]['is_over'] = False pass - if args.uploadmode == 'o': + if args.mode == 'o': self.upload_datas[lpath]['is_over'] = False self.upload_datas[lpath]['slice_md5s'] = [] @@ -1062,7 +1121,7 @@ def _upload_file(self, lpath, rpath): if not self.upload_datas[lpath]['is_over']: m = self.upload_datas[lpath]['upload_function'] if m == '_upload_file_slices': - time.sleep(2) + #time.sleep(2) print ' |-- upload_function:', s % (1, 97, '_upload_file_slices') pieces, slice = self._get_pieces_slice() f = open(lpath, 'rb') @@ -1079,7 +1138,7 @@ def _upload_file(self, lpath, rpath): if self.__slice_block: self.__slice_md5 = md5.new(self.__slice_block).hexdigest() while True: - result = self._upload_slice() + result = self._upload_slice(piece=piece, slice=slice) if result == ENoError: break else: @@ -1114,7 +1173,7 @@ def _upload_file(self, lpath, rpath): #sys.exit(1) elif m == '_upload_one_file': - time.sleep(2) + #time.sleep(2) result = self._upload_one_file(lpath, rpath) if result == ENoError: self.upload_datas[lpath]['is_over'] = True @@ -1128,7 +1187,7 @@ def _upload_file(self, lpath, rpath): #sys.exit(1) elif m == '_rapidupload_file': - time.sleep(2) + #time.sleep(2) result = self._rapidupload_file(lpath, rpath) if result == ENoError: self.upload_datas[lpath]['is_over'] = True @@ -1142,7 +1201,7 @@ def _upload_file(self, lpath, rpath): break print s % (1, 93, ' |-- can\'t be RapidUploaded, ' \ 'now trying normal uploading.') - upload_function = self._get_upload_function(rapidupload_is_fall=True) + upload_function = self._get_upload_function(rapidupload_is_fail=True) self.upload_datas[lpath]['upload_function'] = upload_function if upload_function == '_upload_file_slices': if not self.upload_datas[lpath].has_key('slice_md5s'): @@ -1153,13 +1212,13 @@ def _upload_file(self, lpath, rpath): break else: - if args.uploadmode == 'c': + if args.mode == 'c': if rpath in self.upload_datas[lpath]['remotepaths']: print s % (1, 92, ' |-- file was uploaded.\n') break else: self.upload_datas[lpath]['is_over'] = False - elif args.uploadmode == 'o': + elif args.mode == 'o': print s % (1, 93, ' |-- reupload.') self.upload_datas[lpath]['is_over'] = False @@ -1173,6 +1232,17 @@ def _upload_dir(self, lpath, rpath): remotepath = os.path.join(rpath, t) self._upload_file(localpath, remotepath) + def _init_cipherer(self): + method = args.mode + if method not in CIPHERS: + method = 'aes-256-cfb' + pwd = args.passwd + if not pwd: + print s % (1, 91, ' !! missing Password.\n'), ' -P password' + sys.exit(1) + + self._cipherer = encrypt.Encryptor(pwd, method) + def upload(self, localpaths, remotepath): self.upload_datas = {} if os.path.exists(upload_datas_path): @@ -1181,6 +1251,12 @@ def upload(self, localpaths, remotepath): if upload_datas: self.upload_datas = upload_datas + # initiate Crypter + if 'ec' in args.type_: + self.toEncrypt = True + else: + self.toEncrypt = False + for localpath in localpaths: lpath = localpath if localpath[0] == '~': @@ -2502,6 +2578,62 @@ def _share(self, paths, pwd=None): def share(self, paths, pwd): self._share(paths, pwd) + def decrypt(self, paths): + def get_abspath(pt): + if '~' == pt[0]: + path = os.path.expanduser(pt) + else: + path = os.path.abspath(pt) + if os.path.exists(path): + return path + else: + print s % (1, 91, ' !! path isn\'t existed.'), pt + return None + + def store(path, decrypted_block): + with open(path, 'ab') as g: + g.write(decrypted_block) + + def do(file): + self._init_cipherer() + encrypted_file = open(file, 'rb') + block = encrypted_file.read(100) + encrypted_file.seek(0) + head = re.search(r'^__\d+__', block) + if not head: + print s % (1, 91, ' |-- file isn\'t encrypted.'), file + return + head = head.group() + head_len = len(head) + slice_size = int(re.search(r'__(\d+)__', head).group(1)) + piece = 0 + while True: + if piece == 0: + block = encrypted_file.read(head_len + slice_size) + block = block[head_len:] + piece = 1 + else: + block = encrypted_file.read(slice_size) + if block: + decrypted_block = self._cipherer.decrypt(block) + store(file + '.decrypt', decrypted_block) + else: + break + + if 'no' not in args.type_: # no overwrite + os.rename(file + '.decrypt', file) + + for pt in paths: + path = get_abspath(pt) + if not path: continue + + if os.path.isdir(path): + for parent, dir_, files in os.walk(path): + for file in files: + do(os.path.join(parent, file)) + elif os.path.isfile(path): + do(path) + class panbaiducom(object): @staticmethod def get_web_fileinfo(cm, url): @@ -2685,8 +2817,8 @@ def main(argv): p.add_argument('-P', '--passwd', action='store', \ default=None, type=str, help='设置分享密码,eg: -P pawd') # for upload - p.add_argument('-m', '--uploadmode', action='store', \ - default='c', type=str, choices=['o', 'c'], \ + p.add_argument('-m', '--mode', action='/index.cgi/contrast/https://github.com/tenfar/iScript/compare/store', \ + default='c', type=str, choices=['o', 'c'] + CIPHERS, \ help='上传模式: o --> 重传. c --> 续传 .') # for recurse, head, tail, include, exclude p.add_argument('-R', '--recursive', action='store_true', help='递归 ls') @@ -3143,6 +3275,10 @@ def main(argv): elif comd == 'jca' or comd == 'jobclearall': x.jobclearall() + elif comd == 'dc' or comd == 'decrypt': + x = panbaiducom_HOME() + x.decrypt(xxx) + else: print s % (2, 91, ' !! 命令错误\n') From 3d84ad7ed46955dacb5c6891bfa321ee53feb2b3 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Tue, 7 Apr 2015 21:10:33 +0800 Subject: [PATCH 012/158] support all function of cd, ls --- README.md | 155 +++++++++++++++++------------ pan.baidu.com.py | 254 ++++++++++++++++++++++++++++++++--------------- 2 files changed, 266 insertions(+), 143 deletions(-) diff --git a/README.md b/README.md index fd655d1..fdb4030 100644 --- a/README.md +++ b/README.md @@ -187,6 +187,10 @@ **支持加密上传**, 需要 shadowsocks + **cd, ls 功能完全支持** + + **所有路径可以是 相对路径 或 绝对路径** + 他人分享的网盘连接,只支持单个的下载。 下载工具默认为wget, 可用参数-a num选用aria2 @@ -216,7 +220,8 @@ 命令: - **!!注意:命令参数中,所有网盘的路径必须是 绝对路径** + **!!注意:** + **命令参数中,所有网盘的路径和本地路径可以是 相对路径 或 绝对路径** # 登录 g @@ -233,33 +238,44 @@ # 帐号信息 user - p 或 play url1 url2 .. path1 path2 .. 播放 - u 或 upload localpath remotepath 上传 - s 或 save url remotepath [-s secret] 转存 + # 显示当前工作目录 + cwd + + # 切换当前工作目录 + cd path # 支持 ./../... + + # 播放 + p 或 play url1 url2 .. path1 path2 .. + + # 上传 + u 或 upload localpath remotepath + + # 转存 + s 或 save url remotepath [-s secret] # 下载 - d 或 download url1 url2 .. path1 path2 .. 非递归下载 到当前目录(cwd) - d 或 download url1 url2 .. path1 path2 .. -R 递归下载 到当前目录(cwd) + d 或 download url1 url2 path1 path2 非递归下载 到当前目录(cwd) + d 或 download url1 url2 path1 path2 -R 递归下载 到当前目录(cwd) # !! 注意: - # d /path/to/download -R 递归下载 *download文件夹* 到当前目录(cwd) - # d /path/to/download/ -R 递归下载 *download文件夹中的文件* 到当前目录(cwd) + # d /path/to/download -R 递归下载 *download文件夹* 到当前目录(cwd) + # d /path/to/download/ -R 递归下载 *download文件夹中的文件* 到当前目录(cwd) # 下载并解密 d /path/to/download -R -t dc -P password [-m aes-256-cfb] # 文件操作 - md 或 mkdir path1 path2 .. 创建文件夹 - rn 或 rename path new_path 重命名 - rm 或 remove path1 path2 .. 删除 - mv 或 move path1 path2 .. /path/to/directory 移动 - cp 或 copy path /path/to/directory_or_file 复制 - cp 或 copy path1 path2 .. /path/to/directory 复制 + md 或 mkdir path1 path2 创建文件夹 + rn 或 rename path new_path 重命名 + rm 或 remove path1 path2 删除 + mv 或 move path1 path2 /path/to/directory 移动 + cp 或 copy path /path/to/directory_or_file 复制 + cp 或 copy path1 path2 /path/to/directory 复制 # 使用正则表达式进行文件操作 - rnr 或 rnre foo bar dir1 dir2 .. -I re1 re2 .. 重命名文件夹中的文件名 - rmr 或 rmre dir1 dir2 .. -E re1 re2 .. 删除文件夹下匹配到的文件 - mvr 或 mvre dir1 dir2 .. /path/to/dir -H head1 head2 .. 移动文件夹下匹配到的文件 - cpr 或 cpre dir1 dir2 .. /path/to/dir -T tail1 tail2 .. 复制文件夹下匹配到的文件 + rnr 或 rnre foo bar dir1 dir2 -I re1 re2 重命名文件夹中的文件名 + rmr 或 rmre dir1 dir2 -E re1 re2 删除文件夹下匹配到的文件 + mvr 或 mvre dir1 dir2 /path/to/dir -H head1 head2 移动文件夹下匹配到的文件 + cpr 或 cpre dir1 dir2 /path/to/dir -T tail1 tail2 复制文件夹下匹配到的文件 # 递归加 -R # rmr, mvr, cpr 中 -t, -I, -E, -H, -T 至少要有一个,放在命令行末尾 # -I, -E, -H, -T 后可跟多个匹配式 @@ -271,53 +287,53 @@ rmr / -I '.*' -y # !! 删除网盘中的所有文件 # 回复用bt.py做base64加密的文件 - rnr /path/to/decode1 /path/to/decode2 .. -t f,bd64 + rnr /path/to/decode1 /path/to/decode2 -t f,bd64 # 搜索 - f 或 find keyword1 keyword2 .. [directory] 非递归搜索 - ff keyword1 keyword2 .. [directory] 非递归搜索 反序 - ft keyword1 keyword2 .. [directory] 非递归搜索 by time - ftt keyword1 keyword2 .. [directory] 非递归搜索 by time 反序 - fs keyword1 keyword2 .. [directory] 非递归搜索 by size - fss keyword1 keyword2 .. [directory] 非递归搜索 by size 反序 - fn keyword1 keyword2 .. [directory] 非递归搜索 by name - fnn keyword1 keyword2 .. [directory] 非递归搜索 by name 反序 + f 或 find keyword1 keyword2 [directory] 非递归搜索 + ff keyword1 keyword2 [directory] 非递归搜索 反序 + ft keyword1 keyword2 [directory] 非递归搜索 by time + ftt keyword1 keyword2 [directory] 非递归搜索 by time 反序 + fs keyword1 keyword2 [directory] 非递归搜索 by size + fss keyword1 keyword2 [directory] 非递归搜索 by size 反序 + fn keyword1 keyword2 [directory] 非递归搜索 by name + fnn keyword1 keyword2 [directory] 非递归搜索 by name 反序 # 递归搜索加 -R f 'ice and fire' /doc -R # 搜索所有的账户加 -t all - f keyword1 keyword2 .. [directory] -t all -R - f keyword1 keyword2 .. [directory] -t f,all -R + f keyword1 keyword2 [directory] -t all -R + f keyword1 keyword2 [directory] -t f,all -R # directory 默认为 / # 关于-H, -T, -I, -E # -I, -E, -H, -T 后可跟多个匹配式, 需要放在命令行末尾 - f keyword1 keyword2 ... [directory] -H head -T tail -I "re(gul.*) ex(p|g)ress$" - f keyword1 keyword2 ... [directory] -H head -T tail -E "re(gul.*) ex(p|g)ress$" + f keyword1 keyword2 [directory] -H head -T tail -I "re(gul.*) ex(p|g)ress$" + f keyword1 keyword2 [directory] -H head -T tail -E "re(gul.*) ex(p|g)ress$" # 搜索 加 通道(只支持 donwload, play, rnre, rm, mv) - f keyword1 keyword2 .. [directory] \| d -R 递归搜索后递归下载 - ftt keyword1 keyword2 .. [directory] \| p -R 递归搜索(by time 反序)后递归播放 - f keyword1 keyword2 .. [directory] \| rnr foo bar -R 递归搜索后rename by regex - f keyword1 keyword2 .. [directory] \| rm -R -T tail 递归搜索后删除 - f keyword1 keyword2 .. [directory] \| mv /path/to -R 递归搜索后移动 + f keyword1 keyword2 [directory] \| d -R 递归搜索后递归下载 + ftt keyword1 keyword2 [directory] \| p -R 递归搜索(by time 反序)后递归播放 + f keyword1 keyword2 [directory] \| rnr foo bar -R 递归搜索后rename by regex + f keyword1 keyword2 [directory] \| rm -R -T tail 递归搜索后删除 + f keyword1 keyword2 [directory] \| mv /path/to -R 递归搜索后移动 # 列出文件 - l path1 path2 .. ls by name - ll path1 path2 .. ls by name 反序 - ln path1 path2 .. ls by name - lnn path1 path2 .. ls by name 反序 - lt path1 path2 .. ls by time - ltt path1 path2 .. ls by time 反序 - ls path1 path2 .. ls by size - lss path1 path2 .. ls by size 反序 + l path1 path2 ls by name + ll path1 path2 ls by name 反序 + ln path1 path2 ls by name + lnn path1 path2 ls by name 反序 + lt path1 path2 ls by time + ltt path1 path2 ls by time 反序 + ls path1 path2 ls by size + lss path1 path2 ls by size 反序 l /doc/books /videos # 以下是只列出文件或文件夹 - l path1 path2 .. -t f ls files - l path1 path2 .. -t d ls directorys + l path1 path2 -t f ls files + l path1 path2 -t d ls directorys # 关于-H, -T, -I, -E # -I, -E, -H, -T 后可跟多个匹配式, 需要放在命令行末尾 - l path1 path2 .. -H head -T tail -I "^re(gul.*) ex(p|g)ress$" - l path1 path2 .. -H head -T tail -E "^re(gul.*) ex(p|g)ress$" + l path1 path2 -H head -T tail -I "^re(gul.*) ex(p|g)ress$" + l path1 path2 -H head -T tail -E "^re(gul.*) ex(p|g)ress$" # 显示文件size, md5 - l path1 path2 .. -v + l path1 path2 -v # 空文件夹 l path1 path2 -t e,d # 非空文件夹 @@ -325,25 +341,25 @@ # 分享文件 S 或 share path1 path2 .. 为每个提供的文件路劲创建分享链接 - S 或 share [-P pawd 或 --passwd pawd] path1 path2 .. 为每个提供的路径创建加密的分享链接 + S 或 share [-P pawd 或 --passwd pawd] path1 path2 为每个提供的路径创建加密的分享链接 # 查看文件占用空间 - du path1 path2 .. 文件夹下所有*文件(不包含下层文件夹)*总大小 - du path1 path2 .. -R 文件夹下所有*文件(包含下层文件夹)*总大小 - 如果下层文件多,会花一些时间 - # 相当于 l path1 path2 .. -t du [-R] + du path1 path2 文件夹下所有*文件(不包含下层文件夹)*总大小 + du path1 path2 -R 文件夹下所有*文件(包含下层文件夹)*总大小 + 如果下层文件多,会花一些时间 + # 相当于 l path1 path2 -t du [-R] # eg: du /doc /videos -R # 离线下载 - a 或 add http https ftp ed2k .. remotepath - a 或 add magnet .. remotepath [-t {m,i,d,p}] - a 或 add remote_torrent .. [-t {m,i,d,p}] # 使用网盘中torrent + a 或 add http https ftp ed2k remotepath + a 或 add magnet remotepath [-t {m,i,d,p}] + a 或 add remote_torrent [-t {m,i,d,p}] # 使用网盘中torrent # 离线任务操作 j 或 job # 列出离线下载任务 jd 或 jobdump # 清除全部 *非正在下载中的任务* - jc 或 jobclear taskid1 taskid2 .. # 清除 *正在下载中的任务* + jc 或 jobclear taskid1 taskid2 # 清除 *正在下载中的任务* jca 或 jobclearall # 清除 *全部任务* 参数: @@ -419,8 +435,23 @@ bp user + 显示当前工作目录 + + bp cwd + + 切换当前工作目录 + + bp cd # 切换到 / + bp cd path # 支持 ./../... + bp cd .. + bp cd ../../Music + bp cd ... + 下载: + # 下载当前工作目录 (递归) + bp d . -R + # 下载自己网盘中的*单个或多个文件* bp d http://pan.baidu.com/disk/home#dir/path=/path/to/filename1 http://pan.baidu.com/disk/home#dir/path=/path/to/filename2 .. # or @@ -434,7 +465,7 @@ bp d -R /path/to/directory1 /path/to/directory2 .. -T .mp3 # 非递归下载 - bp d /path/to/directory1 /path/to/directory2 .. + bp d relative_path/to/directory1 /path/to/directory2 .. # 下载别人分享的*单个文件* bp d http://pan.baidu.com/s/1o6psfnxx .. @@ -466,7 +497,9 @@ bp p /movie/her.mkv bp p http://pan.baidu.com/s/xxxxxxxxx -s [secret] - bp p /movie -R # 递归播放 /movie 中所有媒体文件 + + bp cd /movie + bp p movie -R # 递归播放 /movie 中所有媒体文件 # 播放流媒体(m3u8) 上面的命令后加 -t m3 @@ -516,7 +549,7 @@ # -m o --> 重传 # -m c --> 续传 (默认) - bp u ~/Videos/*.mkv /videos -t r + bp u ~/Videos/*.mkv ../videos -t r # 只进行rapidupload bp u ~/Documents ~/Videos ~/Documents /backup -t e diff --git a/pan.baidu.com.py b/pan.baidu.com.py index a49c9a3..b2db4f5 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -4,6 +4,7 @@ import os import sys 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 import urllib import json @@ -129,6 +130,40 @@ def import_shadowsocks(): print s % (1, 92, ' pip2 install shadowsocks') sys.exit(1) +def get_abspath(pt): + if '~' == pt[0]: + path = os.path.expanduser(pt) + else: + path = os.path.abspath(pt) + if os.path.exists(path): + return path + else: + print s % (1, 91, ' !! path isn\'t existed.'), pt + return None + +def make_server_path(cwd, path): + def cd(cwd, part): + if part == '..': + cwd = os.path.dirname(cwd) + elif part == '.': + pass + elif part == '': + pass + elif part == '...': + cwd = os.path.dirname(cwd) + cwd = os.path.dirname(cwd) + else: + cwd = os.path.join(cwd, part) + return cwd + + if path[0] == '/': + return path + else: + parts = path.split('/') + for p in parts: + cwd = cd(cwd, p) + return cwd + # https://stackoverflow.com/questions/1094841/reusable-library-to-get-human-readable-version-of-file-size def sizeof_fmt(num): for x in ['B','KB','MB','GB']: @@ -158,8 +193,8 @@ def __init__(self): self.ondup = 'overwrite' self.accounts = self._check_cookie_file() + self.highlights = [] if any([args.tails, args.heads, args.includes]): - self.highlights = [] for tail in args.tails: self.highlights.append({'text': tail.decode('utf8', 'ignore'), 'is_regex': 0}) @@ -209,7 +244,10 @@ def init(self): j = self.accounts u = [u for u in j if j[u]['on']] if u: - ss.cookies.update(j[u[0]]['cookies']) + user = u[0] + self.user = user + self.cwd = j[user]['cwd'] if j[user].get('cwd') else '/' + ss.cookies.update(j[user]['cookies']) else: print s % (1, 91, ' !! no account is online, please login or userchange') sys.exit(1) @@ -217,7 +255,6 @@ def init(self): if not self.check_login(): print s % (1, 91, ' !! cookie is invalid, please login\n'), u[0] sys.exit(1) - self.save_cookies(u[0], on=1) else: print s % (1, 97, ' no account, please login') sys.exit(1) @@ -334,8 +371,8 @@ def login(self, username, password): login_error_msg[errno] sys.exit(1) - def save_cookies(self, username, on=0): - self.username = username + def save_cookies(self, username=None, on=0): + if not username: username = self.user accounts = self.accounts accounts[username] = {} accounts[username]['cookies'] = ss.cookies.get_dict() @@ -343,6 +380,10 @@ def save_cookies(self, username, on=0): quota = self._get_quota() capacity = '%s/%s' % (sizeof_fmt(quota['used']), sizeof_fmt(quota['total'])) accounts[username]['capacity'] = capacity + if hasattr(self, 'cwd'): + accounts[username]['cwd'] = self.cwd + else: + accounts[username]['cwd'] = '/' for u in accounts: if u != username and on: accounts[u]['on'] = 0 @@ -484,11 +525,11 @@ def _get_path(self, url): t = re.search(r'path=(.+?)(&|$)', url) if t: t = t.group(1) + t = urllib.unquote_plus(t) + t = urllib.unquote_plus(t) + return t else: - t = '/' - t = urllib.unquote_plus(t) - t = urllib.unquote_plus(t) - return t + return url def _get_quota(self): url = 'http://pan.baidu.com/api/quota' @@ -669,6 +710,7 @@ def _get_m3u8(self, info): def download(self, paths): for path in paths: path = self._get_path(path) if path[0] != '/' else path + path = make_server_path(self.cwd, path) base_dir = '' if os.path.split(path)[0] == '/' \ else os.path.split(path)[0] @@ -1244,6 +1286,7 @@ def _init_cipherer(self): self._cipherer = encrypt.Encryptor(pwd, method) def upload(self, localpaths, remotepath): + remotepath = make_server_path(self.cwd, remotepath) self.upload_datas = {} if os.path.exists(upload_datas_path): f = open(upload_datas_path, 'rb') @@ -1258,18 +1301,12 @@ def upload(self, localpaths, remotepath): self.toEncrypt = False for localpath in localpaths: - lpath = localpath - if localpath[0] == '~': - lpath = os.path.expanduser(localpath) - else: - lpath = os.path.abspath(localpath) + lpath = get_abspath(localpath) rpath = remotepath if remotepath[0] == '/' else '/' + remotepath - if os.path.exists(lpath): - pass - else: + if not lpath: print s % (1, 91, ' !! Error: localpath doesn\'t exist') - print s % (1, 91, ' ==>'), lpath + print s % (1, 91, ' ==>'), localpath continue if os.path.isdir(lpath): @@ -1398,6 +1435,7 @@ def _get_share_infos(self, url, remotepath, infos): return infos def save_share(self, url, remotepath, infos=None): + remotepath = make_server_path(self.cwd, remotepath) infos = self._get_share_infos(url, remotepath, infos) if 'c' in args.type_: save_share_datas = {} @@ -1553,6 +1591,7 @@ def _get_share_inbox_infos(self, url, remotepath, infos): def save_inbox_share(self, url, remotepath, infos=None): ss.headers['Referer'] = 'http://pan.baidu.com' + remotepath = make_server_path(self.cwd, remotepath) remotepath = remotepath if remotepath[-1] != '/' else remotepath[:-1] infos = self._get_share_inbox_infos(url, remotepath, infos) for info in infos: @@ -1586,7 +1625,7 @@ def _search(self, keyword, directory): "channel": "chunlei", "clienttype": 0, "web": 1, - "dir": directory if directory else "", + "dir": directory if directory else self.cwd, "key": keyword, #"timeStamp": "0.15937364846467972", #"bdstoken": self._get_bdstoken(), @@ -1665,13 +1704,12 @@ def _find_display(self, info): elif 'd' in args.type_: if not info['isdir']: return - else: - pass + path = info['path'] if args.ls_color == 'on': isdir = s % (1, 93, 'd') if info['isdir'] else s % (1, 97, '-') size = s % (1, 91, sizeof_fmt(info['size']).rjust(8)) - base_dir, filename = os.path.split(info['path']) + base_dir, filename = os.path.split(path) base_dir = s % (2, 95, base_dir.encode('utf8')) \ if base_dir != '/' else '/' @@ -1684,14 +1722,20 @@ def _find_display(self, info): filename, self.highlights, 93 ) - path = os.path.join(base_dir, filename) + if args.view: + path = os.path.join(base_dir, filename) + else: + path = filename elif args.ls_color == 'off': isdir = 'd' if info['isdir'] else '-' size = sizeof_fmt(info['size']).rjust(8) - path = info['path'].encode('utf8') + if args.view: + path = path.encode('utf8') + else: + path = os.path.basename(path).encode('utf8') - if args.view and info.get('md5'): + if args.view == True and info.get('md5'): smd5 = info['md5'].encode('utf8') template = '%s %s %s %s %s' % ( isdir, size, info['size'], smd5, path @@ -1772,6 +1816,7 @@ def warn(comd, display=True): else: print s % (1, 91, ' !! command is supported by download, play, rnre, rm, mv') + args.view = 'find' if 'all' in args.type_: for user in self.accounts: cookie = self.accounts[user]['cookies'] @@ -1828,7 +1873,9 @@ def _ls_directory(self, order, desc, path): sum_size, directorys[0] def ls(self, order, desc, paths): + if not paths: paths = [self.cwd] for path in paths: + path = make_server_path(self.cwd, path) meta = self._meta([path]) if meta: if meta['info'][0]['isdir']: @@ -1868,6 +1915,8 @@ def _filemanager(self, opera, data): print s % (1, 91, ' !! Error at filemanager') def move(self, paths, remotepath): + paths = [ make_server_path(self.cwd, path) for path in paths ] + remotepath = make_server_path(self.cwd, remotepath) self._exist(paths) meta = self._meta([remotepath]) @@ -1889,6 +1938,8 @@ def move(self, paths, remotepath): self._filemanager('move', data) def copy(self, paths, remotepath): + paths = [ make_server_path(self.cwd, path) for path in paths ] + remotepath = make_server_path(self.cwd, remotepath) self._exist(paths) t = None @@ -1937,12 +1988,15 @@ def copy(self, paths, remotepath): self._filemanager('copy', data) def remove(self, paths): + paths = [ make_server_path(self.cwd, path) for path in paths ] self._exist(paths) data = 'filelist=' + urllib.quote_plus(json.dumps(paths)) self._filemanager('delete', data) def rename(self, path, remotepath): + path = make_server_path(self.cwd, path) + remotepath = make_server_path(self.cwd, remotepath) self._exist([path]) meta = self._meta([remotepath]) @@ -2084,6 +2138,7 @@ def _rmcre_do(self, type, infos, todir=None): def filemanager_re(self, type, dirs, todir=None, foo=None, bar=None): tinfos = [] for path in dirs: + path = make_server_path(self.cwd, path) meta = self._meta([path]) if meta: if meta['info'][0]['isdir']: @@ -2110,6 +2165,7 @@ def filemanager_re(self, type, dirs, todir=None, foo=None, bar=None): if type == 'rename': self._rnre_do(foo, bar, tinfos) else: + todir = make_server_path(self.cwd, todir) self._rmcre_do(type, tinfos, todir=todir) ############################################################## @@ -2288,6 +2344,7 @@ def _add_task(self, url, remotepath): return def add_tasks(self, urls, remotepath): + remotepath = make_server_path(self.cwd, remotepath) for url in urls: if url.startswith('magnet:') or url.startswith('/'): if url.startswith('/'): @@ -2504,6 +2561,7 @@ def jobclearall(self): def mkdir(self, paths): for path in paths: + path = make_server_path(self.cwd, path) print s % (1, 97, ' ++ mkdir:'), path meta = self._meta([path]) if not meta: @@ -2576,20 +2634,10 @@ def _share(self, paths, pwd=None): return ENoError def share(self, paths, pwd): + paths = [ make_server_path(self.cwd, path) for path in paths ] self._share(paths, pwd) def decrypt(self, paths): - def get_abspath(pt): - if '~' == pt[0]: - path = os.path.expanduser(pt) - else: - path = os.path.abspath(pt) - if os.path.exists(path): - return path - else: - print s % (1, 91, ' !! path isn\'t existed.'), pt - return None - def store(path, decrypted_block): with open(path, 'ab') as g: g.write(decrypted_block) @@ -2634,6 +2682,24 @@ def do(file): elif os.path.isfile(path): do(path) + def change_directory(self, path): + def cd_do(path): + meta = self._meta([path]) + if meta: + if meta['info'][0]['isdir']: + self.cwd = path + else: + self.cwd = os.path.dirname(path) + else: + print s % (1, 93, ' !! path isn\'t existed.'), path + + if not path: + self.cwd = '/' + else: + path = path[0] + path = make_server_path(self.cwd, path) + cd_do(path) + class panbaiducom(object): @staticmethod def get_web_fileinfo(cm, url): @@ -2773,7 +2839,7 @@ def sighandler(signum, frame): #print s % (1, 91, " !! Frame: %s" % frame) sys.exit(1) -def main(argv): +def handle_signal(): signal.signal(signal.SIGBUS, sighandler) signal.signal(signal.SIGHUP, sighandler) # http://stackoverflow.com/questions/14207708/ioerror-errno-32-broken-pipe-python @@ -2788,12 +2854,7 @@ def main(argv): signal.signal(signal.SIGSEGV, sighandler) signal.signal(signal.SIGTERM, sighandler) - usage = "usage: https://github.com/PeterDing/iScript#pan.baidu.com.py" - if len(argv) <= 1: - print usage - sys.exit() - - ###################################################### +def handle_args(argv): # for argparse p = argparse.ArgumentParser(description='about pan.baidu.com.' \ ' 用法见 https://github.com/PeterDing/iScript') @@ -2849,11 +2910,12 @@ def main(argv): args = p.parse_args(argv[4:]) xxx = args.xxx args.comd = comd - ####################################################### + return comd, xxx +def handle_command(comd, xxx): if comd == 'login' or comd == 'g': from getpass import getpass - x = panbaiducom_HOME() + xh = panbaiducom_HOME() if len(xxx) < 1: username = raw_input(s % (1, 97, ' username: ')) @@ -2867,10 +2929,10 @@ def main(argv): else: print s % (1, 91, ' login\n login username\n login username password') - x.login(username, password) + xh.login(username, password) result = x.check_login() if result: - x.save_cookies(username, on=1) + xh.save_cookies(username, on=1) print s % (1, 92, ' ++ login succeeds.') else: print s % (1, 91, ' login failes') @@ -2929,9 +2991,6 @@ def main(argv): sys.exit(1) global px - # disable urllib3's warnings https://urllib3.readthedocs.org/en/latest/security.html#insecurerequestwarning - requests.packages.urllib3.disable_warnings() - px = panbaiducom_HOME() px.init() px.upload(xxx[:-1], xxx[-1]) @@ -2965,7 +3024,7 @@ def main(argv): elif comd == 'd' or comd == 'download' \ or comd == 'p' or comd == 'play': if len(xxx) < 1: - print s % (1, 91, ' !! 参数错误\n download url1 url2 ..\n' \ + print s % (1, 91, ' !! 参数错误\n download path1 .. url1 ..\n' \ ' d url1 url2 ..') sys.exit(1) @@ -2978,9 +3037,7 @@ def main(argv): paths4 = [] for path in paths: - if path[0] == '/': - paths1.append(path) - elif '/disk/home' in path: + if '/disk/home' in path: paths1.append(path) elif 'baidu.com/pcloud/album/file' in path: paths2.append(path) @@ -2990,7 +3047,7 @@ def main(argv): elif 'pcs.baidu.com' in path: paths4.append(path) else: - print s % (2, 91, ' !!! url 地址不正确.'), path + paths1.append(path) if paths1: x = panbaiducom_HOME() @@ -3087,7 +3144,7 @@ def main(argv): or comd == 'ln' or comd == 'lnn'\ or comd == 'ls' or comd == 'lss' \ or comd == 'lt' or comd == 'ltt': - if len(xxx) < 1: + if len(xxx) < 1 and (comd[0] != 'l'): print s % (1, 91, ' !! 参数错误\n move path1 path2 .. /path/to/directory\n' \ ' mv path1 path2 .. /path/to/directory\n' \ ' remove path1 path2 ..\n' \ @@ -3100,10 +3157,10 @@ def main(argv): ' l path1 path2 ..\n' \ ' ls path1 path2 ..\n') sys.exit(1) - e = True if 'f' in ['f' for i in xxx if i[0] != '/'] else False - if e: - print s % (1, 91, ' !! path is incorrect.') - sys.exit(1) + #e = True if 'f' in ['f' for i in xxx if i[0] != '/'] else False + #if e and (comd[0] != 'l'): + #print s % (1, 91, ' !! path is incorrect.') + #sys.exit(1) x = panbaiducom_HOME() x.init() if comd == 'mv' or comd == 'move': @@ -3158,10 +3215,10 @@ def main(argv): bar = argv[3] dirs = xxx - e = True if 'f' in ['f' for i in dirs if i[0] != '/'] else False - if e: - print s % (1, 91, ' !! path is incorrect.') - sys.exit(1) + #e = True if 'f' in ['f' for i in dirs if i[0] != '/'] else False + #if e: + #print s % (1, 91, ' !! path is incorrect.') + #sys.exit(1) x = panbaiducom_HOME() x.init() @@ -3173,10 +3230,10 @@ def main(argv): sys.exit(1) dirs = xxx - e = True if 'f' in ['f' for i in dirs if i[0] != '/'] else False - if e: - print s % (1, 91, ' !! path is incorrect.') - sys.exit(1) + #e = True if 'f' in ['f' for i in dirs if i[0] != '/'] else False + #if e: + #print s % (1, 91, ' !! path is incorrect.') + #sys.exit(1) x = panbaiducom_HOME() x.init() @@ -3188,10 +3245,10 @@ def main(argv): sys.exit(1) dirs = xxx - e = True if 'f' in ['f' for i in dirs if i[0] != '/'] else False - if e: - print s % (1, 91, ' !! path is incorrect.') - sys.exit(1) + #e = True if 'f' in ['f' for i in dirs if i[0] != '/'] else False + #if e: + #print s % (1, 91, ' !! path is incorrect.') + #sys.exit(1) x = panbaiducom_HOME() x.init() @@ -3203,10 +3260,10 @@ def main(argv): sys.exit(1) dirs = xxx - e = True if 'f' in ['f' for i in dirs if i[0] != '/'] else False - if e: - print s % (1, 91, ' !! path is incorrect.') - sys.exit(1) + #e = True if 'f' in ['f' for i in dirs if i[0] != '/'] else False + #if e: + #print s % (1, 91, ' !! path is incorrect.') + #sys.exit(1) x = panbaiducom_HOME() x.init() @@ -3221,8 +3278,9 @@ def main(argv): if not args.type_: args.type_.append('m') # default is mediatype - if xxx[-1].startswith('/') and not xxx[0].startswith('/'): - remotepath = xxx[-1] if xxx[-1][-1] == '/' else xxx[-1] + '/' + if len(xxx[-1]) < 4 \ + or xxx[-1][:4] not in ['magn', 'http', 'ed2k', 'ftp:']: + remotepath = xxx[-1] urls = xxx[:-1] else: remotepath = '/' @@ -3238,10 +3296,10 @@ def main(argv): ' md path1 path2 ..') sys.exit(1) paths = xxx - e = True if 'f' in ['f' for i in xxx if i[0] != '/'] else False - if e: - print s % (1, 91, ' !! some path is wrong') - sys.exit(1) + #e = True if 'f' in ['f' for i in xxx if i[0] != '/'] else False + #if e: + #print s % (1, 91, ' !! some path is wrong') + #sys.exit(1) x = panbaiducom_HOME() x.init() x.mkdir(paths) @@ -3277,11 +3335,43 @@ def main(argv): elif comd == 'dc' or comd == 'decrypt': x = panbaiducom_HOME() + x.init() x.decrypt(xxx) + elif comd == 'cd': + x = panbaiducom_HOME() + x.init() + + if len(xxx)> 1: + print s % (1, 91, ' !! 参数错误\n cd path') + sys.exit(1) + + x.change_directory(xxx) + + elif comd == 'cwd': + xd = panbaiducom_HOME() + xd.init() + print xd.cwd + else: print s % (2, 91, ' !! 命令错误\n') + if 'x' in locals(): + x.save_cookies(on=1) + elif 'px' in locals(): + px.save_cookies(on=1) + +def main(argv): + handle_signal() + + usage = "usage: https://github.com/PeterDing/iScript#pan.baidu.com.py" + if len(argv) <= 1: + print usage + sys.exit() + + comd, xxx = handle_args(argv) + handle_command(comd, xxx) + if __name__ == '__main__': argv = sys.argv main(argv) From 063875b882570edc4713c64fc0d238a50e0f2a14 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Tue, 7 Apr 2015 21:26:08 +0800 Subject: [PATCH 013/158] change args.view to count --- README.md | 20 ++++++++++---------- pan.baidu.com.py | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index fdb4030..cced462 100644 --- a/README.md +++ b/README.md @@ -371,9 +371,9 @@ -V, --VERIFY verification -v, --view view detail eg: - a magnet /path -v # 离线下载并显示下载的文件 - d -p url1 url2 .. -v # 显示播放文件的完整路径 - l path1 path2 .. -v # 显示文件的size, md5 + a magnet /path -v # 离线下载并显示下载的文件 + d -p url1 url2 -v # 显示播放文件的完整路径 + l path1 path2 -vv # 显示文件的size, md5 -s SECRET, --secret SECRET 提取密码 -f number, --from_ number 从第几个开始(用于download, play),eg: p /video -f 42 -t ext, --type_ ext 类型参数, 用 "," 分隔 @@ -591,13 +591,13 @@ bp f 三体 /doc/fiction -R bp f 晓波 /doc -R - bp ff keyword1 keyword2 .. /path/to/music 非递归搜索 反序 - bp ft keyword1 keyword2 .. /path/to/doc 非递归搜索 by time - bp ftt keyword1 keyword2 .. /path/to/other 非递归搜索 by time 反序 - bp fs keyword1 keyword2 .. 非递归搜索 by size - bp fss keyword1 keyword2 .. 非递归搜索 by size 反序 - bp fn keyword1 keyword2 .. 非递归搜索 by name - bp fnn keyword1 keyword2 .. 非递归搜索 by name 反序 + bp ff keyword1 keyword2 /path/to/music 非递归搜索 反序 + bp ft keyword1 keyword2 /path/to/doc 非递归搜索 by time + bp ftt keyword1 keyword2 /path/to/other 非递归搜索 by time 反序 + bp fs keyword1 keyword2 非递归搜索 by size + bp fss keyword1 keyword2 非递归搜索 by size 反序 + bp fn keyword1 keyword2 非递归搜索 by name + bp fnn keyword1 keyword2 非递归搜索 by name 反序 # 递归搜索加 -R # 关于-H, -T, -I, -E diff --git a/pan.baidu.com.py b/pan.baidu.com.py index b2db4f5..e5210ef 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -1735,7 +1735,7 @@ def _find_display(self, info): else: path = os.path.basename(path).encode('utf8') - if args.view == True and info.get('md5'): + if args.view> 1 and info.get('md5'): smd5 = info['md5'].encode('utf8') template = '%s %s %s %s %s' % ( isdir, size, info['size'], smd5, path @@ -2862,7 +2862,7 @@ def handle_args(argv): p.add_argument('-a', '--aria2c', action='store', default=None, \ type=int, help='aria2c分段下载数量') p.add_argument('-p', '--play', action='store_true', help='play with mpv') - p.add_argument('-v', '--view', action='store_true', help='view details') + p.add_argument('-v', '--view', action='count', help='view details') p.add_argument('-V', '--VERIFY', action='store_true', help='verify') p.add_argument('-y', '--yes', action='store_true', help='yes') p.add_argument('-q', '--quiet', action='store_true', help='quiet for download and play') From 53f56a56f1512a88f6ef3332b1e075240d752a5d Mon Sep 17 00:00:00 2001 From: PeterDing Date: Wed, 8 Apr 2015 07:49:36 +0800 Subject: [PATCH 014/158] fix --- 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 e5210ef..64b4905 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -2930,7 +2930,7 @@ def handle_command(comd, xxx): print s % (1, 91, ' login\n login username\n login username password') xh.login(username, password) - result = x.check_login() + result = xh.check_login() if result: xh.save_cookies(username, on=1) print s % (1, 92, ' ++ login succeeds.') From a860c120d5b347469180c8d8c2c8f7811822e469 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Wed, 8 Apr 2015 09:02:27 +0800 Subject: [PATCH 015/158] fix --- README.md | 5 ++++- pan.baidu.com.py | 14 +++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index cced462..51c7e14 100644 --- a/README.md +++ b/README.md @@ -332,8 +332,10 @@ # -I, -E, -H, -T 后可跟多个匹配式, 需要放在命令行末尾 l path1 path2 -H head -T tail -I "^re(gul.*) ex(p|g)ress$" l path1 path2 -H head -T tail -E "^re(gul.*) ex(p|g)ress$" - # 显示文件size, md5 + # 显示绝对路径 l path1 path2 -v + # 显示文件size, md5 + l path1 path2 -vv # 空文件夹 l path1 path2 -t e,d # 非空文件夹 @@ -385,6 +387,7 @@ p -t m3 # 播放流媒体(m3u8) s -t c # 连续转存 (如果转存出错,再次运行命令 # 可以从出错的地方开始,用于转存大量文件时) + l -v # 显示绝对路径 l -t f # 文件 l -t d # 文件夹 l -t du # 查看文件占用空间 diff --git a/pan.baidu.com.py b/pan.baidu.com.py index 64b4905..5afd2a4 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -1816,7 +1816,7 @@ def warn(comd, display=True): else: print s % (1, 91, ' !! command is supported by download, play, rnre, rm, mv') - args.view = 'find' + if not args.view: args.view = 1 if 'all' in args.type_: for user in self.accounts: cookie = self.accounts[user]['cookies'] @@ -3055,16 +3055,16 @@ def handle_command(comd, xxx): x.download(paths1) if paths2: - x = panbaiducom() - x.do2(paths2) + xw = panbaiducom() + xw.do2(paths2) if paths3: - x = panbaiducom() - x.do(paths3) + xw = panbaiducom() + xw.do(paths3) if paths4: - x = panbaiducom() - x.do4(paths4) + xw = panbaiducom() + xw.do4(paths4) elif comd == 's' or comd == 'save': if len(xxx) != 2: From e72e38bcb3ed7fc86f454166113d0e38cdba457e Mon Sep 17 00:00:00 2001 From: PeterDing Date: Wed, 8 Apr 2015 09:16:06 +0800 Subject: [PATCH 016/158] fix --- pan.baidu.com.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index 5afd2a4..db3d4b0 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -1834,6 +1834,7 @@ def warn(comd, display=True): self.save_cookies(user, on=self.accounts[user]['on']) else: do() + sys.exit() ############################################################## # for ls From 47995ae92d393492ec7797ef077d0e61002a0450 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Wed, 8 Apr 2015 09:56:48 +0800 Subject: [PATCH 017/158] fix --- pan.baidu.com.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index db3d4b0..c29d91d 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -3287,6 +3287,13 @@ def handle_command(comd, xxx): remotepath = '/' urls = xxx + localtorrents = [i for i in xxx \ + if i[:4] not in ['magn', 'http', 'ed2k', 'ftp:'] \ + and i[-8:] == '.torrent'] + if localtorrents: + remotepath = '/' + urls = localtorrents + x = panbaiducom_HOME() x.init() x.add_tasks(urls, remotepath) From 9dadb0256c9fe8464242235e3fcf358f25a7de37 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Wed, 8 Apr 2015 14:30:23 +0800 Subject: [PATCH 018/158] fix --- 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 c29d91d..8664871 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -234,7 +234,7 @@ def correct_do(): if type(j[i]) != type({}): del j[i] else: - if not j[i].get('cookies') and not j[i].get('on'): + if not j[i].get('cookies'): del j[i] return j @@ -371,7 +371,7 @@ def login(self, username, password): login_error_msg[errno] sys.exit(1) - def save_cookies(self, username=None, on=0): + def save_cookies(self, username=None, on=0, tocwd=False): if not username: username = self.user accounts = self.accounts accounts[username] = {} @@ -381,7 +381,9 @@ def save_cookies(self, username=None, on=0): capacity = '%s/%s' % (sizeof_fmt(quota['used']), sizeof_fmt(quota['total'])) accounts[username]['capacity'] = capacity if hasattr(self, 'cwd'): - accounts[username]['cwd'] = self.cwd + if not accounts[username].get('cwd'): + accounts[username]['cwd'] = '/' + if tocwd: accounts[username]['cwd'] = self.cwd else: accounts[username]['cwd'] = '/' for u in accounts: @@ -1831,7 +1833,7 @@ def warn(comd, display=True): print '\n' + user + ':' do() - self.save_cookies(user, on=self.accounts[user]['on']) + #self.save_cookies(user, on=self.accounts[user]['on']) else: do() sys.exit() @@ -3365,9 +3367,9 @@ def handle_command(comd, xxx): print s % (2, 91, ' !! 命令错误\n') if 'x' in locals(): - x.save_cookies(on=1) + x.save_cookies(on=1, tocwd=True) elif 'px' in locals(): - px.save_cookies(on=1) + px.save_cookies(on=1, tocwd=True) def main(argv): handle_signal() From b795420179ac269ac346244436bf6579f2945121 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Wed, 8 Apr 2015 15:51:24 +0800 Subject: [PATCH 019/158] directory must be abspath at [def find] --- README.md | 1 + pan.baidu.com.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 51c7e14..fd09e49 100644 --- a/README.md +++ b/README.md @@ -290,6 +290,7 @@ rnr /path/to/decode1 /path/to/decode2 -t f,bd64 # 搜索 + # directory 必须是绝对路径 f 或 find keyword1 keyword2 [directory] 非递归搜索 ff keyword1 keyword2 [directory] 非递归搜索 反序 ft keyword1 keyword2 [directory] 非递归搜索 by time diff --git a/pan.baidu.com.py b/pan.baidu.com.py index 8664871..ca11a8a 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -1627,11 +1627,12 @@ def _search(self, keyword, directory): "channel": "chunlei", "clienttype": 0, "web": 1, - "dir": directory if directory else self.cwd, "key": keyword, + "dir": directory if directory else "", #"timeStamp": "0.15937364846467972", #"bdstoken": self._get_bdstoken(), } + if args.recursive: p['recursion'] = 1 url = 'http://pan.baidu.com/api/search' r = ss.get(url, params=p) From 543b5db170f146b6f08e223599fc4be3ce2e2420 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Wed, 8 Apr 2015 18:31:39 +0800 Subject: [PATCH 020/158] fix [def job] --- pan.baidu.com.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index ca11a8a..cd3bc19 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -2407,7 +2407,8 @@ def _task_display(self, infos): status_color = 92 if i['status'] == '0' else 91 print template % ( i['id'].encode('utf8'), - status_color, self.jobstatus[i['status'].encode('utf8')], + status_color, + self.jobstatus.get(i['status'].encode('utf8'), '未知'), i['done'], i['name'].encode('utf8'), i['path'].encode('utf8'), @@ -2486,7 +2487,7 @@ def _list_task(self): "clienttype": 0, "channel": "chunlei", "method": "list_task", - "need_task_info": 0, + "need_task_info": 1, "status": 255, "start": 0, "limit": 1000, @@ -2496,7 +2497,7 @@ def _list_task(self): url = 'http://pan.baidu.com/rest/2.0/services/cloud_dl' r = ss.get(url, params=p) j = r.json() - if j.get('errno'): + if j.get('error_code'): print s % (1, 91, ' !! Error at _query_task:'), j sys.exit(1) From a5ad37329dc07ce969d3e5dc4e17aa5dbc610ce4 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Wed, 8 Apr 2015 20:39:27 +0800 Subject: [PATCH 021/158] set the directory to cwd at [def find] --- README.md | 4 ++-- pan.baidu.com.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index fd09e49..2977be9 100644 --- a/README.md +++ b/README.md @@ -199,7 +199,7 @@ 下载默认为非递归,递归下载加 -R - 搜索时,默认在 / + 搜索时,默认在 cwd 搜索支持高亮 @@ -290,7 +290,7 @@ rnr /path/to/decode1 /path/to/decode2 -t f,bd64 # 搜索 - # directory 必须是绝对路径 + # directory 必须是绝对路径, 默认是 cwd f 或 find keyword1 keyword2 [directory] 非递归搜索 ff keyword1 keyword2 [directory] 非递归搜索 反序 ft keyword1 keyword2 [directory] 非递归搜索 by time diff --git a/pan.baidu.com.py b/pan.baidu.com.py index cd3bc19..c7e9cae 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -3116,12 +3116,12 @@ def handle_command(comd, xxx): iii = xxx.index('|') if '|' in xxx else -1 fxxx = xxx[:iii] if iii != -1 else xxx pxxx = xxx[iii+1:] if iii != -1 else None - directory = None if fxxx[-1][0] == '/': keywords = fxxx[:-1] directory = fxxx[-1] else: keywords = fxxx + directory = x.cwd if comd == 'f' or comd == 'find': x.find(keywords, directory=directory, pipe=pxxx) From 8c924cc7a7d79404efcbba2912eb93995bb3e67b Mon Sep 17 00:00:00 2001 From: PeterDing Date: Wed, 8 Apr 2015 22:05:55 +0800 Subject: [PATCH 022/158] missing me --- 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 c7e9cae..6310fda 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -1032,9 +1032,9 @@ def _combine_file(self, lpath, rpath): } if self.toEncrypt and 'np' not in args.type_: - p['path'] = 'encrypted_' + os.path.join(rpath, os.path.basename(lpath)), + p['path'] = os.path.join(rpath, 'encrypted_' + os.path.basename(lpath)) else: - p['path'] = os.path.join(rpath, os.path.basename(lpath)), + p['path'] = os.path.join(rpath, os.path.basename(lpath)) data = { 'param': json.dumps( @@ -1062,9 +1062,10 @@ def _upload_slice(self, piece=0, slice=DefaultSliceSize): __slice_block = b'__' + bytes(slice) + b'__' + __slice_block else: __slice_block = self.__slice_block + self.__slice_md5 = md5.new(__slice_block).hexdigest() - file = cStringIO.StringIO(__slice_block) - files = {'file': ('file', file, '')} + fl = cStringIO.StringIO(__slice_block) + files = {'file': ('file', fl, '')} data = MultipartEncoder(files) theaders = headers theaders['Content-Type'] = data.content_type @@ -1180,7 +1181,6 @@ def _upload_file(self, lpath, rpath): for piece in xrange(current_piece_point, pieces): self.__slice_block = f.read(slice) if self.__slice_block: - self.__slice_md5 = md5.new(self.__slice_block).hexdigest() while True: result = self._upload_slice(piece=piece, slice=slice) if result == ENoError: @@ -1198,6 +1198,7 @@ def _upload_file(self, lpath, rpath): pre=' ', msg='%s/%s' % (str(piece+1), \ str(pieces)) ) + f.close() if result != ENoError: self.upload_datas[lpath]['slice_md5s'] = [] @@ -1208,12 +1209,13 @@ def _upload_file(self, lpath, rpath): if result == ENoError: self.upload_datas[lpath]['is_over'] = True self.upload_datas[lpath]['remotepaths'].update([rpath]) - del self.upload_datas[lpath]['slice_md5s'] + self.upload_datas[lpath]['slice_md5s'] = [] self.save_datas(upload_datas_path, self.upload_datas) print s % (1, 92, '\n |-- success.\n') break else: print s % (1, 91, '\n !! Error at _combine_file:'), result + break #sys.exit(1) elif m == '_upload_one_file': From ab40991b448a25b314d04ddc5e30c3a1bad9746f Mon Sep 17 00:00:00 2001 From: PeterDing Date: Thu, 9 Apr 2015 08:28:07 +0800 Subject: [PATCH 023/158] set uploading mode to be un-recursive by default --- README.md | 161 ++++++++++++++++++++++++----------------------- pan.baidu.com.py | 10 +-- 2 files changed, 87 insertions(+), 84 deletions(-) diff --git a/README.md b/README.md index 2977be9..351be14 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ ### xiami.py - 下载或播放高品质虾米音乐(xiami.com) -1. 依赖 +#### 1. 依赖 wget @@ -38,7 +38,7 @@ mpv (http://mpv.io) -2. 使用说明 +#### 2. 使用说明 xiami.py 是一个虾米音乐的命令行(CLI)客户端。提供登录、下载、播放、收藏的功能。 @@ -60,7 +60,7 @@ > 在运行脚本的终端,输入1次Enter,关闭当前播放并播放下一个文件,连续输入2次Enter,关闭当前播放并退出。 - 命令: + #### 命令: # 虾米账号登录 g @@ -80,7 +80,7 @@ p 或 play url1 url2 .. # 播放 s 或 save url1 url2 .. # 收藏 - 参数: + #### 参数: -p, --play 按顺序播放 -pp 按歌曲被播放的次数,从高到低播放 @@ -90,7 +90,7 @@ -t TAGS, --tags TAGS 收藏用的tags,用英文逗号分开, eg: -t piano,cello,guitar -n, --undownload 不下载,用于修改已存在的MP3的id3 tags -3. 用法 +#### 3. 用法 \# xm 是xiami.py的马甲 (alias xm='python2 /path/to/xiami.py') @@ -125,19 +125,19 @@ xm d http://www.xiami.com/genre/detail/gid/2?spm=a1z1s.3057857.6850221.1.g9ySan xm d http://www.xiami.com/genre/detail/sid/2970?spm=a1z1s.3057857.6850221.4.pkepgt - 播放: + #### 播放: # url 是上面的 xm p url - 收藏: + #### 收藏: xm s http://www.xiami.com/album/168709?spm=a1z1s.6928801.1561534521.114.ShN6mD xm s -t 'tag1,tag 2,tag 3' http://www.xiami.com/song/2082998?spm=a1z1s.6659513.0.0.DT2j7T xm s http://www.xiami.com/song/showcollect/id/30374035?spm=a1z1s.3061701.6856305.16.fvh75t xm s http://www.xiami.com/artist/23460?spm=a1z1s.6928801.1561534521.115.ShW08b -4. 参考: +#### 4. 参考: > http://kanoha.org/2011/08/30/xiami-absolute-address/ @@ -152,7 +152,7 @@ ### pan.baidu.com.py - 百度网盘的下载、离线下载、上传、播放、转存、文件操作 -1. 依赖 +#### 1. 依赖 wget @@ -177,7 +177,7 @@ # 除了用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 -2. 使用说明 +#### 2. 使用说明 pan.baidu.com.py 是一个百度网盘的命令行客户端。 @@ -218,7 +218,7 @@ > 在运行脚本的终端,输入1次Enter,关闭当前播放并播放下一个文件,连续输入2次Enter,关闭当前播放并退出。 - 命令: + #### 命令: **!!注意:** **命令参数中,所有网盘的路径和本地路径可以是 相对路径 或 绝对路径** @@ -365,7 +365,7 @@ jc 或 jobclear taskid1 taskid2 # 清除 *正在下载中的任务* jca 或 jobclearall # 清除 *全部任务* - 参数: + #### 参数: -a num, --aria2c num aria2c分段下载数量: eg: -a 10 -p, --play play with mpv @@ -413,11 +413,11 @@ # -t, -H, -T, -I, -E 都能用于 download, play, ls, find, rnre, rmre, cpre, mvre -3. 用法 +#### 3. 用法 \# bp 是pan.baidu.com.py的马甲 (alias bp='python2 /path/to/pan.baidu.com.py') - 登录: + #### 登录: bp g bp login @@ -427,23 +427,23 @@ # 多帐号登录 # 一直用 bp login 即可 - 删除帐号: + #### 删除帐号: bp ud - 切换帐号: + #### 切换帐号: bp uc - 帐号信息: + #### 帐号信息: bp user - 显示当前工作目录 + #### 显示当前工作目录 bp cwd - 切换当前工作目录 + #### 切换当前工作目录 bp cd # 切换到 / bp cd path # 支持 ./../... @@ -451,7 +451,7 @@ bp cd ../../Music bp cd ... - 下载: + #### 下载: # 下载当前工作目录 (递归) bp d . -R @@ -491,13 +491,13 @@ bp d /path/to/encrypted_file -t dc -P password -m 'rc4-md5' bp d /path/to/directory -t dc -P password -m 'rc4-md5' - 解码已下载的加密文件: + #### 解码已下载的加密文件: bp dc /local/to/encrypted_file -P password -m 'aes-256-cfb' bp dc /local/to/encrypted_file -P password bp dc /local/to/directory -P password - 播放: + #### 播放: bp p /movie/her.mkv bp p http://pan.baidu.com/s/xxxxxxxxx -s [secret] @@ -510,7 +510,7 @@ 清晰度与在浏览器上播放的一样. 如果源文件是高清的(720P,1280P),那么流媒体会自动转为480P. - 离线下载: + #### 离线下载: bp a http://mirrors.kernel.org/archlinux/iso/latest/archlinux-2014年06月01日-dual.iso /path/to/save bp a https://github.com/PeterDing/iScript/archive/master.zip /path/to/save @@ -524,7 +524,7 @@ ↓ 网盘中的torrent - magnet离线下载 -- 文件选择: + #### magnet离线下载 -- 文件选择: -t m # 视频文件 (默认), 如: mkv, avi ..etc -t i # 图像文件, 如: jpg, png ..etc @@ -537,7 +537,7 @@ bp a 'magnet:?xt=urn:btih:64b7700828fd44b37c0c045091939a2c0258ddc2' /path/to/save -v -t p,d bp a /download/a.torrent -v -t m,i,d # 使用网盘中torrent,下载到/download - 离线任务操作: + #### 离线任务操作: bp j bp j 3482938 8302833 @@ -546,32 +546,33 @@ bp jc 1208382 58239221 .. bp jca - 上传: + #### 上传: (默认为非递归,递归加 -R) bp u ~/Documents/reading/三体\ by\ 刘慈欣.mobi /doc -m o # 上传模式: # -m o --> 重传 # -m c --> 续传 (默认) + # 递归加-R bp u ~/Videos/*.mkv ../videos -t r # 只进行rapidupload - bp u ~/Documents ~/Videos ~/Documents /backup -t e + bp u ~/Documents ~/Videos ~/Documents /backup -t e -R # 如果云端已经存在则不上传(不比对md5) # 用 -t e 时, -m o 无效 bp u ~/Documents ~/Videos ~/Documents /backup -t r,e # 以上两种模式 - 加密上传: + #### 加密上传: (默认为非递归,递归加 -R) - bp u ~/{p1,p2,p3} -t ec -P password # 默认加密方法 'aes-256-cfb' + bp u ~/{p1,p2,p3} -t ec -P password # 默认加密方法 'aes-256-cfb' bp u ~/{p1,p2,p3} -t ec -P password -m 'rc4-md5' # 注意: # 上传后的文件名会默认加上前缀 encrypted_ # 不加前缀用 -t ec,np - 转存: + #### 转存: bp s url remotepath [-s secret] # url是他人分享的连接, 如: http://pan.baidu.com/share/link?shareid=xxxxxxx&uk=xxxxxxx, http://pan.baidu.com/s/xxxxxxxx @@ -586,7 +587,7 @@ bp s 'http://pan.baidu.com/share/link?shareid=2705944270&uk=708312363' /path/to/save -t c # 注意:再次运行时,命令要一样。 - 搜索: + #### 搜索: bp f keyword1 keyword2 bp f "this is one keyword" "this is another keyword" /path/to/search @@ -618,7 +619,7 @@ bp f ccav \| rm -R -T avi 递归搜索后删除 bp f 新闻联播(大结局) \| mv /Favor -R 递归搜索后移动 - 恢复用bt.py做base64加密的文件: + #### 恢复用bt.py做base64加密的文件: rnr /ooxx -t f,bd64 !! 注意: /ooxx 中的所有文件都必须是被base64加密的,且加密段要有.base64后缀 @@ -628,7 +629,7 @@ 见[命令](#cmd) -4. 参考: +#### 4. 参考: > https://gist.github.com/HououinRedflag/6191023 @@ -641,12 +642,12 @@ ### bt.py - magnet torrent 互转、及 过滤敏.感.词 -1. 依赖 +#### 1. 依赖 python2-requests (https://github.com/kennethreitz/requests) bencode (https://github.com/bittorrent/bencode) -2. 使用说明 +#### 2. 使用说明 magnet 和 torrent 的相互转换 @@ -680,7 +681,7 @@ > 对于baidu, 加入离线任务后,需等待一段时间才会下载完成。 - 命令: + #### 命令: # magnet 2 torrent m 或 mt magnet_link1 magnet_link2 .. [-d /path/to/save] @@ -705,7 +706,7 @@ cr 或 ctre foo bar magnet_link1 /path/to/torrent1 .. [-d /path/to/save] # foo bar 都是 regex - 参数: + #### 参数: -p PROXY, --proxy PROXY proxy for torrage.com, eg: -p 127.0.0.1:8087 (默认) -t TYPE_, --type_ TYPE_ 类型参数: @@ -715,7 +716,7 @@ -n NAME, --name NAME 顶级文件夹名称, eg: -m thistopdirectory -i localpath1 localpath2 .., --import_from localpath1 localpath2 .. 从本地文本文件导入magnet (用正则表达式匹配) -3. 用法 +#### 3. 用法 \# bt 是bt.py的马甲 (alias bt='python2 /path/to/bt.py') @@ -741,7 +742,7 @@ # 用base64加密的文件名: bt c magnet_link -t be64 -4. 参考: +#### 4. 参考: > http://blog.chinaunix.net/uid-28450123-id-4051635.html @@ -752,7 +753,7 @@ ### 115.py - 115网盘的下载和播放 -1. 依赖 +#### 1. 依赖 wget @@ -764,7 +765,7 @@ mplayer # 我的linux上mpv播放wmv出错,换用mplayer -2. 使用说明 +#### 2. 使用说明 初次使用需要登录 pan115 login @@ -795,7 +796,7 @@ -l amount, --limit amount 下载速度限制,eg: -l 100k -d "url" 增加离线下载 "http/ftp/magnet/ed2k" -3. 用法 +#### 3. 用法 \# pan115 是115.py的马甲 (alias pan115='python2 /path/to/115.py') @@ -820,12 +821,12 @@ # 增加离线下载 pan115 -d "magnet:?xt=urn:btih:757fc565c56462b28b4f9c86b21ac753500eb2a7&dn=archlinux-2014年04月01日-dual.iso" - 播放 + #### 播放 # url 是上面的 pan115 -p url -4. 参考: +#### 4. 参考: > http://passport.115.com/static/wap/js/common.js?v=1.6.39 @@ -834,7 +835,7 @@ ### yunpan.360.cn.py - 360网盘的下载 -1. 依赖 +#### 1. 依赖 wget @@ -843,7 +844,7 @@ python2-requests (https://github.com/kennethreitz/requests) -2. 使用说明 +#### 2. 使用说明 初次使用需要登录 yp login @@ -859,14 +860,14 @@ cookies保存在 ~/.360.cookies - 参数: + #### 参数: -a, --aria2c download with aria2c -f number, --from_ number 从第几个开始下载,eg: -f 42 -t ext, --type_ ext 要下载的文件的后缀,eg: -t mp3 -l amount, --limit amount 下载速度限制,eg: -l 100k -3. 用法 +#### 3. 用法 \# yp 是yunpan.360.cn.py的马甲 (alias yp='python2 /path/to/yunpan.360.cn.py') @@ -889,7 +890,7 @@ # 下载用aria2, url 是上面的 yp -a url -4. 参考: +#### 4. 参考: > https://github.com/Shu-Ji/gorthon/blob/master/_3rdapp/CloudDisk360/main.py @@ -898,7 +899,7 @@ ### music.baidu.com.py - 下载或播放高品质百度音乐(music.baidu.com) -1. 依赖 +#### 1. 依赖 wget @@ -906,11 +907,11 @@ mpv (http://mpv.io) -2. 使用说明 +#### 2. 使用说明 默认执行下载,如要播放,加参数-p。 - 参数: + #### 参数: -f, --flac download flac -i, --high download 320, default @@ -923,7 +924,7 @@ > 在运行脚本的终端,输入1次Enter,关闭当前播放并播放下一个文件,连续输入2次Enter,关闭当前播放并退出。 -3. 用法 +#### 3. 用法 \# bm 是music.baidu.com.py的马甲 (alias bm='python2 /path/to/music.baidu.com.py') @@ -933,12 +934,12 @@ # 下载单曲 bm http://music.baidu.com/song/117948039 - 播放: + #### 播放: # url 是上面的 bm -p url -4. 参考: +#### 4. 参考: > http://v2ex.com/t/77685 # 第9楼 @@ -947,7 +948,7 @@ ### music.163.com.py - 下载或播放高品质网易音乐(music.163.com) -1. 依赖 +#### 1. 依赖 wget @@ -957,7 +958,7 @@ mpv (http://mpv.io) -2. 使用说明 +#### 2. 使用说明 **默认下载和播放高品质音乐,如果服务器没有高品质音乐则转到低品质音乐。** @@ -969,7 +970,7 @@ > 在运行脚本的终端,输入1次Enter,关闭当前播放并播放下一个文件,连续输入2次Enter,关闭当前播放并退出。 -3. 用法 +#### 3. 用法 \# nm 是music.163.com.py的马甲 (alias nm='python2 /path/to/music.163.com.py') @@ -992,12 +993,12 @@ nm http://music.163.com/#/discover/toplist?id=11641012 - 播放: + #### 播放: # url 是上面的 nm -p url -4. 参考: +#### 4. 参考: > 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 @@ -1008,7 +1009,7 @@ ### flvxz_cl.py - flvxz.com 视频解析 client - 支持下载、播放 -1. 依赖 +#### 1. 依赖 wget @@ -1016,13 +1017,13 @@ mpv (http://mpv.io) -2. 使用说明 +#### 2. 使用说明 flvxz.com 视频解析 **不提供视频合并操作** - 支持的网站: + #### 支持的网站: """ 已知支持120个以上视频网站,覆盖大多数国内视频站点,少量国外视频站点 @@ -1033,21 +1034,21 @@ > 在运行脚本的终端,输入1次Enter,关闭当前播放并播放下一个文件,连续输入2次Enter,关闭当前播放并退出。 -3. 用法 +#### 3. 用法 \# fl是flvxz_cl.py的马甲 (alias fl='python2 /path/to/flvxz_cl.py') - 下载: + #### 下载: fl http://v.youku.com/v_show/id_XNTI2Mzg4NjAw.html fl http://www.tudou.com/albumplay/Lqfme5hSolM/tJ_Gl3POz7Y.html - 播放: + #### 播放: # url 是上面的 fl url -p -4. 相关脚本: +#### 4. 相关脚本: > https://github.com/iambus/youku-lixian @@ -1060,13 +1061,13 @@ ### tumblr.py - 下载某个tumblr.com的所有图片 -1. 依赖 +#### 1. 依赖 wget python2-requests (https://github.com/kennethreitz/requests) -2. 使用说明 +#### 2. 使用说明 * 使用前需用在 http://www.tumblr.com/oauth/apps 加入一个app,证实后得到api_key,再在源码中填入,完成后则可使用。 @@ -1080,13 +1081,13 @@ 支持连续下载,下载进度储存在下载文件夹内的 json.json。 - 参数: + #### 参数: -p PROCESSES, --processes PROCESSES 指定多进程数,默认为10个,最多为20个 eg: -p 20 -c, --check 尝试修复未下载成功的图片 -t TAG, --tag TAG 下载特定tag的图片, eg: -t beautiful -3. 用法 +#### 3. 用法 \# tm是tumblr.py的马甲 (alias tm='python2 /path/to/tumblr.py') @@ -1103,7 +1104,7 @@ ### unzip.py - 解决linux下unzip乱码的问题 -用法 +#### 用法 python2 unzip.py azipfile1.zip azipfile2.zip .. python2 unzip.py azipfile.zip -s secret @@ -1118,10 +1119,10 @@ ### ed2k_search.py - 基于 donkey4u.com 的emule搜索 -1. 依赖 +#### 1. 依赖 python2 -2. 用法 +#### 2. 用法 \# ed 是ed2k_search.py的马甲 (alias ed='python2 /path/to/ed2k_search.py') @@ -1136,7 +1137,7 @@ **警告: 18岁以下者,请自觉远离。** -1. 依赖 +#### 1. 依赖 wget @@ -1146,7 +1147,7 @@ mpv (http://mpv.io) -2. 使用说明 +#### 2. 使用说明 > 没有解决 *7个/day* 限制 @@ -1160,15 +1161,15 @@ > 在运行脚本的终端,输入1次Enter,关闭当前播放并播放下一个文件,连续输入2次Enter,关闭当前播放并退出。 -3. 用法 +#### 3. 用法 \# pn 是91porn.py的马甲 (alias pn='python2 /path/to/91porn.py') - 下载: + #### 下载: pn url # 91porn.com(或其镜像) 视频的url - 播放: + #### 播放: pn -p url @@ -1176,7 +1177,7 @@ pn -u url -4. 参考 +#### 4. 参考 > http://v2ex.com/t/110196 # 第16楼 diff --git a/pan.baidu.com.py b/pan.baidu.com.py index 6310fda..1cc49d6 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -1270,13 +1270,14 @@ def _upload_file(self, lpath, rpath): def _upload_dir(self, lpath, rpath): base_dir = os.path.split(lpath)[0] - for a, b, c in os.walk(lpath): - for path in c: - localpath = os.path.join(a, path) + for parent, directories, files in os.walk(lpath): + for file in files: + localpath = os.path.join(parent, file) t = localpath.replace(base_dir + '/', '') t = os.path.split(t)[0] remotepath = os.path.join(rpath, t) self._upload_file(localpath, remotepath) + if not args.recursive: break def _init_cipherer(self): method = args.mode @@ -2683,9 +2684,10 @@ def do(file): if not path: continue if os.path.isdir(path): - for parent, dir_, files in os.walk(path): + for parent, directories, files in os.walk(path): for file in files: do(os.path.join(parent, file)) + if not args.recursive: break elif os.path.isfile(path): do(path) From a21affe42ea25c8f7ab6eaa417d1602e649f2c98 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Thu, 9 Apr 2015 08:54:11 +0800 Subject: [PATCH 024/158] support -t d, -t f for [def upload] --- README.md | 6 ++++++ pan.baidu.com.py | 10 +++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 351be14..e0ab7f0 100644 --- a/README.md +++ b/README.md @@ -548,6 +548,12 @@ #### 上传: (默认为非递归,递归加 -R) + # 支持文件类型选择 + bp u ~/Documents/* # 默认上传所以文件 + bp u ~/Documents/* -t f # 不上传文件夹 + bp u ~/Documents/* -t d # 不上传文件 + bp u ~/Documents/* -t f,d # 不上传文件和文件夹 + bp u ~/Documents/reading/三体\ by\ 刘慈欣.mobi /doc -m o # 上传模式: # -m o --> 重传 diff --git a/pan.baidu.com.py b/pan.baidu.com.py index 1cc49d6..c84697e 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -1292,6 +1292,7 @@ def _init_cipherer(self): def upload(self, localpaths, remotepath): remotepath = make_server_path(self.cwd, remotepath) + rpath = remotepath if remotepath[0] == '/' else '/' + remotepath self.upload_datas = {} if os.path.exists(upload_datas_path): f = open(upload_datas_path, 'rb') @@ -1307,20 +1308,19 @@ def upload(self, localpaths, remotepath): for localpath in localpaths: lpath = get_abspath(localpath) - rpath = remotepath if remotepath[0] == '/' else '/' + remotepath if not lpath: print s % (1, 91, ' !! Error: localpath doesn\'t exist') print s % (1, 91, ' ==>'), localpath continue - if os.path.isdir(lpath): + if os.path.isdir(lpath) and 'f' not in args.type_: self._upload_dir(lpath, rpath) - elif os.path.isfile(lpath): + elif os.path.isfile(lpath) and 'd' not in args.type_: self._upload_file(lpath, rpath) else: - print s % (1, 91, ' !! Error: localpath ?') - sys.exit(1) + #print s % (1, 91, ' !! Error: localpath ?'), localpath + pass self.save_datas(upload_datas_path, self.upload_datas) From 7b9196fe2b19e0e3e7e79daa371ddbe8838391d4 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Thu, 9 Apr 2015 09:39:10 +0800 Subject: [PATCH 025/158] missing me --- README.md | 1591 ++++++++++++++++++++++++++++------------------------- 1 file changed, 853 insertions(+), 738 deletions(-) diff --git a/README.md b/README.md index e0ab7f0..b80577f 100644 --- a/README.md +++ b/README.md @@ -30,112 +30,124 @@ #### 1. 依赖 - wget +``` +wget - python2-requests (https://github.com/kennethreitz/requests) +python2-requests (https://github.com/kennethreitz/requests) - python2-mutagen (https://code.google.com/p/mutagen/) +python2-mutagen (https://code.google.com/p/mutagen/) - mpv (http://mpv.io) +mpv (http://mpv.io) +``` #### 2. 使用说明 - xiami.py 是一个虾米音乐的命令行(CLI)客户端。提供登录、下载、播放、收藏的功能。 +xiami.py 是一个虾米音乐的命令行(CLI)客户端。提供登录、下载、播放、收藏的功能。 - 初次使用需要登录 xm login (原xiami账号) +初次使用需要登录 xm login (原xiami账号) - **支持淘宝账户** xm logintaobao +**支持淘宝账户** xm logintaobao - **对于淘宝账户,登录后只保存有关虾米的cookies,删除了有关淘宝的cookies** +**对于淘宝账户,登录后只保存有关虾米的cookies,删除了有关淘宝的cookies** - **vip账户**支持高品质音乐的下载和播放。 +**vip账户**支持高品质音乐的下载和播放。 - **原虾米vip用户如果不能获得高品质音乐,请用关联的淘宝帐号登录。** +**原虾米vip用户如果不能获得高品质音乐,请用关联的淘宝帐号登录。** - 下载的MP3默认添加id3 tags,保存在当前目录下。 +下载的MP3默认添加id3 tags,保存在当前目录下。 - cookies保存在 ~/.Xiami.cookies。 +cookies保存在 ~/.Xiami.cookies。 - 关于播放操作: +关于播放操作: -> 在运行脚本的终端,输入1次Enter,关闭当前播放并播放下一个文件,连续输入2次Enter,关闭当前播放并退出。 +> 在运行脚本的终端,输入1次Enter,关闭当前播放并播放下一个文件,连续输入2次Enter,关闭当前播放并退出。 - #### 命令: +#### 命令: - # 虾米账号登录 - g - login - login username - login username password +``` +# 虾米账号登录 +g +login +login username +login username password - # 淘宝账号登录 - gt - logintaobao - logintaobao username - logintaobao username password +# 淘宝账号登录 +gt +logintaobao +logintaobao username +logintaobao username password - signout # 退出登录 +signout # 退出登录 - d 或 download url1 url2 .. # 下载 - p 或 play url1 url2 .. # 播放 - s 或 save url1 url2 .. # 收藏 +d 或 download url1 url2 .. # 下载 +p 或 play url1 url2 .. # 播放 +s 或 save url1 url2 .. # 收藏 +``` - #### 参数: +#### 参数: - -p, --play 按顺序播放 - -pp 按歌曲被播放的次数,从高到低播放 - -l, --low 低品质mp3 - -d, --undescription 不加入disk的描述 - -f num, --from_ num 从第num个开始 - -t TAGS, --tags TAGS 收藏用的tags,用英文逗号分开, eg: -t piano,cello,guitar - -n, --undownload 不下载,用于修改已存在的MP3的id3 tags +``` +-p, --play 按顺序播放 +-pp 按歌曲被播放的次数,从高到低播放 +-l, --low 低品质mp3 +-d, --undescription 不加入disk的描述 +-f num, --from_ num 从第num个开始 +-t TAGS, --tags TAGS 收藏用的tags,用英文逗号分开, eg: -t piano,cello,guitar +-n, --undownload 不下载,用于修改已存在的MP3的id3 tags +``` #### 3. 用法 - \# xm 是xiami.py的马甲 (alias xm='python2 /path/to/xiami.py') +xm 是xiami.py的马甲 (alias xm='python2 /path/to/xiami.py') - # 登录 - xm g - xm login - xm login username - xm login username password +``` +# 登录 +xm g +xm login +xm login username +xm login username password - # 退出登录 - xm signout +# 退出登录 +xm signout - # 下载专辑 - xm d http://www.xiami.com/album/168709?spm=a1z1s.6928801.1561534521.114.ShN6mD +# 下载专辑 +xm d http://www.xiami.com/album/168709?spm=a1z1s.6928801.1561534521.114.ShN6mD - # 下载单曲 - xm d http://www.xiami.com/song/2082998?spm=a1z1s.6659513.0.0.DT2j7T +# 下载单曲 +xm d http://www.xiami.com/song/2082998?spm=a1z1s.6659513.0.0.DT2j7T - # 下载精选集 - xm d http://www.xiami.com/song/showcollect/id/30374035?spm=a1z1s.3061701.6856305.16.fvh75t +# 下载精选集 +xm d http://www.xiami.com/song/showcollect/id/30374035?spm=a1z1s.3061701.6856305.16.fvh75t - # 下载该艺术家所有专辑, Top 20 歌曲, radio - xm d http://www.xiami.com/artist/23460?spm=a1z1s.6928801.1561534521.115.ShW08b +# 下载该艺术家所有专辑, Top 20 歌曲, radio +xm d http://www.xiami.com/artist/23460?spm=a1z1s.6928801.1561534521.115.ShW08b - # 下载用户的收藏, 虾米推荐, radio, 推荐 - xm d http://www.xiami.com/u/141825?spm=a1z1s.3521917.0.0.zI0APP +# 下载用户的收藏, 虾米推荐, radio, 推荐 +xm d http://www.xiami.com/u/141825?spm=a1z1s.3521917.0.0.zI0APP - # 下载排行榜 - xm d http://www.xiami.com/chart/index/c/2?spm=a1z1s.2943549.6827465.6.VrEAoY +# 下载排行榜 +xm d http://www.xiami.com/chart/index/c/2?spm=a1z1s.2943549.6827465.6.VrEAoY - # 下载 风格 genre, radio - xm d http://www.xiami.com/genre/detail/gid/2?spm=a1z1s.3057857.6850221.1.g9ySan - xm d http://www.xiami.com/genre/detail/sid/2970?spm=a1z1s.3057857.6850221.4.pkepgt +# 下载 风格 genre, radio +xm d http://www.xiami.com/genre/detail/gid/2?spm=a1z1s.3057857.6850221.1.g9ySan +xm d http://www.xiami.com/genre/detail/sid/2970?spm=a1z1s.3057857.6850221.4.pkepgt +``` - #### 播放: +#### 播放: - # url 是上面的 - xm p url +``` +# url 是上面的 +xm p url +``` - #### 收藏: +#### 收藏: - xm s http://www.xiami.com/album/168709?spm=a1z1s.6928801.1561534521.114.ShN6mD - xm s -t 'tag1,tag 2,tag 3' http://www.xiami.com/song/2082998?spm=a1z1s.6659513.0.0.DT2j7T - xm s http://www.xiami.com/song/showcollect/id/30374035?spm=a1z1s.3061701.6856305.16.fvh75t - xm s http://www.xiami.com/artist/23460?spm=a1z1s.6928801.1561534521.115.ShW08b +``` +xm s http://www.xiami.com/album/168709?spm=a1z1s.6928801.1561534521.114.ShN6mD +xm s -t 'tag1,tag 2,tag 3' http://www.xiami.com/song/2082998?spm=a1z1s.6659513.0.0.DT2j7T +xm s http://www.xiami.com/song/showcollect/id/30374035?spm=a1z1s.3061701.6856305.16.fvh75t +xm s http://www.xiami.com/artist/23460?spm=a1z1s.6928801.1561534521.115.ShW08b +``` #### 4. 参考: @@ -154,486 +166,526 @@ #### 1. 依赖 - wget +``` +wget - aria2 (~ 1.18) +aria2 (~ 1.18) - python2-rsa +python2-rsa - python2-pyasn1 +python2-pyasn1 - python2-requests (https://github.com/kennethreitz/requests) +python2-requests (https://github.com/kennethreitz/requests) - requests-toolbelt (https://github.com/sigmavirus24/requests-toolbelt) +requests-toolbelt (https://github.com/sigmavirus24/requests-toolbelt) - mpv (http://mpv.io) +mpv (http://mpv.io) - mplayer # 我的linux上mpv播放wmv出错,换用mplayer +mplayer # 我的linux上mpv播放wmv出错,换用mplayer - # 可选依赖 - shadowsocks # 用于加密上传。 - # 用 python2 的 pip 安装 +# 可选依赖 +shadowsocks # 用于加密上传。 + # 用 python2 的 pip 安装 - # 除了用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 +# 除了用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 +``` #### 2. 使用说明 - pan.baidu.com.py 是一个百度网盘的命令行客户端。 +pan.baidu.com.py 是一个百度网盘的命令行客户端。 - 初次使用需要登录 bp login +初次使用需要登录 bp login - **支持多帐号登录** +**支持多帐号登录** - **支持加密上传**, 需要 shadowsocks +**支持加密上传**, 需要 shadowsocks - **cd, ls 功能完全支持** +**cd, ls 功能完全支持** - **所有路径可以是 相对路径 或 绝对路径** +**所有路径可以是 相对路径 或 绝对路径** - 他人分享的网盘连接,只支持单个的下载。 +他人分享的网盘连接,只支持单个的下载。 - 下载工具默认为wget, 可用参数-a num选用aria2 +下载工具默认为wget, 可用参数-a num选用aria2 - 下载的文件,保存在当前目录下。 +下载的文件,保存在当前目录下。 - 下载默认为非递归,递归下载加 -R +下载默认为非递归,递归下载加 -R - 搜索时,默认在 cwd +搜索时,默认在 cwd - 搜索支持高亮 +搜索支持高亮 - 上传模式默认是 c (续传)。 +上传模式默认是 c (续传)。 - **开启证实(verification) 用参数 -V** +**开启证实(verification) 用参数 -V** - 理论上,上传的单个文件最大支持 2T +理论上,上传的单个文件最大支持 2T - cookies保存在 ~/.bp.cookies +cookies保存在 ~/.bp.cookies - 上传数据保存在 ~/.bp.pickle +上传数据保存在 ~/.bp.pickle - 关于播放操作: +关于播放操作: -> 在运行脚本的终端,输入1次Enter,关闭当前播放并播放下一个文件,连续输入2次Enter,关闭当前播放并退出。 +> 在运行脚本的终端,输入1次Enter,关闭当前播放并播放下一个文件,连续输入2次Enter,关闭当前播放并退出。 - - #### 命令: + +#### 命令: - **!!注意:** - **命令参数中,所有网盘的路径和本地路径可以是 相对路径 或 绝对路径** +**!!注意:** +**命令参数中,所有网盘的路径和本地路径可以是 相对路径 或 绝对路径** - # 登录 - g - login - login username - login username password - - # 删除帐号 - userdelete 或 ud - - # 切换帐号 - userchange 或 uc - - # 帐号信息 - user - - # 显示当前工作目录 - cwd - - # 切换当前工作目录 - cd path # 支持 ./../... - - # 播放 - p 或 play url1 url2 .. path1 path2 .. - - # 上传 - u 或 upload localpath remotepath - - # 转存 - s 或 save url remotepath [-s secret] - - # 下载 - d 或 download url1 url2 path1 path2 非递归下载 到当前目录(cwd) - d 或 download url1 url2 path1 path2 -R 递归下载 到当前目录(cwd) - # !! 注意: - # d /path/to/download -R 递归下载 *download文件夹* 到当前目录(cwd) - # d /path/to/download/ -R 递归下载 *download文件夹中的文件* 到当前目录(cwd) - - # 下载并解密 - d /path/to/download -R -t dc -P password [-m aes-256-cfb] - - # 文件操作 - md 或 mkdir path1 path2 创建文件夹 - rn 或 rename path new_path 重命名 - rm 或 remove path1 path2 删除 - mv 或 move path1 path2 /path/to/directory 移动 - cp 或 copy path /path/to/directory_or_file 复制 - cp 或 copy path1 path2 /path/to/directory 复制 - - # 使用正则表达式进行文件操作 - rnr 或 rnre foo bar dir1 dir2 -I re1 re2 重命名文件夹中的文件名 - rmr 或 rmre dir1 dir2 -E re1 re2 删除文件夹下匹配到的文件 - mvr 或 mvre dir1 dir2 /path/to/dir -H head1 head2 移动文件夹下匹配到的文件 - cpr 或 cpre dir1 dir2 /path/to/dir -T tail1 tail2 复制文件夹下匹配到的文件 - # 递归加 -R - # rmr, mvr, cpr 中 -t, -I, -E, -H, -T 至少要有一个,放在命令行末尾 - # -I, -E, -H, -T 后可跟多个匹配式 - # 可以用 -t 指定操作的文件类型 - -t f # 文件 - -t d # 文件夹 - # rnr 中 foo bar 都是 regex - # -y, --yes # 不显示警示,直接进行。 !!注意,除非你知道你做什么,否则请不要使用。 - rmr / -I '.*' -y # !! 删除网盘中的所有文件 - - # 回复用bt.py做base64加密的文件 - rnr /path/to/decode1 /path/to/decode2 -t f,bd64 - - # 搜索 - # directory 必须是绝对路径, 默认是 cwd - f 或 find keyword1 keyword2 [directory] 非递归搜索 - ff keyword1 keyword2 [directory] 非递归搜索 反序 - ft keyword1 keyword2 [directory] 非递归搜索 by time - ftt keyword1 keyword2 [directory] 非递归搜索 by time 反序 - fs keyword1 keyword2 [directory] 非递归搜索 by size - fss keyword1 keyword2 [directory] 非递归搜索 by size 反序 - fn keyword1 keyword2 [directory] 非递归搜索 by name - fnn keyword1 keyword2 [directory] 非递归搜索 by name 反序 - # 递归搜索加 -R - f 'ice and fire' /doc -R - # 搜索所有的账户加 -t all - f keyword1 keyword2 [directory] -t all -R - f keyword1 keyword2 [directory] -t f,all -R - # directory 默认为 / - # 关于-H, -T, -I, -E - # -I, -E, -H, -T 后可跟多个匹配式, 需要放在命令行末尾 - f keyword1 keyword2 [directory] -H head -T tail -I "re(gul.*) ex(p|g)ress$" - f keyword1 keyword2 [directory] -H head -T tail -E "re(gul.*) ex(p|g)ress$" - # 搜索 加 通道(只支持 donwload, play, rnre, rm, mv) - f keyword1 keyword2 [directory] \| d -R 递归搜索后递归下载 - ftt keyword1 keyword2 [directory] \| p -R 递归搜索(by time 反序)后递归播放 - f keyword1 keyword2 [directory] \| rnr foo bar -R 递归搜索后rename by regex - f keyword1 keyword2 [directory] \| rm -R -T tail 递归搜索后删除 - f keyword1 keyword2 [directory] \| mv /path/to -R 递归搜索后移动 - - # 列出文件 - l path1 path2 ls by name - ll path1 path2 ls by name 反序 - ln path1 path2 ls by name - lnn path1 path2 ls by name 反序 - lt path1 path2 ls by time - ltt path1 path2 ls by time 反序 - ls path1 path2 ls by size - lss path1 path2 ls by size 反序 - l /doc/books /videos - # 以下是只列出文件或文件夹 - l path1 path2 -t f ls files - l path1 path2 -t d ls directorys - # 关于-H, -T, -I, -E - # -I, -E, -H, -T 后可跟多个匹配式, 需要放在命令行末尾 - l path1 path2 -H head -T tail -I "^re(gul.*) ex(p|g)ress$" - l path1 path2 -H head -T tail -E "^re(gul.*) ex(p|g)ress$" - # 显示绝对路径 - l path1 path2 -v - # 显示文件size, md5 - l path1 path2 -vv - # 空文件夹 - l path1 path2 -t e,d - # 非空文件夹 - l path1 path2 -t ne,d - - # 分享文件 - S 或 share path1 path2 .. 为每个提供的文件路劲创建分享链接 - S 或 share [-P pawd 或 --passwd pawd] path1 path2 为每个提供的路径创建加密的分享链接 - - # 查看文件占用空间 - du path1 path2 文件夹下所有*文件(不包含下层文件夹)*总大小 - du path1 path2 -R 文件夹下所有*文件(包含下层文件夹)*总大小 - 如果下层文件多,会花一些时间 - # 相当于 l path1 path2 -t du [-R] - # eg: - du /doc /videos -R - - # 离线下载 - a 或 add http https ftp ed2k remotepath - a 或 add magnet remotepath [-t {m,i,d,p}] - a 或 add remote_torrent [-t {m,i,d,p}] # 使用网盘中torrent - - # 离线任务操作 - j 或 job # 列出离线下载任务 - jd 或 jobdump # 清除全部 *非正在下载中的任务* - jc 或 jobclear taskid1 taskid2 # 清除 *正在下载中的任务* - jca 或 jobclearall # 清除 *全部任务* - - #### 参数: - - -a num, --aria2c num aria2c分段下载数量: eg: -a 10 - -p, --play play with mpv - -y, --yes yes # 用于 rmre, mvre, cpre, rnre !!慎用 - -q, --quiet 无输出模式, 用于 download, play - -V, --VERIFY verification - -v, --view view detail - eg: - a magnet /path -v # 离线下载并显示下载的文件 - d -p url1 url2 -v # 显示播放文件的完整路径 - l path1 path2 -vv # 显示文件的size, md5 - -s SECRET, --secret SECRET 提取密码 - -f number, --from_ number 从第几个开始(用于download, play),eg: p /video -f 42 - -t ext, --type_ ext 类型参数, 用 "," 分隔 - eg: - d -t dc # 下载并解密,覆盖加密文件(默认) - d -t dc,no # 下载并解密,不覆盖加密文件 - dc -t no # 解密,不覆盖加密文件 - d -t ie # ignore error, 忽略除Ctrl-C以外的下载错误 - p -t m3 # 播放流媒体(m3u8) - s -t c # 连续转存 (如果转存出错,再次运行命令 - # 可以从出错的地方开始,用于转存大量文件时) - l -v # 显示绝对路径 - l -t f # 文件 - l -t d # 文件夹 - l -t du # 查看文件占用空间 - l -t e,d # 空文件夹 - f -t all # 搜索所有账户 - a -t m,d,p,a - u -t ec # encrypt, 加密上传, 默认加前缀 - u -t ec,np # encrypt, 加密上传, 不加前缀 - u -t r # 只进行 rapidupload - u -t e # 如果云端已经存在则不上传(不比对md5) - u -t r,e - -t s # shuffle,乱序 - -l amount, --limit amount 下载速度限制,eg: -l 100k - -m {o,c}, --mode {o,c} 模式: o # 重新上传. c # 连续上传. - 加密方法: https://github.com/shadowsocks/shadowsocks/wiki/Encryption - -R, --recursive 递归, 用于download, play, ls, find, rmre, rnre, rmre, cpre - -H HEADS, --head HEADS 匹配开头的字符,eg: -H Head1 Head2 .. - -T TAILS, --tail TAILS 匹配结尾的字符,eg: -T Tail1 Tail2 .. - -I INCLUDES, --include INCLUDES 不排除匹配到表达的文件名, 可以是正则表达式,eg: -I "*.mp3" "*.avi" .. - -E EXCLUDES, --exclude EXCLUDES 排除匹配到表达的文件名, 可以是正则表达式,eg: -E "*.html" "*.jpg" .. - -c {on, off}, --ls_color {on, off} ls 颜色,默认是on - - # -t, -H, -T, -I, -E 都能用于 download, play, ls, find, rnre, rmre, cpre, mvre +``` +# 登录 +g +login +login username +login username password + +# 删除帐号 +userdelete 或 ud + +# 切换帐号 +userchange 或 uc + +# 帐号信息 +user + +# 显示当前工作目录 +cwd + +# 切换当前工作目录 +cd path # 支持 ./../... + +# 播放 +p 或 play url1 url2 .. path1 path2 .. + +# 上传 +u 或 upload localpath remotepath + +# 转存 +s 或 save url remotepath [-s secret] + +# 下载 +d 或 download url1 url2 path1 path2 非递归下载 到当前目录(cwd) +d 或 download url1 url2 path1 path2 -R 递归下载 到当前目录(cwd) +# !! 注意: +# d /path/to/download -R 递归下载 *download文件夹* 到当前目录(cwd) +# d /path/to/download/ -R 递归下载 *download文件夹中的文件* 到当前目录(cwd) + +# 下载并解密 +d /path/to/download -R -t dc -P password [-m aes-256-cfb] + +# 文件操作 +md 或 mkdir path1 path2 创建文件夹 +rn 或 rename path new_path 重命名 +rm 或 remove path1 path2 删除 +mv 或 move path1 path2 /path/to/directory 移动 +cp 或 copy path /path/to/directory_or_file 复制 +cp 或 copy path1 path2 /path/to/directory 复制 + +# 使用正则表达式进行文件操作 +rnr 或 rnre foo bar dir1 dir2 -I re1 re2 重命名文件夹中的文件名 +rmr 或 rmre dir1 dir2 -E re1 re2 删除文件夹下匹配到的文件 +mvr 或 mvre dir1 dir2 /path/to/dir -H head1 head2 移动文件夹下匹配到的文件 +cpr 或 cpre dir1 dir2 /path/to/dir -T tail1 tail2 复制文件夹下匹配到的文件 +# 递归加 -R +# rmr, mvr, cpr 中 -t, -I, -E, -H, -T 至少要有一个,放在命令行末尾 +# -I, -E, -H, -T 后可跟多个匹配式 +# 可以用 -t 指定操作的文件类型 + -t f # 文件 + -t d # 文件夹 +# rnr 中 foo bar 都是 regex +# -y, --yes # 不显示警示,直接进行。 !!注意,除非你知道你做什么,否则请不要使用。 +rmr / -I '.*' -y # !! 删除网盘中的所有文件 + +# 回复用bt.py做base64加密的文件 +rnr /path/to/decode1 /path/to/decode2 -t f,bd64 + +# 搜索 +# directory 必须是绝对路径, 默认是 cwd +f 或 find keyword1 keyword2 [directory] 非递归搜索 +ff keyword1 keyword2 [directory] 非递归搜索 反序 +ft keyword1 keyword2 [directory] 非递归搜索 by time +ftt keyword1 keyword2 [directory] 非递归搜索 by time 反序 +fs keyword1 keyword2 [directory] 非递归搜索 by size +fss keyword1 keyword2 [directory] 非递归搜索 by size 反序 +fn keyword1 keyword2 [directory] 非递归搜索 by name +fnn keyword1 keyword2 [directory] 非递归搜索 by name 反序 +# 递归搜索加 -R +f 'ice and fire' /doc -R +# 搜索所有的账户加 -t all +f keyword1 keyword2 [directory] -t all -R +f keyword1 keyword2 [directory] -t f,all -R +# directory 默认为 / +# 关于-H, -T, -I, -E +# -I, -E, -H, -T 后可跟多个匹配式, 需要放在命令行末尾 +f keyword1 keyword2 [directory] -H head -T tail -I "re(gul.*) ex(p|g)ress$" +f keyword1 keyword2 [directory] -H head -T tail -E "re(gul.*) ex(p|g)ress$" +# 搜索 加 通道(只支持 donwload, play, rnre, rm, mv) +f keyword1 keyword2 [directory] \| d -R 递归搜索后递归下载 +ftt keyword1 keyword2 [directory] \| p -R 递归搜索(by time 反序)后递归播放 +f keyword1 keyword2 [directory] \| rnr foo bar -R 递归搜索后rename by regex +f keyword1 keyword2 [directory] \| rm -R -T tail 递归搜索后删除 +f keyword1 keyword2 [directory] \| mv /path/to -R 递归搜索后移动 + +# 列出文件 +l path1 path2 ls by name +ll path1 path2 ls by name 反序 +ln path1 path2 ls by name +lnn path1 path2 ls by name 反序 +lt path1 path2 ls by time +ltt path1 path2 ls by time 反序 +ls path1 path2 ls by size +lss path1 path2 ls by size 反序 +l /doc/books /videos +# 以下是只列出文件或文件夹 +l path1 path2 -t f ls files +l path1 path2 -t d ls directorys +# 关于-H, -T, -I, -E +# -I, -E, -H, -T 后可跟多个匹配式, 需要放在命令行末尾 +l path1 path2 -H head -T tail -I "^re(gul.*) ex(p|g)ress$" +l path1 path2 -H head -T tail -E "^re(gul.*) ex(p|g)ress$" +# 显示绝对路径 +l path1 path2 -v +# 显示文件size, md5 +l path1 path2 -vv +# 空文件夹 +l path1 path2 -t e,d +# 非空文件夹 +l path1 path2 -t ne,d + +# 分享文件 +S 或 share path1 path2 .. 为每个提供的文件路劲创建分享链接 +S 或 share [-P pawd 或 --passwd pawd] path1 path2 为每个提供的路径创建加密的分享链接 + +# 查看文件占用空间 +du path1 path2 文件夹下所有*文件(不包含下层文件夹)*总大小 +du path1 path2 -R 文件夹下所有*文件(包含下层文件夹)*总大小 + 如果下层文件多,会花一些时间 +# 相当于 l path1 path2 -t du [-R] +# eg: +du /doc /videos -R + +# 离线下载 +a 或 add http https ftp ed2k remotepath +a 或 add magnet remotepath [-t {m,i,d,p}] +a 或 add remote_torrent [-t {m,i,d,p}] # 使用网盘中torrent + +# 离线任务操作 +j 或 job # 列出离线下载任务 +jd 或 jobdump # 清除全部 *非正在下载中的任务* +jc 或 jobclear taskid1 taskid2 # 清除 *正在下载中的任务* +jca 或 jobclearall # 清除 *全部任务* +``` + +#### 参数: + +``` +-a num, --aria2c num aria2c分段下载数量: eg: -a 10 +-p, --play play with mpv +-y, --yes yes # 用于 rmre, mvre, cpre, rnre !!慎用 +-q, --quiet 无输出模式, 用于 download, play +-V, --VERIFY verification +-v, --view view detail + eg: + a magnet /path -v # 离线下载并显示下载的文件 + d -p url1 url2 -v # 显示播放文件的完整路径 + l path1 path2 -vv # 显示文件的size, md5 +-s SECRET, --secret SECRET 提取密码 +-f number, --from_ number 从第几个开始(用于download, play),eg: p /video -f 42 +-t ext, --type_ ext 类型参数, 用 "," 分隔 + eg: + d -t dc # 下载并解密,覆盖加密文件(默认) + d -t dc,no # 下载并解密,不覆盖加密文件 + dc -t no # 解密,不覆盖加密文件 + d -t ie # ignore error, 忽略除Ctrl-C以外的下载错误 + p -t m3 # 播放流媒体(m3u8) + s -t c # 连续转存 (如果转存出错,再次运行命令 + # 可以从出错的地方开始,用于转存大量文件时) + l -v # 显示绝对路径 + l -t f # 文件 + l -t d # 文件夹 + l -t du # 查看文件占用空间 + l -t e,d # 空文件夹 + f -t all # 搜索所有账户 + a -t m,d,p,a + u -t ec # encrypt, 加密上传, 默认加前缀 + u -t ec,np # encrypt, 加密上传, 不加前缀 + u -t r # 只进行 rapidupload + u -t e # 如果云端已经存在则不上传(不比对md5) + u -t r,e + -t s # shuffle,乱序 +-l amount, --limit amount 下载速度限制,eg: -l 100k +-m {o,c}, --mode {o,c} 模式: o # 重新上传. c # 连续上传. + 加密方法: https://github.com/shadowsocks/shadowsocks/wiki/Encryption +-R, --recursive 递归, 用于download, play, ls, find, rmre, rnre, rmre, cpre +-H HEADS, --head HEADS 匹配开头的字符,eg: -H Head1 Head2 .. +-T TAILS, --tail TAILS 匹配结尾的字符,eg: -T Tail1 Tail2 .. +-I INCLUDES, --include INCLUDES 不排除匹配到表达的文件名, 可以是正则表达式,eg: -I "*.mp3" "*.avi" .. +-E EXCLUDES, --exclude EXCLUDES 排除匹配到表达的文件名, 可以是正则表达式,eg: -E "*.html" "*.jpg" .. +-c {on, off}, --ls_color {on, off} ls 颜色,默认是on + +# -t, -H, -T, -I, -E 都能用于 download, play, ls, find, rnre, rmre, cpre, mvre +``` #### 3. 用法 - \# bp 是pan.baidu.com.py的马甲 (alias bp='python2 /path/to/pan.baidu.com.py') - - #### 登录: - - bp g - bp login - bp login username - bp login username password - - # 多帐号登录 - # 一直用 bp login 即可 - - #### 删除帐号: - - bp ud - - #### 切换帐号: - - bp uc - - #### 帐号信息: - - bp user - - #### 显示当前工作目录 - - bp cwd - - #### 切换当前工作目录 - - bp cd # 切换到 / - bp cd path # 支持 ./../... - bp cd .. - bp cd ../../Music - bp cd ... - - #### 下载: - - # 下载当前工作目录 (递归) - bp d . -R - - # 下载自己网盘中的*单个或多个文件* - bp d http://pan.baidu.com/disk/home#dir/path=/path/to/filename1 http://pan.baidu.com/disk/home#dir/path=/path/to/filename2 .. - # or - bp d /path/to/filename1 /path/to/filename2 .. - - # 递归下载自己网盘中的*单个或多个文件夹* - bp d -R http://pan.baidu.com/disk/home#dir/path=/path/to/directory1 http://pan.baidu.com/disk/home#dir/path=/path/to/directory2 .. - # or - bp d -R /path/to/directory1 /path/to/directory2 .. - # 递归下载后缀为 .mp3 的文件 - bp d -R /path/to/directory1 /path/to/directory2 .. -T .mp3 - - # 非递归下载 - bp d relative_path/to/directory1 /path/to/directory2 .. - - # 下载别人分享的*单个文件* - bp d http://pan.baidu.com/s/1o6psfnxx .. - bp d 'http://pan.baidu.com/share/link?shareid=1622654699&uk=1026372002&fid=2112674284' .. +bp 是pan.baidu.com.py的马甲 (alias bp='python2 /path/to/pan.baidu.com.py') - # 下载别人加密分享的*单个文件*,密码参数-s - bp d http://pan.baidu.com/s/1i3FVlw5 -s vuej +#### 登录: - # 用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 +``` +bp g +bp login +bp login username +bp login username password - # 下载并解码 - ## 默认加密方法为 aes-256-cfb - bp d /path/to/encrypted_file -t dc -P password # 覆盖加密文件 - bp d /path/to/encrypted_file -t dc,no # 不覆盖加密文件 - ## 设置加密方法 - bp d /path/to/encrypted_file -t dc -P password -m 'rc4-md5' - bp d /path/to/directory -t dc -P password -m 'rc4-md5' +# 多帐号登录 +# 一直用 bp login 即可 +``` - #### 解码已下载的加密文件: +#### 删除帐号: - bp dc /local/to/encrypted_file -P password -m 'aes-256-cfb' - bp dc /local/to/encrypted_file -P password - bp dc /local/to/directory -P password +``` +bp ud +``` - #### 播放: +#### 切换帐号: - bp p /movie/her.mkv - bp p http://pan.baidu.com/s/xxxxxxxxx -s [secret] +``` +bp uc +``` - bp cd /movie - bp p movie -R # 递归播放 /movie 中所有媒体文件 +#### 帐号信息: - # 播放流媒体(m3u8) - 上面的命令后加 -t m3 - 清晰度与在浏览器上播放的一样. - 如果源文件是高清的(720P,1280P),那么流媒体会自动转为480P. +``` +bp user +``` - #### 离线下载: +#### 显示当前工作目录 - bp a http://mirrors.kernel.org/archlinux/iso/latest/archlinux-2014年06月01日-dual.iso /path/to/save - bp a https://github.com/PeterDing/iScript/archive/master.zip /path/to/save - bp a ftp://ftp.netscape.com/testfile /path/to/save +``` +bp cwd +``` - bp a 'magnet:?xt=urn:btih:64b7700828fd44b37c0c045091939a2c0258ddc2' /path/to/save -v -t a - bp a 'ed2k://|file|[美]徐中約《中国近代史》第六版原版PDF.rar|547821118|D09FC5F70DEA63E585A74FBDFBD7598F|/' /path/to/save +#### 切换当前工作目录 - bp a /path/to/a.torrent .. -v -t m,i # 使用网盘中torrent,下载到/path/to - # 注意 --------------------- - ↓ - 网盘中的torrent +``` +bp cd # 切换到 / +bp cd path # 支持 ./../... +bp cd .. +bp cd ../../Music +bp cd ... +``` - #### magnet离线下载 -- 文件选择: +#### 下载: - -t m # 视频文件 (默认), 如: mkv, avi ..etc - -t i # 图像文件, 如: jpg, png ..etc - -t d # 文档文件, 如: pdf, doc, docx, epub, mobi ..etc - -t p # 压缩文件, 如: rar, zip ..etc - -t a # 所有文件 - m, i, d, p, a 可以任意组合(用,分隔), 如: -t m,i,d -t d,p -t i,p - remotepath 默认为 / +``` +# 下载当前工作目录 (递归) +bp d . -R - bp a 'magnet:?xt=urn:btih:64b7700828fd44b37c0c045091939a2c0258ddc2' /path/to/save -v -t p,d - bp a /download/a.torrent -v -t m,i,d # 使用网盘中torrent,下载到/download +# 下载自己网盘中的*单个或多个文件* +bp d http://pan.baidu.com/disk/home#dir/path=/path/to/filename1 http://pan.baidu.com/disk/home#dir/path=/path/to/filename2 .. +# or +bp d /path/to/filename1 /path/to/filename2 .. - #### 离线任务操作: +# 递归下载自己网盘中的*单个或多个文件夹* +bp d -R http://pan.baidu.com/disk/home#dir/path=/path/to/directory1 http://pan.baidu.com/disk/home#dir/path=/path/to/directory2 .. +# or +bp d -R /path/to/directory1 /path/to/directory2 .. +# 递归下载后缀为 .mp3 的文件 +bp d -R /path/to/directory1 /path/to/directory2 .. -T .mp3 - bp j - bp j 3482938 8302833 - bp jd - bp jc taskid1 taskid2 .. - bp jc 1208382 58239221 .. - bp jca +# 非递归下载 +bp d relative_path/to/directory1 /path/to/directory2 .. + +# 下载别人分享的*单个文件* +bp d http://pan.baidu.com/s/1o6psfnxx .. +bp d 'http://pan.baidu.com/share/link?shareid=1622654699&uk=1026372002&fid=2112674284' .. - #### 上传: (默认为非递归,递归加 -R) +# 下载别人加密分享的*单个文件*,密码参数-s +bp d http://pan.baidu.com/s/1i3FVlw5 -s vuej + +# 用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 - # 支持文件类型选择 - bp u ~/Documents/* # 默认上传所以文件 - bp u ~/Documents/* -t f # 不上传文件夹 - bp u ~/Documents/* -t d # 不上传文件 - bp u ~/Documents/* -t f,d # 不上传文件和文件夹 +# 下载并解码 +## 默认加密方法为 aes-256-cfb +bp d /path/to/encrypted_file -t dc -P password # 覆盖加密文件 +bp d /path/to/encrypted_file -t dc,no # 不覆盖加密文件 +## 设置加密方法 +bp d /path/to/encrypted_file -t dc -P password -m 'rc4-md5' +bp d /path/to/directory -t dc -P password -m 'rc4-md5' +``` - bp u ~/Documents/reading/三体\ by\ 刘慈欣.mobi /doc -m o - # 上传模式: - # -m o --> 重传 - # -m c --> 续传 (默认) - # 递归加-R +#### 解码已下载的加密文件: - bp u ~/Videos/*.mkv ../videos -t r - # 只进行rapidupload +``` +bp dc /local/to/encrypted_file -P password -m 'aes-256-cfb' +bp dc /local/to/encrypted_file -P password +bp dc /local/to/directory -P password +``` - bp u ~/Documents ~/Videos ~/Documents /backup -t e -R - # 如果云端已经存在则不上传(不比对md5) - # 用 -t e 时, -m o 无效 +#### 播放: + +``` +bp p /movie/her.mkv +bp p http://pan.baidu.com/s/xxxxxxxxx -s [secret] + +bp cd /movie +bp p movie -R # 递归播放 /movie 中所有媒体文件 + +# 播放流媒体(m3u8) +上面的命令后加 -t m3 +清晰度与在浏览器上播放的一样. +如果源文件是高清的(720P,1280P),那么流媒体会自动转为480P. +``` + +#### 离线下载: + +``` +bp a http://mirrors.kernel.org/archlinux/iso/latest/archlinux-2014年06月01日-dual.iso /path/to/save +bp a https://github.com/PeterDing/iScript/archive/master.zip /path/to/save +bp a ftp://ftp.netscape.com/testfile /path/to/save + +bp a 'magnet:?xt=urn:btih:64b7700828fd44b37c0c045091939a2c0258ddc2' /path/to/save -v -t a +bp a 'ed2k://|file|[美]徐中約《中国近代史》第六版原版PDF.rar|547821118|D09FC5F70DEA63E585A74FBDFBD7598F|/' /path/to/save + +bp a /path/to/a.torrent .. -v -t m,i # 使用网盘中torrent,下载到/path/to +# 注意 --------------------- + ↓ + 网盘中的torrent +``` + +#### magnet离线下载 -- 文件选择: + +``` +-t m # 视频文件 (默认), 如: mkv, avi ..etc +-t i # 图像文件, 如: jpg, png ..etc +-t d # 文档文件, 如: pdf, doc, docx, epub, mobi ..etc +-t p # 压缩文件, 如: rar, zip ..etc +-t a # 所有文件 +m, i, d, p, a 可以任意组合(用,分隔), 如: -t m,i,d -t d,p -t i,p +remotepath 默认为 / + +bp a 'magnet:?xt=urn:btih:64b7700828fd44b37c0c045091939a2c0258ddc2' /path/to/save -v -t p,d +bp a /download/a.torrent -v -t m,i,d # 使用网盘中torrent,下载到/download +``` + +#### 离线任务操作: + +``` +bp j +bp j 3482938 8302833 +bp jd +bp jc taskid1 taskid2 .. +bp jc 1208382 58239221 .. +bp jca +``` + +#### 上传: (默认为非递归,递归加 -R) + +``` +# 支持文件类型选择 +bp u ~/Documents/* # 默认上传所以文件 +bp u ~/Documents/* -t f # 不上传文件夹 +bp u ~/Documents/* -t d # 不上传文件 +bp u ~/Documents/* -t f,d # 不上传文件和文件夹 + +bp u ~/Documents/reading/三体\ by\ 刘慈欣.mobi /doc -m o +# 上传模式: +# -m o --> 重传 +# -m c --> 续传 (默认) +# 递归加-R + +bp u ~/Videos/*.mkv ../videos -t r +# 只进行rapidupload + +bp u ~/Documents ~/Videos ~/Documents /backup -t e -R +# 如果云端已经存在则不上传(不比对md5) +# 用 -t e 时, -m o 无效 + +bp u ~/Documents ~/Videos ~/Documents /backup -t r,e # 以上两种模式 +``` + +#### 加密上传: (默认为非递归,递归加 -R) + +``` +bp u ~/{p1,p2,p3} -t ec -P password # 默认加密方法 'aes-256-cfb' +bp u ~/{p1,p2,p3} -t ec -P password -m 'rc4-md5' + +# 注意: +# 上传后的文件名会默认加上前缀 encrypted_ +# 不加前缀用 -t ec,np +``` + +#### 转存: + +``` +bp s url remotepath [-s secret] +# url是他人分享的连接, 如: http://pan.baidu.com/share/link?shareid=xxxxxxx&uk=xxxxxxx, http://pan.baidu.com/s/xxxxxxxx +bp s 'http://pan.baidu.com/share/link?shareid=xxxxxxx&uk=xxxxxxx' /path/to/save +bp s http://pan.baidu.com/s/xxxxxxxx /path/to/save +bp s http://pan.baidu.com/s/xxxxxxxx /path/to/save -s xxxx +bp s http://pan.baidu.com/s/xxxxxxxx#dir/path=/path/to/anything /path/to/save -s xxxx + +bp s http://pan.baidu.com/inbox/i/xxxxxxxx /path/to/save + +# -t c 连续转存 (如果转存出错,再次运行命令可以从出错的地方开始,用于转存大量文件时) +bp s 'http://pan.baidu.com/share/link?shareid=2705944270&uk=708312363' /path/to/save -t c +# 注意:再次运行时,命令要一样。 +``` + +#### 搜索: + +``` +bp f keyword1 keyword2 +bp f "this is one keyword" "this is another keyword" /path/to/search + +bp f ooxx -R +bp f 三体 /doc/fiction -R +bp f 晓波 /doc -R + +bp ff keyword1 keyword2 /path/to/music 非递归搜索 反序 +bp ft keyword1 keyword2 /path/to/doc 非递归搜索 by time +bp ftt keyword1 keyword2 /path/to/other 非递归搜索 by time 反序 +bp fs keyword1 keyword2 非递归搜索 by size +bp fss keyword1 keyword2 非递归搜索 by size 反序 +bp fn keyword1 keyword2 非递归搜索 by name +bp fnn keyword1 keyword2 非递归搜索 by name 反序 + +# 递归搜索加 -R +# 关于-H, -T, -I, -E +bp f mp3 /path/to/search -H "[" "01" -T ".tmp" -I ".*-.*" -R + +# 搜索所有的账户 +bp f iDoNotKnow .. [directory] -t all -R +bp f archlinux ubuntu .. [directory] -t f,all -T .iso -R + +# 搜索 加 通道(只支持 donwload, play, rnre, rm, mv) +bp f bioloy \| d -R 递归搜索后递归下载 +bp ftt ooxx \| p -R -t f 递归搜索(by time 反序)后递归播放 +bp f sound \| rnr mp3 mp4 -R 递归搜索后rename by regex +bp f ccav \| rm -R -T avi 递归搜索后删除 +bp f 新闻联播(大结局) \| mv /Favor -R 递归搜索后移动 +``` + +#### 恢复用bt.py做base64加密的文件: - bp u ~/Documents ~/Videos ~/Documents /backup -t r,e # 以上两种模式 +``` +rnr /ooxx -t f,bd64 +!! 注意: /ooxx 中的所有文件都必须是被base64加密的,且加密段要有.base64后缀 +# 可以参考 by.py 的用法 +``` + +ls、重命名、移动、删除、复制、使用正则表达式进行文件操作: - #### 加密上传: (默认为非递归,递归加 -R) - - bp u ~/{p1,p2,p3} -t ec -P password # 默认加密方法 'aes-256-cfb' - bp u ~/{p1,p2,p3} -t ec -P password -m 'rc4-md5' - - # 注意: - # 上传后的文件名会默认加上前缀 encrypted_ - # 不加前缀用 -t ec,np - - #### 转存: - - bp s url remotepath [-s secret] - # url是他人分享的连接, 如: http://pan.baidu.com/share/link?shareid=xxxxxxx&uk=xxxxxxx, http://pan.baidu.com/s/xxxxxxxx - bp s 'http://pan.baidu.com/share/link?shareid=xxxxxxx&uk=xxxxxxx' /path/to/save - bp s http://pan.baidu.com/s/xxxxxxxx /path/to/save - bp s http://pan.baidu.com/s/xxxxxxxx /path/to/save -s xxxx - bp s http://pan.baidu.com/s/xxxxxxxx#dir/path=/path/to/anything /path/to/save -s xxxx - - bp s http://pan.baidu.com/inbox/i/xxxxxxxx /path/to/save - - # -t c 连续转存 (如果转存出错,再次运行命令可以从出错的地方开始,用于转存大量文件时) - bp s 'http://pan.baidu.com/share/link?shareid=2705944270&uk=708312363' /path/to/save -t c - # 注意:再次运行时,命令要一样。 - - #### 搜索: - - bp f keyword1 keyword2 - bp f "this is one keyword" "this is another keyword" /path/to/search - - bp f ooxx -R - bp f 三体 /doc/fiction -R - bp f 晓波 /doc -R - - bp ff keyword1 keyword2 /path/to/music 非递归搜索 反序 - bp ft keyword1 keyword2 /path/to/doc 非递归搜索 by time - bp ftt keyword1 keyword2 /path/to/other 非递归搜索 by time 反序 - bp fs keyword1 keyword2 非递归搜索 by size - bp fss keyword1 keyword2 非递归搜索 by size 反序 - bp fn keyword1 keyword2 非递归搜索 by name - bp fnn keyword1 keyword2 非递归搜索 by name 反序 - - # 递归搜索加 -R - # 关于-H, -T, -I, -E - bp f mp3 /path/to/search -H "[" "01" -T ".tmp" -I ".*-.*" -R - - # 搜索所有的账户 - bp f iDoNotKnow .. [directory] -t all -R - bp f archlinux ubuntu .. [directory] -t f,all -T .iso -R - - # 搜索 加 通道(只支持 donwload, play, rnre, rm, mv) - bp f bioloy \| d -R 递归搜索后递归下载 - bp ftt ooxx \| p -R -t f 递归搜索(by time 反序)后递归播放 - bp f sound \| rnr mp3 mp4 -R 递归搜索后rename by regex - bp f ccav \| rm -R -T avi 递归搜索后删除 - bp f 新闻联播(大结局) \| mv /Favor -R 递归搜索后移动 - - #### 恢复用bt.py做base64加密的文件: - - rnr /ooxx -t f,bd64 - !! 注意: /ooxx 中的所有文件都必须是被base64加密的,且加密段要有.base64后缀 - # 可以参考 by.py 的用法 - - ls、重命名、移动、删除、复制、使用正则表达式进行文件操作: - - 见[命令](#cmd) +见[命令](#cmd) #### 4. 参考: @@ -650,103 +702,113 @@ #### 1. 依赖 - python2-requests (https://github.com/kennethreitz/requests) - bencode (https://github.com/bittorrent/bencode) +``` +python2-requests (https://github.com/kennethreitz/requests) +bencode (https://github.com/bittorrent/bencode) +``` #### 2. 使用说明 - magnet 和 torrent 的相互转换 +magnet 和 torrent 的相互转换 - 过滤敏.感.词功能用于净网时期的 baidu, xunlei +过滤敏.感.词功能用于净网时期的 baidu, xunlei - ~~8.30日后,无法使用。 见 http://tieba.baidu.com/p/3265467666~~ +~~8.30日后,无法使用。 见 http://tieba.baidu.com/p/3265467666~~ - [**百度云疑似解封,百度网盘内八秒视频部分恢复**](http://fuli.ba/baiduyunhuifuguankan.html) +[**百度云疑似解封,百度网盘内八秒视频部分恢复**](http://fuli.ba/baiduyunhuifuguankan.html) - **!! 注意:过滤后生成的torrent在百度网盘只能用一次,如果需要再次使用,则需用 -n 改顶层目录名** +**!! 注意:过滤后生成的torrent在百度网盘只能用一次,如果需要再次使用,则需用 -n 改顶层目录名** - 磁力连接转种子,用的是 +磁力连接转种子,用的是 - http://bt.box.n0808.com - http://btcache.me - http://www.sobt.org # 302 --> http://www.win8down.com/url.php?hash= - http://www.31bt.com - http://178.73.198.210 - http://www.btspread.com # link to http://btcache.me - http://torcache.net - http://zoink.it - http://torrage.com # 用torrage.com需要设置代理, eg: -p 127.0.0.1:8087 - http://torrentproject.se - http://istoretor.com - http://torrentbox.sx - http://www.torrenthound.com - http://www.silvertorrent.org - http://magnet.vuze.com +``` +http://bt.box.n0808.com +http://btcache.me +http://www.sobt.org # 302 --> http://www.win8down.com/url.php?hash= +http://www.31bt.com +http://178.73.198.210 +http://www.btspread.com # link to http://btcache.me +http://torcache.net +http://zoink.it +http://torrage.com # 用torrage.com需要设置代理, eg: -p 127.0.0.1:8087 +http://torrentproject.se +http://istoretor.com +http://torrentbox.sx +http://www.torrenthound.com +http://www.silvertorrent.org +http://magnet.vuze.com +``` - 如果有更好的种子库,请提交issue +如果有更好的种子库,请提交issue -> 对于baidu, 加入离线任务后,需等待一段时间才会下载完成。 +> 对于baidu, 加入离线任务后,需等待一段时间才会下载完成。 - #### 命令: +#### 命令: - # magnet 2 torrent - m 或 mt magnet_link1 magnet_link2 .. [-d /path/to/save] - m -i /there/are/files -d new +``` +# magnet 2 torrent +m 或 mt magnet_link1 magnet_link2 .. [-d /path/to/save] +m -i /there/are/files -d new - # torrent 2 magnet, 输出magnet - t 或 tm path1 path2 .. +# torrent 2 magnet, 输出magnet +t 或 tm path1 path2 .. - # 过滤敏.感.词 - # 有2种模式 - # -t n (默认) 用数字替换文件名 - # -t be64 用base64加密文件名,torrent用百度下载后,可用 pan.baidu.com.py rnr /path -t f,bd64 改回原名字 - c 或 ct magnet_link1 magnet_link2 .. /path/to/torrent1 /path/to/torrent2 .. [-d /path/to/save] - c -i /there/are/files and_other_dir -d new # 从文件或文件夹中寻找 magnet,再过滤 - # 过滤敏.感.词 - 将magnet或torrent转成不敏感的 torrent - # /path/to/save 默认为 . +# 过滤敏.感.词 +# 有2种模式 +# -t n (默认) 用数字替换文件名 +# -t be64 用base64加密文件名,torrent用百度下载后,可用 pan.baidu.com.py rnr /path -t f,bd64 改回原名字 +c 或 ct magnet_link1 magnet_link2 .. /path/to/torrent1 /path/to/torrent2 .. [-d /path/to/save] +c -i /there/are/files and_other_dir -d new # 从文件或文件夹中寻找 magnet,再过滤 +# 过滤敏.感.词 - 将magnet或torrent转成不敏感的 torrent +# /path/to/save 默认为 . - # 用base64加密的文件名: - c magnet_link1 magnet_link2 .. /path/to/torrent1 /path/to/torrent2 .. [-d /path/to/save] -t be64 +# 用base64加密的文件名: +c magnet_link1 magnet_link2 .. /path/to/torrent1 /path/to/torrent2 .. [-d /path/to/save] -t be64 - # 使用正则表达式过滤敏.感.词 - cr 或 ctre foo bar magnet_link1 /path/to/torrent1 .. [-d /path/to/save] - # foo bar 都是 regex +# 使用正则表达式过滤敏.感.词 +cr 或 ctre foo bar magnet_link1 /path/to/torrent1 .. [-d /path/to/save] +# foo bar 都是 regex +``` - #### 参数: +#### 参数: - -p PROXY, --proxy PROXY proxy for torrage.com, eg: -p 127.0.0.1:8087 (默认) - -t TYPE_, --type_ TYPE_ 类型参数: - -t n (默认) 用数字替换文件名 - -t be64 用base64加密文件名,torrent用百度下载后,可用 pan.baidu.com.py rnr /path -t f,bd64 改回原名字 - -d DIRECTORY, --directory DIRECTORY 指定torrents的保存路径, eg: -d /path/to/save - -n NAME, --name NAME 顶级文件夹名称, eg: -m thistopdirectory - -i localpath1 localpath2 .., --import_from localpath1 localpath2 .. 从本地文本文件导入magnet (用正则表达式匹配) +``` +-p PROXY, --proxy PROXY proxy for torrage.com, eg: -p 127.0.0.1:8087 (默认) +-t TYPE_, --type_ TYPE_ 类型参数: + -t n (默认) 用数字替换文件名 + -t be64 用base64加密文件名,torrent用百度下载后,可用 pan.baidu.com.py rnr /path -t f,bd64 改回原名字 +-d DIRECTORY, --directory DIRECTORY 指定torrents的保存路径, eg: -d /path/to/save +-n NAME, --name NAME 顶级文件夹名称, eg: -m thistopdirectory +-i localpath1 localpath2 .., --import_from localpath1 localpath2 .. 从本地文本文件导入magnet (用正则表达式匹配) +``` #### 3. 用法 - \# bt 是bt.py的马甲 (alias bt='python2 /path/to/bt.py') +bt 是bt.py的马甲 (alias bt='python2 /path/to/bt.py') - bt mt magnet_link1 magnet_link2 .. [-d /path/to/save] - bt tm path1 path2 .. - bt ct magnet_link1 path1 .. [-d /path/to/save] +``` +bt mt magnet_link1 magnet_link2 .. [-d /path/to/save] +bt tm path1 path2 .. +bt ct magnet_link1 path1 .. [-d /path/to/save] - bt m magnet_link1 magnet_link2 .. [-d /path/to/save] - bt t path1 path2 .. - bt c magnet_link1 path1 .. [-d /path/to/save] +bt m magnet_link1 magnet_link2 .. [-d /path/to/save] +bt t path1 path2 .. +bt c magnet_link1 path1 .. [-d /path/to/save] - # 用torrage.com - bt m magnet_link1 path1 .. -p 127.0.0.1:8087 - bt c magnet_link1 path1 .. -p 127.0.0.1:8087 +# 用torrage.com +bt m magnet_link1 path1 .. -p 127.0.0.1:8087 +bt c magnet_link1 path1 .. -p 127.0.0.1:8087 - # 从文件或文件夹中寻找 magnet,再过滤 - bt c -i ~/Downloads -d new +# 从文件或文件夹中寻找 magnet,再过滤 +bt c -i ~/Downloads -d new - # 使用正则表达式过滤敏.感.词 - bt cr '.*(old).*' '1円' magnet_link - bt cr 'old.iso' 'new.iso' /path/to/torrent +# 使用正则表达式过滤敏.感.词 +bt cr '.*(old).*' '1円' magnet_link +bt cr 'old.iso' 'new.iso' /path/to/torrent - # 用base64加密的文件名: - bt c magnet_link -t be64 +# 用base64加密的文件名: +bt c magnet_link -t be64 +``` #### 4. 参考: @@ -761,76 +823,84 @@ #### 1. 依赖 - wget +``` +wget - aria2 (~ 1.18) +aria2 (~ 1.18) - python2-requests (https://github.com/kennethreitz/requests) +python2-requests (https://github.com/kennethreitz/requests) - mpv (http://mpv.io) +mpv (http://mpv.io) - mplayer # 我的linux上mpv播放wmv出错,换用mplayer +mplayer # 我的linux上mpv播放wmv出错,换用mplayer +``` #### 2. 使用说明 - 初次使用需要登录 pan115 login +初次使用需要登录 pan115 login - **脚本是用于下载自己的115网盘文件,不支持他人分享文件。** +**脚本是用于下载自己的115网盘文件,不支持他人分享文件。** - 下载工具默认为wget, 可用参数-a选用aria2。 +下载工具默认为wget, 可用参数-a选用aria2。 - **现在vip和非vip用户下载只能有1个通道,用aria2下载已经无意义。** +**现在vip和非vip用户下载只能有1个通道,用aria2下载已经无意义。** - 对所有文件,默认执行下载(用wget),如要播放媒体文件,加参数-p。 +对所有文件,默认执行下载(用wget),如要播放媒体文件,加参数-p。 - **非vip用户下载太慢,已经不支持播放。 vip播放正常** +**非vip用户下载太慢,已经不支持播放。 vip播放正常** - 下载的文件,保存在当前目录下。 +下载的文件,保存在当前目录下。 - cookies保存在 ~/.115.cookies +cookies保存在 ~/.115.cookies - 关于播放操作: +关于播放操作: -> 在运行脚本的终端,输入1次Enter,关闭当前播放并播放下一个文件,连续输入2次Enter,关闭当前播放并退出。 +> 在运行脚本的终端,输入1次Enter,关闭当前播放并播放下一个文件,连续输入2次Enter,关闭当前播放并退出。 - 参数: +#### 参数: - -a, --aria2c download with aria2c - -p, --play play with mpv - -f number, --from_ number 从第几个开始下载,eg: -f 42 - -t ext, --type_ ext 要下载的文件的后缀,eg: -t mp3 - -l amount, --limit amount 下载速度限制,eg: -l 100k - -d "url" 增加离线下载 "http/ftp/magnet/ed2k" +``` +-a, --aria2c download with aria2c +-p, --play play with mpv +-f number, --from_ number 从第几个开始下载,eg: -f 42 +-t ext, --type_ ext 要下载的文件的后缀,eg: -t mp3 +-l amount, --limit amount 下载速度限制,eg: -l 100k +-d "url" 增加离线下载 "http/ftp/magnet/ed2k" +``` #### 3. 用法 - \# pan115 是115.py的马甲 (alias pan115='python2 /path/to/115.py') +pan115 是115.py的马甲 (alias pan115='python2 /path/to/115.py') - # 登录 - pan115 g - pan115 login - pan115 login username - pan115 login username password +``` +# 登录 +pan115 g +pan115 login +pan115 login username +pan115 login username password - # 退出登录 - pan115 signout +# 退出登录 +pan115 signout - # 递归下载自己网盘中的*文件夹* - pan115 http://115.com/?cid=xxxxxxxxxxxx&offset=0&mode=wangpan +# 递归下载自己网盘中的*文件夹* +pan115 http://115.com/?cid=xxxxxxxxxxxx&offset=0&mode=wangpan - # 下载自己网盘中的*单个文件* -- 只能是115上可单独打开的文件,如pdf,视频 - pan115 http://wenku.115.com/preview/?pickcode=xxxxxxxxxxxx +# 下载自己网盘中的*单个文件* -- 只能是115上可单独打开的文件,如pdf,视频 +pan115 http://wenku.115.com/preview/?pickcode=xxxxxxxxxxxx - # 下载用aria2, url 是上面的 - pan115 -a url +# 下载用aria2, url 是上面的 +pan115 -a url - # 增加离线下载 - pan115 -d "magnet:?xt=urn:btih:757fc565c56462b28b4f9c86b21ac753500eb2a7&dn=archlinux-2014年04月01日-dual.iso" +# 增加离线下载 +pan115 -d "magnet:?xt=urn:btih:757fc565c56462b28b4f9c86b21ac753500eb2a7&dn=archlinux-2014年04月01日-dual.iso" +``` - #### 播放 +#### 播放 - # url 是上面的 - pan115 -p url +``` +# url 是上面的 +pan115 -p url +``` #### 4. 参考: @@ -843,58 +913,63 @@ #### 1. 依赖 - wget - - aria2 (~ 1.18) +``` +wget - python2-requests (https://github.com/kennethreitz/requests) +aria2 (~ 1.18) +python2-requests (https://github.com/kennethreitz/requests) +``` #### 2. 使用说明 - 初次使用需要登录 yp login +初次使用需要登录 yp login - **!!!!!! 万恶的360不支持断点续传 !!!!!!** +**!!!!!! 万恶的360不支持断点续传 !!!!!!** - 由于上面的原因,不能播放媒体文件。 +由于上面的原因,不能播放媒体文件。 - 只支持自己的\*文件夹\*的递归下载。 +只支持自己的\*文件夹\*的递归下载。 - 下载工具默认为wget, 可用参数-a选用aria2 +下载工具默认为wget, 可用参数-a选用aria2 - 下载的文件,保存在当前目录下。 +下载的文件,保存在当前目录下。 - cookies保存在 ~/.360.cookies +cookies保存在 ~/.360.cookies - #### 参数: +#### 参数: - -a, --aria2c download with aria2c - -f number, --from_ number 从第几个开始下载,eg: -f 42 - -t ext, --type_ ext 要下载的文件的后缀,eg: -t mp3 - -l amount, --limit amount 下载速度限制,eg: -l 100k +``` +-a, --aria2c download with aria2c +-f number, --from_ number 从第几个开始下载,eg: -f 42 +-t ext, --type_ ext 要下载的文件的后缀,eg: -t mp3 +-l amount, --limit amount 下载速度限制,eg: -l 100k +``` #### 3. 用法 - \# yp 是yunpan.360.cn.py的马甲 (alias yp='python2 /path/to/yunpan.360.cn.py') +yp 是yunpan.360.cn.py的马甲 (alias yp='python2 /path/to/yunpan.360.cn.py') - # 登录 - yp g - yp login - yp login username - yp login username password +``` +# 登录 +yp g +yp login +yp login username +yp login username password - # 退出登录 - yp signout +# 退出登录 +yp signout - # 递归下载自己网盘中的*文件夹* - yp http://c17.yunpan.360.cn/my/?sid=#/path/to/directory - yp http://c17.yunpan.360.cn/my/?sid=#%2Fpath%3D%2Fpath%2Fto%2Fdirectory - # or - yp sid=/path/to/directory - yp sid%3D%2Fpath%2Fto%2Fdirectory +# 递归下载自己网盘中的*文件夹* +yp http://c17.yunpan.360.cn/my/?sid=#/path/to/directory +yp http://c17.yunpan.360.cn/my/?sid=#%2Fpath%3D%2Fpath%2Fto%2Fdirectory +# or +yp sid=/path/to/directory +yp sid%3D%2Fpath%2Fto%2Fdirectory - # 下载用aria2, url 是上面的 - yp -a url +# 下载用aria2, url 是上面的 +yp -a url +``` #### 4. 参考: @@ -907,43 +982,51 @@ #### 1. 依赖 - wget +``` +wget - python2-mutagen (https://code.google.com/p/mutagen/) +python2-mutagen (https://code.google.com/p/mutagen/) - mpv (http://mpv.io) +mpv (http://mpv.io) +``` #### 2. 使用说明 - 默认执行下载,如要播放,加参数-p。 +默认执行下载,如要播放,加参数-p。 - #### 参数: +#### 参数: - -f, --flac download flac - -i, --high download 320, default - -l, --low download 128 - -p, --play play with mpv +``` +-f, --flac download flac +-i, --high download 320, default +-l, --low download 128 +-p, --play play with mpv +``` - 下载的MP3默认添加id3 tags,保存在当前目录下。 +下载的MP3默认添加id3 tags,保存在当前目录下。 - 关于播放操作: +关于播放操作: -> 在运行脚本的终端,输入1次Enter,关闭当前播放并播放下一个文件,连续输入2次Enter,关闭当前播放并退出。 +> 在运行脚本的终端,输入1次Enter,关闭当前播放并播放下一个文件,连续输入2次Enter,关闭当前播放并退出。 #### 3. 用法 - \# bm 是music.baidu.com.py的马甲 (alias bm='python2 /path/to/music.baidu.com.py') +bm 是music.baidu.com.py的马甲 (alias bm='python2 /path/to/music.baidu.com.py') - # 下载专辑 - bm http://music.baidu.com/album/115032005 +``` +# 下载专辑 +bm http://music.baidu.com/album/115032005 - # 下载单曲 - bm http://music.baidu.com/song/117948039 +# 下载单曲 +bm http://music.baidu.com/song/117948039 +``` - #### 播放: +#### 播放: - # url 是上面的 - bm -p url +``` +# url 是上面的 +bm -p url +``` #### 4. 参考: @@ -956,53 +1039,58 @@ #### 1. 依赖 - wget +``` +wget - python2-requests (https://github.com/kennethreitz/requests) +python2-requests (https://github.com/kennethreitz/requests) - python2-mutagen (https://code.google.com/p/mutagen/) +python2-mutagen (https://code.google.com/p/mutagen/) - mpv (http://mpv.io) +mpv (http://mpv.io) +``` #### 2. 使用说明 - **默认下载和播放高品质音乐,如果服务器没有高品质音乐则转到低品质音乐。** +**默认下载和播放高品质音乐,如果服务器没有高品质音乐则转到低品质音乐。** - 默认执行下载,如要播放,加参数-p。 +默认执行下载,如要播放,加参数-p。 - 下载的MP3默认添加id3 tags,保存在当前目录下。 +下载的MP3默认添加id3 tags,保存在当前目录下。 - 关于播放操作: +关于播放操作: -> 在运行脚本的终端,输入1次Enter,关闭当前播放并播放下一个文件,连续输入2次Enter,关闭当前播放并退出。 +> 在运行脚本的终端,输入1次Enter,关闭当前播放并播放下一个文件,连续输入2次Enter,关闭当前播放并退出。 #### 3. 用法 - \# nm 是music.163.com.py的马甲 (alias nm='python2 /path/to/music.163.com.py') +nm 是music.163.com.py的马甲 (alias nm='python2 /path/to/music.163.com.py') - # 下载专辑 - nm http://music.163.com/#/album?id=18915 +``` +# 下载专辑 +nm http://music.163.com/#/album?id=18915 - # 下载单曲 - nm http://music.163.com/#/song?id=186114 +# 下载单曲 +nm http://music.163.com/#/song?id=186114 - # 下载歌单 - nm http://music.163.com/#/playlist?id=12214308 +# 下载歌单 +nm http://music.163.com/#/playlist?id=12214308 - # 下载该艺术家所有专辑或 Top 50 歌曲 - nm http://music.163.com/#/artist?id=6452 +# 下载该艺术家所有专辑或 Top 50 歌曲 +nm http://music.163.com/#/artist?id=6452 - # 下载DJ节目 - nm http://music.163.com/#/dj?id=675051 +# 下载DJ节目 +nm http://music.163.com/#/dj?id=675051 - # 下载排行榜 - nm http://music.163.com/#/discover/toplist?id=11641012 +# 下载排行榜 +nm http://music.163.com/#/discover/toplist?id=11641012 +``` +#### 播放: - #### 播放: - - # url 是上面的 - nm -p url +``` +# url 是上面的 +nm -p url +``` #### 4. 参考: @@ -1017,42 +1105,48 @@ #### 1. 依赖 - wget +``` +wget - python2-requests (https://github.com/kennethreitz/requests) +python2-requests (https://github.com/kennethreitz/requests) - mpv (http://mpv.io) +mpv (http://mpv.io) +``` #### 2. 使用说明 - flvxz.com 视频解析 +flvxz.com 视频解析 - **不提供视频合并操作** +**不提供视频合并操作** - #### 支持的网站: +#### 支持的网站: - """ - 已知支持120个以上视频网站,覆盖大多数国内视频站点,少量国外视频站点 - """ - -- flvxz.com +""" +已知支持120个以上视频网站,覆盖大多数国内视频站点,少量国外视频站点 + """ + -- flvxz.com - 关于播放操作: +关于播放操作: -> 在运行脚本的终端,输入1次Enter,关闭当前播放并播放下一个文件,连续输入2次Enter,关闭当前播放并退出。 +> 在运行脚本的终端,输入1次Enter,关闭当前播放并播放下一个文件,连续输入2次Enter,关闭当前播放并退出。 #### 3. 用法 - \# fl是flvxz_cl.py的马甲 (alias fl='python2 /path/to/flvxz_cl.py') +fl是flvxz_cl.py的马甲 (alias fl='python2 /path/to/flvxz_cl.py') - #### 下载: +#### 下载: - fl http://v.youku.com/v_show/id_XNTI2Mzg4NjAw.html - fl http://www.tudou.com/albumplay/Lqfme5hSolM/tJ_Gl3POz7Y.html +``` +fl http://v.youku.com/v_show/id_XNTI2Mzg4NjAw.html +fl http://www.tudou.com/albumplay/Lqfme5hSolM/tJ_Gl3POz7Y.html +``` - #### 播放: +#### 播放: - # url 是上面的 - fl url -p +``` +# url 是上面的 +fl url -p +``` #### 4. 相关脚本: @@ -1069,41 +1163,47 @@ #### 1. 依赖 - wget +``` +wget - python2-requests (https://github.com/kennethreitz/requests) +python2-requests (https://github.com/kennethreitz/requests) +``` #### 2. 使用说明 - * 使用前需用在 http://www.tumblr.com/oauth/apps 加入一个app,证实后得到api_key,再在源码中填入,完成后则可使用。 +* 使用前需用在 http://www.tumblr.com/oauth/apps 加入一个app,证实后得到api_key,再在源码中填入,完成后则可使用。 - * 或者用 http://www.tumblr.com/docs/en/api/v2 提供的api_key ( fuiKNFp9vQFvjLNvx4sUwti4Yb5yGutBN4Xh10LXZhhRKjWlV4 ) +* 或者用 http://www.tumblr.com/docs/en/api/v2 提供的api_key ( fuiKNFp9vQFvjLNvx4sUwti4Yb5yGutBN4Xh10LXZhhRKjWlV4 ) - 默认开10个进程,如需改变用参数-p [num]。 +默认开10个进程,如需改变用参数-p [num]。 - 下载的文件,保存在当前目录下。 +下载的文件,保存在当前目录下。 - 默认下载原图。 +默认下载原图。 - 支持连续下载,下载进度储存在下载文件夹内的 json.json。 +支持连续下载,下载进度储存在下载文件夹内的 json.json。 - #### 参数: +#### 参数: - -p PROCESSES, --processes PROCESSES 指定多进程数,默认为10个,最多为20个 eg: -p 20 - -c, --check 尝试修复未下载成功的图片 - -t TAG, --tag TAG 下载特定tag的图片, eg: -t beautiful +``` +-p PROCESSES, --processes PROCESSES 指定多进程数,默认为10个,最多为20个 eg: -p 20 +-c, --check 尝试修复未下载成功的图片 +-t TAG, --tag TAG 下载特定tag的图片, eg: -t beautiful +``` #### 3. 用法 - \# tm是tumblr.py的马甲 (alias tm='python2 /path/to/tumblr.py') +tm是tumblr.py的马甲 (alias tm='python2 /path/to/tumblr.py') - # 下载某个tumblr - tm http://sosuperawesome.tumblr.com/ - tm http://sosuperawesome.tumblr.com/ -t beautiful +``` +# 下载某个tumblr +tm http://sosuperawesome.tumblr.com/ +tm http://sosuperawesome.tumblr.com/ -t beautiful - # 指定tag下载 - tm beautiful - tm cool +# 指定tag下载 +tm beautiful +tm cool +``` --- @@ -1112,9 +1212,11 @@ #### 用法 - python2 unzip.py azipfile1.zip azipfile2.zip .. - python2 unzip.py azipfile.zip -s secret - # -s 密码 +``` +python2 unzip.py azipfile1.zip azipfile2.zip .. +python2 unzip.py azipfile.zip -s secret +# -s 密码 +``` 代码来自以下连接,我改了一点。 @@ -1126,15 +1228,20 @@ ### ed2k_search.py - 基于 donkey4u.com 的emule搜索 #### 1. 依赖 - python2 + +``` +python2 +``` #### 2. 用法 - \# ed 是ed2k_search.py的马甲 (alias ed='python2 /path/to/ed2k_search.py') +ed 是ed2k_search.py的马甲 (alias ed='python2 /path/to/ed2k_search.py') - ed this is a keyword - or - ed "this is a keyword" +``` +ed this is a keyword +or +ed "this is a keyword" +``` --- @@ -1145,43 +1252,51 @@ #### 1. 依赖 - wget +``` +wget - aria2 (~ 1.18) +aria2 (~ 1.18) - python2-requests (https://github.com/kennethreitz/requests) +python2-requests (https://github.com/kennethreitz/requests) - mpv (http://mpv.io) +mpv (http://mpv.io) +``` #### 2. 使用说明 -> 没有解决 *7个/day* 限制 +> 没有解决 *7个/day* 限制 - 下载工具默认为wget, 可用参数-a选用aria2 +下载工具默认为wget, 可用参数-a选用aria2 - 默认执行下载,如要播放媒体文件,加参数-p。 +默认执行下载,如要播放媒体文件,加参数-p。 - 下载的文件,保存在当前目录下。 +下载的文件,保存在当前目录下。 - 关于播放操作: +关于播放操作: -> 在运行脚本的终端,输入1次Enter,关闭当前播放并播放下一个文件,连续输入2次Enter,关闭当前播放并退出。 +> 在运行脚本的终端,输入1次Enter,关闭当前播放并播放下一个文件,连续输入2次Enter,关闭当前播放并退出。 #### 3. 用法 - \# pn 是91porn.py的马甲 (alias pn='python2 /path/to/91porn.py') +pn 是91porn.py的马甲 (alias pn='python2 /path/to/91porn.py') - #### 下载: +#### 下载: - pn url # 91porn.com(或其镜像) 视频的url +``` +pn url # 91porn.com(或其镜像) 视频的url +``` - #### 播放: +#### 播放: - pn -p url +``` +pn -p url +``` - 显示下载链接,但不下载: +显示下载链接,但不下载: - pn -u url +``` +pn -u url +``` #### 4. 参考 From 6e37ec8bafd0a63c0933266bba93635c1c246dac Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年4月10日 14:43:02 +0800 Subject: [PATCH 026/158] fix [add_task] --- 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 c84697e..b343c6c 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -1900,7 +1900,7 @@ def ls(self, order, desc, paths): def _exist(self, list_): meta = self._meta(list_) if not meta: - print s % (1, 91, ' !! Error at _exist, some paths are not existed.') + print s % (1, 91, ' !! Error at _exist, some paths are not existed.'), list_ sys.exit(1) def _filemanager(self, opera, data): @@ -2351,7 +2351,7 @@ def _add_task(self, url, remotepath): return def add_tasks(self, urls, remotepath): - remotepath = make_server_path(self.cwd, remotepath) + remotepath = make_server_path(self.cwd, remotepath) + '/' for url in urls: if url.startswith('magnet:') or url.startswith('/'): if url.startswith('/'): From 50fb86cabe41d429d58ace47ddbf30179b0f3453 Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年4月10日 14:43:39 +0800 Subject: [PATCH 027/158] fix [album_pic_url] --- xiami.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/xiami.py b/xiami.py index 92a15cd..1f1ea68 100755 --- a/xiami.py +++ b/xiami.py @@ -507,9 +507,11 @@ def get_songs(self, album_id, song_id=None): t = re.sub(r'<.+?>(\r\n|)', u'\n', t) album_description = t - t = re.search(r'href="(.+?)" id="albumCover"', html1).group(1) - tt = t.rfind('.') - t = '%s_4%s' % (t[:tt], t[tt:]) + #t = re.search(r'href="(.+?)" id="albumCover"', html1).group(1) + t = re.search(r'id="albumCover".+?"(http://.+?)" ', html1).group(1) + #tt = t.rfind('.') + #t = '%s_4%s' % (t[:tt], t[tt:]) + t = t.replace('_2.', '_4.') album_pic_url = t songs = [] From 115751450c12e05de3b4f7e7d43ab8b149405f45 Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年4月11日 14:43:57 +0800 Subject: [PATCH 028/158] fix [save_share] --- README.md | 8 ++++++-- pan.baidu.com.py | 7 +++---- xiami.py | 2 +- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index b80577f..d89b838 100644 --- a/README.md +++ b/README.md @@ -265,6 +265,9 @@ p 或 play url1 url2 .. path1 path2 .. # 上传 u 或 upload localpath remotepath +# 加密上传 +u localpath remotepath -P password -t ec -R + # 转存 s 或 save url remotepath [-s secret] @@ -386,6 +389,7 @@ jca 或 jobclearall # 清除 *全部任务* ``` -a num, --aria2c num aria2c分段下载数量: eg: -a 10 -p, --play play with mpv +-P password, --passwd password 分享密码,加密密码 -y, --yes yes # 用于 rmre, mvre, cpre, rnre !!慎用 -q, --quiet 无输出模式, 用于 download, play -V, --VERIFY verification @@ -516,8 +520,8 @@ bp d url -s [secret] -a 10 # 下载并解码 ## 默认加密方法为 aes-256-cfb -bp d /path/to/encrypted_file -t dc -P password # 覆盖加密文件 -bp d /path/to/encrypted_file -t dc,no # 不覆盖加密文件 +bp d /path/to/encrypted_file -t dc -P password # 覆盖加密文件 (默认) +bp d /path/to/encrypted_file -t dc,no -P password # 不覆盖加密文件 ## 设置加密方法 bp d /path/to/encrypted_file -t dc -P password -m 'rc4-md5' bp d /path/to/directory -t dc -P password -m 'rc4-md5' diff --git a/pan.baidu.com.py b/pan.baidu.com.py index b343c6c..668c2d6 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -1339,8 +1339,7 @@ def _share_transfer(self, info): if not info['isdir']: remote_file_path = '/'.join( - [info['remotepath'], os.path.split(info['path'])[-1]] - ) + [info['remotepath'], os.path.split(info['path'])[-1]] ) meta = self._meta([remote_file_path]) if meta: j = {'errno': 'file has exist'} @@ -1464,7 +1463,7 @@ def save_share(self, url, remotepath, infos=None): infos += self._get_share_list(info) break else: - print s % (1, 91, ' !! Error: can\'t transfer file') + print s % (1, 91, ' !! Error: can\'t transfer file'), result break elif result['errno'] == 'file has exist': print s % (1, 93, ' |-- file has exist.') @@ -3085,7 +3084,7 @@ def handle_command(comd, xxx): path = x._get_path(xxx[0]) remotepath = xxx[1].decode('utf8', 'ignore') infos = [] - if path != '/': + if path != '/' and path[0] == '/': infos.append( { 'isdir': 1, diff --git a/xiami.py b/xiami.py index 1f1ea68..ed0db37 100755 --- a/xiami.py +++ b/xiami.py @@ -922,7 +922,7 @@ def download(self, songs, amount_songs=u'1', n=1): print ' |--', s % (1, 97, 'MP3-Quality:'), s % (1, 91, 'Low') file_name_for_wget = file_name.replace('`', '\`') - cmd = 'wget -c -T 5 -nv ' \ + cmd = 'wget -c -nv ' \ '-U "%s" ' \ '--header "Referer:http://img.xiami.com' \ '/static/swf/seiya/1.4/player.swf?v=%s" ' \ From c93efd37696361a89cc4921c61588fb4666e59fd Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年4月11日 22:26:42 +0800 Subject: [PATCH 029/158] fix --- music.163.com.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/music.163.com.py b/music.163.com.py index cfc77c5..fa87467 100755 --- a/music.163.com.py +++ b/music.163.com.py @@ -369,10 +369,8 @@ def display_infos(self, i): def play(self, amount_songs, n=None): for i in self.song_infos: - r = ss.get(i['durl'], allow_redirects=False) - real_durl = r.headers['location'] self.display_infos(i) - cmd = 'mpv --really-quiet --audio-display no "%s"' % real_durl + cmd = 'mpv --really-quiet --audio-display no %s' % i['durl'] os.system(cmd) timeout = 1 ii, _, _ = select.select([sys.stdin], [], [], timeout) @@ -401,7 +399,6 @@ def download(self, amount_songs, n=None): else: ii += 1 continue - file_name_for_wget = file_name.replace('`', '\`') if not args.undownload: q = {'h': 'High', 'm': 'Middle', 'l': 'Low'} mp3_quality = q[i['mp3_quality']] @@ -415,10 +412,9 @@ def download(self, amount_songs, n=None): u' ++ mp3_quality: %s' \ % (n, amount_songs, col, s % (1, 91, mp3_quality))) - - r = ss.get(i['durl'], allow_redirects=False) - real_durl = r.headers['location'] - cmd = 'wget -c -nv -O "%s.tmp" %s' % (file_name_for_wget, real_durl) + 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']) cmd = cmd.encode('utf8') status = os.system(cmd) if status != 0: # other http-errors, such as 302. From 73369bc4a5a92dd4f5f98d1cbecbf5b090b4e718 Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年4月12日 13:53:32 +0800 Subject: [PATCH 030/158] fix [def upload] --- README.md | 36 ++++++++++++++++++++---------------- pan.baidu.com.py | 46 +++++++++++++++++++++++++++++++++++++--------- 2 files changed, 57 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index d89b838..977e802 100644 --- a/README.md +++ b/README.md @@ -266,20 +266,23 @@ p 或 play url1 url2 .. path1 path2 .. u 或 upload localpath remotepath # 加密上传 -u localpath remotepath -P password -t ec -R +u localpath remotepath [-P password] -t ec -R # 转存 s 或 save url remotepath [-s secret] # 下载 -d 或 download url1 url2 path1 path2 非递归下载 到当前目录(cwd) -d 或 download url1 url2 path1 path2 -R 递归下载 到当前目录(cwd) +d 或 download url1 url2 path1 path2 非递归下载 到当前本地目录 +d 或 download url1 url2 path1 path2 -R 递归下载 到当前本地目录 # !! 注意: -# d /path/to/download -R 递归下载 *download文件夹* 到当前目录(cwd) -# d /path/to/download/ -R 递归下载 *download文件夹中的文件* 到当前目录(cwd) +# d /path/to/download -R 递归下载 *download文件夹* 到当前本地目录 +# d /path/to/download/ -R 递归下载 *download文件夹中的文件* 到当前本地目录 # 下载并解密 -d /path/to/download -R -t dc -P password [-m aes-256-cfb] +d /path/to/download -R -t dc [-P password] [-m aes-256-cfb] + +# 解密已下载的文件 +dc path1 path2 -R [-P password] [-m aes-256-cfb] # 文件操作 md 或 mkdir path1 path2 创建文件夹 @@ -395,6 +398,7 @@ jca 或 jobclearall # 清除 *全部任务* -V, --VERIFY verification -v, --view view detail eg: + l -v # 显示绝对路径 a magnet /path -v # 离线下载并显示下载的文件 d -p url1 url2 -v # 显示播放文件的完整路径 l path1 path2 -vv # 显示文件的size, md5 @@ -409,7 +413,6 @@ jca 或 jobclearall # 清除 *全部任务* p -t m3 # 播放流媒体(m3u8) s -t c # 连续转存 (如果转存出错,再次运行命令 # 可以从出错的地方开始,用于转存大量文件时) - l -v # 显示绝对路径 l -t f # 文件 l -t d # 文件夹 l -t du # 查看文件占用空间 @@ -520,19 +523,19 @@ bp d url -s [secret] -a 10 # 下载并解码 ## 默认加密方法为 aes-256-cfb -bp d /path/to/encrypted_file -t dc -P password # 覆盖加密文件 (默认) -bp d /path/to/encrypted_file -t dc,no -P password # 不覆盖加密文件 +bp d /path/to/encrypted_file -t dc [-P password] # 覆盖加密文件 (默认) +bp d /path/to/encrypted_file -t dc,no [-P password] # 不覆盖加密文件 ## 设置加密方法 -bp d /path/to/encrypted_file -t dc -P password -m 'rc4-md5' -bp d /path/to/directory -t dc -P password -m 'rc4-md5' +bp d /path/to/encrypted_file -t dc [-P password] -m 'rc4-md5' +bp d /path/to/directory -t dc [-P password] -m 'rc4-md5' ``` #### 解码已下载的加密文件: ``` -bp dc /local/to/encrypted_file -P password -m 'aes-256-cfb' -bp dc /local/to/encrypted_file -P password -bp dc /local/to/directory -P password +bp dc /local/to/encrypted_file [-P password] -m 'aes-256-cfb' +bp dc /local/to/encrypted_file [-P password] +bp dc /local/to/directory [-P password] ``` #### 播放: @@ -620,8 +623,8 @@ bp u ~/Documents ~/Videos ~/Documents /backup -t r,e # 以上两种模式 #### 加密上传: (默认为非递归,递归加 -R) ``` -bp u ~/{p1,p2,p3} -t ec -P password # 默认加密方法 'aes-256-cfb' -bp u ~/{p1,p2,p3} -t ec -P password -m 'rc4-md5' +bp u ~/{p1,p2,p3} -t ec [-P password] # 默认加密方法 'aes-256-cfb' +bp u ~/{p1,p2,p3} -t ec [-P password] -m 'rc4-md5' # 注意: # 上传后的文件名会默认加上前缀 encrypted_ @@ -648,6 +651,7 @@ bp s 'http://pan.baidu.com/share/link?shareid=2705944270&uk=708312363' /path/to/ #### 搜索: ``` +# 默认搜索当前服务器工作目录 cwd bp f keyword1 keyword2 bp f "this is one keyword" "this is another keyword" /path/to/search diff --git a/pan.baidu.com.py b/pan.baidu.com.py index 668c2d6..073a3b3 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -1140,13 +1140,16 @@ def _upload_file(self, lpath, rpath): } if self.toEncrypt: - self._init_cipherer() + self._init_cipherer(toencrypt=True) self.upload_datas[lpath]['is_over'] = False - self.upload_datas[lpath]['remotepaths'] = set() + #self.upload_datas[lpath]['remotepaths'] = set() self.upload_datas[lpath]['slice_md5s'] = [] if 'e' in args.type_: - path = os.path.join(rpath, os.path.basename(lpath)) + if 'ec' in args.type_ and not 'np' in args.type_: + path = os.path.join(rpath, 'encrypted_' + os.path.basename(lpath)) + else: + path = os.path.join(rpath, os.path.basename(lpath)) meta = self._meta([path]) if meta: self.upload_datas[lpath]['is_over'] = True @@ -1279,16 +1282,15 @@ def _upload_dir(self, lpath, rpath): self._upload_file(localpath, remotepath) if not args.recursive: break - def _init_cipherer(self): + def _init_cipherer(self, toencrypt=False): method = args.mode if method not in CIPHERS: method = 'aes-256-cfb' - pwd = args.passwd - if not pwd: - print s % (1, 91, ' !! missing Password.\n'), ' -P password' + if not args.passwd: + print s % (1, 91, ' !! missing Password.\n') sys.exit(1) - self._cipherer = encrypt.Encryptor(pwd, method) + self._cipherer = encrypt.Encryptor(args.passwd, method) def upload(self, localpaths, remotepath): remotepath = make_server_path(self.cwd, remotepath) @@ -2645,11 +2647,15 @@ def share(self, paths, pwd): self._share(paths, pwd) def decrypt(self, paths): + def init_decrypted_file(path): + open(path, 'w').close() + def store(path, decrypted_block): with open(path, 'ab') as g: g.write(decrypted_block) def do(file): + init_decrypted_file(file + '.decrypt') self._init_cipherer() encrypted_file = open(file, 'rb') block = encrypted_file.read(100) @@ -2884,7 +2890,7 @@ def handle_args(argv): p.add_argument('-l', '--limit', action='store', \ default=None, type=str, help='下载速度限制,eg: -l 100k') p.add_argument('-P', '--passwd', action='store', \ - default=None, type=str, help='设置分享密码,eg: -P pawd') + default=None, type=str, help='设置密码,eg: -P pawd') # for upload p.add_argument('-m', '--mode', action='store', \ default='c', type=str, choices=['o', 'c'] + CIPHERS, \ @@ -2920,6 +2926,21 @@ def handle_args(argv): args.comd = comd return comd, xxx +def enter_password(): + if not args.passwd: + from getpass import getpass + if 'ec' in args.type_: + while True: + pwd1 = getpass(s % (2, 97, 'Password: ')) + pwd2 = getpass(s % (2, 97, 'verify Password: ')) + if pwd1 == pwd2: + args.passwd = pwd1 + break + else: + print s % (2, 91, '! Passwords do not match.') + elif 'dc' in args.type_: + args.passwd = getpass(s % (2, 97, 'Password: ')) + def handle_command(comd, xxx): if comd == 'login' or comd == 'g': from getpass import getpass @@ -2999,6 +3020,8 @@ def handle_command(comd, xxx): sys.exit(1) global px + enter_password() + px = panbaiducom_HOME() px.init() px.upload(xxx[:-1], xxx[-1]) @@ -3038,6 +3061,8 @@ def handle_command(comd, xxx): if comd == 'p' or comd == 'play': args.play = True + enter_password() + paths = xxx paths1 = [] paths2 = [] @@ -3349,6 +3374,9 @@ def handle_command(comd, xxx): x.jobclearall() elif comd == 'dc' or comd == 'decrypt': + if 'dc' not in args.type_: args.type_.append('dc') + enter_password() + x = panbaiducom_HOME() x.init() x.decrypt(xxx) From 2ef23738a067df8a363b66d246cf07d5db45becb Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年4月12日 21:39:19 +0800 Subject: [PATCH 031/158] fix [def get_songs -- year] --- xiami.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xiami.py b/xiami.py index ed0db37..85c4300 100755 --- a/xiami.py +++ b/xiami.py @@ -490,8 +490,8 @@ def get_songs(self, album_id, song_id=None): t = re.search(r'"/artist/\d+.+?>(.+?)<', html1).group(1) artist_name = modificate_text(t) - t = re.findall(u'(\d+)年(\d+)月(\d+)', html1)[0] - year = '-'.join(t) + t = re.findall(u'(\d+)年(\d+)月(\d+)', html1) + year = '-'.join(t[0]) if t else '' album_description = '' t = re.search(u'专辑介绍:(.+?)
', From ad164d1cbedeeca44aaebeaf5cdbc37e3d2b3394 Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年4月14日 10:15:37 +0800 Subject: [PATCH 032/158] missing me --- README.md | 78 +++++++++++++++++++++++++++---------------------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 977e802..ba89b6d 100644 --- a/README.md +++ b/README.md @@ -79,9 +79,9 @@ logintaobao username password signout # 退出登录 -d 或 download url1 url2 .. # 下载 -p 或 play url1 url2 .. # 播放 -s 或 save url1 url2 .. # 收藏 +d 或 download url1 url2 # 下载 +p 或 play url1 url2 # 播放 +s 或 save url1 url2 # 收藏 ``` #### 参数: @@ -260,7 +260,7 @@ cwd cd path # 支持 ./../... # 播放 -p 或 play url1 url2 .. path1 path2 .. +p 或 play url1 url2 path1 path2 # 上传 u 或 upload localpath remotepath @@ -364,7 +364,7 @@ l path1 path2 -t e,d l path1 path2 -t ne,d # 分享文件 -S 或 share path1 path2 .. 为每个提供的文件路劲创建分享链接 +S 或 share path1 path2 为每个提供的文件路劲创建分享链接 S 或 share [-P pawd 或 --passwd pawd] path1 path2 为每个提供的路径创建加密的分享链接 # 查看文件占用空间 @@ -429,10 +429,10 @@ jca 或 jobclearall # 清除 *全部任务* -m {o,c}, --mode {o,c} 模式: o # 重新上传. c # 连续上传. 加密方法: https://github.com/shadowsocks/shadowsocks/wiki/Encryption -R, --recursive 递归, 用于download, play, ls, find, rmre, rnre, rmre, cpre --H HEADS, --head HEADS 匹配开头的字符,eg: -H Head1 Head2 .. --T TAILS, --tail TAILS 匹配结尾的字符,eg: -T Tail1 Tail2 .. --I INCLUDES, --include INCLUDES 不排除匹配到表达的文件名, 可以是正则表达式,eg: -I "*.mp3" "*.avi" .. --E EXCLUDES, --exclude EXCLUDES 排除匹配到表达的文件名, 可以是正则表达式,eg: -E "*.html" "*.jpg" .. +-H HEADS, --head HEADS 匹配开头的字符,eg: -H Head1 Head2 +-T TAILS, --tail TAILS 匹配结尾的字符,eg: -T Tail1 Tail2 +-I INCLUDES, --include INCLUDES 不排除匹配到表达的文件名, 可以是正则表达式,eg: -I "*.mp3" "*.avi" +-E EXCLUDES, --exclude EXCLUDES 排除匹配到表达的文件名, 可以是正则表达式,eg: -E "*.html" "*.jpg" -c {on, off}, --ls_color {on, off} ls 颜色,默认是on # -t, -H, -T, -I, -E 都能用于 download, play, ls, find, rnre, rmre, cpre, mvre @@ -495,23 +495,23 @@ bp cd ... bp d . -R # 下载自己网盘中的*单个或多个文件* -bp d http://pan.baidu.com/disk/home#dir/path=/path/to/filename1 http://pan.baidu.com/disk/home#dir/path=/path/to/filename2 .. +bp d http://pan.baidu.com/disk/home#dir/path=/path/to/filename1 http://pan.baidu.com/disk/home#dir/path=/path/to/filename2 # or -bp d /path/to/filename1 /path/to/filename2 .. +bp d /path/to/filename1 /path/to/filename2 # 递归下载自己网盘中的*单个或多个文件夹* -bp d -R http://pan.baidu.com/disk/home#dir/path=/path/to/directory1 http://pan.baidu.com/disk/home#dir/path=/path/to/directory2 .. +bp d -R http://pan.baidu.com/disk/home#dir/path=/path/to/directory1 http://pan.baidu.com/disk/home#dir/path=/path/to/directory2 # or -bp d -R /path/to/directory1 /path/to/directory2 .. +bp d -R /path/to/directory1 /path/to/directory2 # 递归下载后缀为 .mp3 的文件 -bp d -R /path/to/directory1 /path/to/directory2 .. -T .mp3 +bp d -R /path/to/directory1 /path/to/directory2 -T .mp3 # 非递归下载 -bp d relative_path/to/directory1 /path/to/directory2 .. +bp d relative_path/to/directory1 /path/to/directory2 # 下载别人分享的*单个文件* -bp d http://pan.baidu.com/s/1o6psfnxx .. -bp d 'http://pan.baidu.com/share/link?shareid=1622654699&uk=1026372002&fid=2112674284' .. +bp d http://pan.baidu.com/s/1o6psfnxx +bp d 'http://pan.baidu.com/share/link?shareid=1622654699&uk=1026372002&fid=2112674284' # 下载别人加密分享的*单个文件*,密码参数-s bp d http://pan.baidu.com/s/1i3FVlw5 -s vuej @@ -563,8 +563,8 @@ bp a ftp://ftp.netscape.com/testfile /path/to/save bp a 'magnet:?xt=urn:btih:64b7700828fd44b37c0c045091939a2c0258ddc2' /path/to/save -v -t a bp a 'ed2k://|file|[美]徐中約《中国近代史》第六版原版PDF.rar|547821118|D09FC5F70DEA63E585A74FBDFBD7598F|/' /path/to/save -bp a /path/to/a.torrent .. -v -t m,i # 使用网盘中torrent,下载到/path/to -# 注意 --------------------- +bp a /path/to/a.torrent -v -t m,i # 使用网盘中torrent,下载到/path/to +# 注意 ------------------ ↓ 网盘中的torrent ``` @@ -590,8 +590,8 @@ bp a /download/a.torrent -v -t m,i,d # 使用网盘中torrent,下载到/dow bp j bp j 3482938 8302833 bp jd -bp jc taskid1 taskid2 .. -bp jc 1208382 58239221 .. +bp jc taskid1 taskid2 +bp jc 1208382 58239221 bp jca ``` @@ -610,7 +610,7 @@ bp u ~/Documents/reading/三体\ by\ 刘慈欣.mobi /doc -m o # -m c --> 续传 (默认) # 递归加-R -bp u ~/Videos/*.mkv ../videos -t r +bp u ~/Videos/*.mkv /videos -t r # 只进行rapidupload bp u ~/Documents ~/Videos ~/Documents /backup -t e -R @@ -672,8 +672,8 @@ bp fnn keyword1 keyword2 非递归搜索 by name 反序 bp f mp3 /path/to/search -H "[" "01" -T ".tmp" -I ".*-.*" -R # 搜索所有的账户 -bp f iDoNotKnow .. [directory] -t all -R -bp f archlinux ubuntu .. [directory] -t f,all -T .iso -R +bp f iDoNotKnow [directory] -t all -R +bp f archlinux ubuntu [directory] -t f,all -T .iso -R # 搜索 加 通道(只支持 donwload, play, rnre, rm, mv) bp f bioloy \| d -R 递归搜索后递归下载 @@ -755,26 +755,26 @@ http://magnet.vuze.com ``` # magnet 2 torrent -m 或 mt magnet_link1 magnet_link2 .. [-d /path/to/save] +m 或 mt magnet_link1 magnet_link2 [-d /path/to/save] m -i /there/are/files -d new # torrent 2 magnet, 输出magnet -t 或 tm path1 path2 .. +t 或 tm path1 path2 # 过滤敏.感.词 # 有2种模式 # -t n (默认) 用数字替换文件名 # -t be64 用base64加密文件名,torrent用百度下载后,可用 pan.baidu.com.py rnr /path -t f,bd64 改回原名字 -c 或 ct magnet_link1 magnet_link2 .. /path/to/torrent1 /path/to/torrent2 .. [-d /path/to/save] +c 或 ct magnet_link1 magnet_link2 /path/to/torrent1 /path/to/torrent2 [-d /path/to/save] c -i /there/are/files and_other_dir -d new # 从文件或文件夹中寻找 magnet,再过滤 # 过滤敏.感.词 - 将magnet或torrent转成不敏感的 torrent # /path/to/save 默认为 . # 用base64加密的文件名: -c magnet_link1 magnet_link2 .. /path/to/torrent1 /path/to/torrent2 .. [-d /path/to/save] -t be64 +c magnet_link1 magnet_link2 /path/to/torrent1 /path/to/torrent2 [-d /path/to/save] -t be64 # 使用正则表达式过滤敏.感.词 -cr 或 ctre foo bar magnet_link1 /path/to/torrent1 .. [-d /path/to/save] +cr 或 ctre foo bar magnet_link1 /path/to/torrent1 [-d /path/to/save] # foo bar 都是 regex ``` @@ -787,7 +787,7 @@ cr 或 ctre foo bar magnet_link1 /path/to/torrent1 .. [-d /path/to/save] -t be64 用base64加密文件名,torrent用百度下载后,可用 pan.baidu.com.py rnr /path -t f,bd64 改回原名字 -d DIRECTORY, --directory DIRECTORY 指定torrents的保存路径, eg: -d /path/to/save -n NAME, --name NAME 顶级文件夹名称, eg: -m thistopdirectory --i localpath1 localpath2 .., --import_from localpath1 localpath2 .. 从本地文本文件导入magnet (用正则表达式匹配) +-i localpath1 localpath2, --import_from localpath1 localpath2 从本地文本文件导入magnet (用正则表达式匹配) ``` #### 3. 用法 @@ -795,17 +795,17 @@ cr 或 ctre foo bar magnet_link1 /path/to/torrent1 .. [-d /path/to/save] bt 是bt.py的马甲 (alias bt='python2 /path/to/bt.py') ``` -bt mt magnet_link1 magnet_link2 .. [-d /path/to/save] -bt tm path1 path2 .. -bt ct magnet_link1 path1 .. [-d /path/to/save] +bt mt magnet_link1 magnet_link2 [-d /path/to/save] +bt tm path1 path2 +bt ct magnet_link1 path1 [-d /path/to/save] -bt m magnet_link1 magnet_link2 .. [-d /path/to/save] -bt t path1 path2 .. -bt c magnet_link1 path1 .. [-d /path/to/save] +bt m magnet_link1 magnet_link2 [-d /path/to/save] +bt t path1 path2 +bt c magnet_link1 path1 [-d /path/to/save] # 用torrage.com -bt m magnet_link1 path1 .. -p 127.0.0.1:8087 -bt c magnet_link1 path1 .. -p 127.0.0.1:8087 +bt m magnet_link1 path1 -p 127.0.0.1:8087 +bt c magnet_link1 path1 -p 127.0.0.1:8087 # 从文件或文件夹中寻找 magnet,再过滤 bt c -i ~/Downloads -d new @@ -1221,7 +1221,7 @@ tm cool #### 用法 ``` -python2 unzip.py azipfile1.zip azipfile2.zip .. +python2 unzip.py azipfile1.zip azipfile2.zip python2 unzip.py azipfile.zip -s secret # -s 密码 ``` From cd6a0ac232959c576b4dce11ca2901af0629d5a0 Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年4月14日 13:48:35 +0800 Subject: [PATCH 033/158] =?UTF-8?q?fix=20[def=20=5Fmeta]=20--=20error=203:?= =?UTF-8?q?"=E4=B8=80=E6=AC=A1=E6=93=8D=E4=BD=9C=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E4=B8=8D=E5=8F=AF=E8=B6=85=E8=BF=87100=E4=B8=AA"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pan.baidu.com.py | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index 073a3b3..ed6f215 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -933,18 +933,27 @@ def _meta(self, file_list, dlink=0): "blocks": 0, # 0 or 1 #"bdstoken": self._get_bdstoken() } - data = {'target': json.dumps(file_list)} url = 'http://pan.baidu.com/api/filemetas' + i = 0 + j = {} while True: - try: - r = ss.post(url, params=p, data=data) - j = r.json() - if j['errno'] == 0: - return j - else: - return False - except Exception: - time.sleep(1) + fl = file_list[i:i+100] + if fl: + data = {'target': json.dumps(fl)} + try: + r = ss.post(url, params=p, data=data) + js = r.json() + if js['errno'] == 0 and i == 0: + j = js + elif js['errno'] == 0: + j['info'].append(js['info']) + else: + return False + except Exception: + time.sleep(1) + else: + return j + i += 100 ################################################################ # for upload @@ -1819,7 +1828,7 @@ def warn(comd, display=True): if not warn('move', display=True): return paths = [i['path'].encode('utf8') for i in infos] remotepath = pipe[1] - self.move(paths, remotepath) + self.move(paths, remotepath, check=False) else: print s % (1, 91, ' !! command is supported by download, play, rnre, rm, mv') @@ -1901,7 +1910,8 @@ def ls(self, order, desc, paths): def _exist(self, list_): meta = self._meta(list_) if not meta: - print s % (1, 91, ' !! Error at _exist, some paths are not existed.'), list_ + print s % (1, 91, ' !! Error at _exist, some paths are not existed.'), \ + list_ if len(list_) <= 10 else '' sys.exit(1) def _filemanager(self, opera, data): @@ -1922,10 +1932,10 @@ def _filemanager(self, opera, data): else: print s % (1, 91, ' !! Error at filemanager') - def move(self, paths, remotepath): + def move(self, paths, remotepath, check=True): paths = [ make_server_path(self.cwd, path) for path in paths ] remotepath = make_server_path(self.cwd, remotepath) - self._exist(paths) + if check: self._exist(paths) meta = self._meta([remotepath]) if not meta: @@ -2137,7 +2147,7 @@ def _rmcre_do(self, type, infos, todir=None): if type == 'remove': self.remove(paths) if type == 'move': - self.move(paths, todir) + self.move(paths, todir, check=False) elif type == 'copy': self.copy(paths, todir) else: From 00ecc47d72837a17f1680e4028679f67dc674d71 Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年4月16日 15:02:11 +0800 Subject: [PATCH 034/158] pick an random color from 0 to 7 --- 115.py | 4 ++-- flvxz_cl.py | 4 ++-- pan.baidu.com.py | 4 ++-- tumblr.py | 2 +- xiami.py | 2 +- yunpan.360.cn.py | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/115.py b/115.py index e3bcffd..8cb93b7 100755 --- a/115.py +++ b/115.py @@ -261,7 +261,7 @@ def download(infos): if os.path.exists(infos['file']): return 0 - num = random.randint(0, 7) % 7 + num = random.randint(0, 7) % 8 col = s % (2, num + 90, infos['file']) infos['nn'] = infos['nn'] if infos.get('nn') else 1 infos['total_file'] = infos['total_file'] \ @@ -305,7 +305,7 @@ def download(infos): @staticmethod def play(infos): - num = random.randint(0, 7) % 7 + num = random.randint(0, 7) % 8 col = s % (2, num + 90, infos['name']) infos['nn'] = infos['nn'] if infos.get('nn') else 1 infos['total_file'] = infos['total_file'] \ diff --git a/flvxz_cl.py b/flvxz_cl.py index 53c7efa..942adf9 100755 --- a/flvxz_cl.py +++ b/flvxz_cl.py @@ -57,7 +57,7 @@ def download(infos): #if os.path.exists(infos['filename']): #return 0 - num = random.randint(0, 7) % 7 + num = random.randint(0, 7) % 8 col = s % (2, num + 90, os.path.basename(infos['filename'])) print '\n ++ 正在下载:', '#', \ s % (1, 97, infos['n']), '/', \ @@ -78,7 +78,7 @@ def download(infos): sys.exit(1) def play(infos): - num = random.randint(0, 7) % 7 + num = random.randint(0, 7) % 8 col = s % (2, num + 90, os.path.basename(infos['filename'])) print '\n ++ play:', '#', \ s % (1, 97, infos['n']), '/', \ diff --git a/pan.baidu.com.py b/pan.baidu.com.py index ed6f215..75702a3 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -810,7 +810,7 @@ def _download_do(infos): if os.path.exists(infos['file']): return - num = random.randint(0, 7) % 7 + num = random.randint(0, 7) % 8 col = sizeof_fmt(infos['size']) + ' # ' + s % (2, num + 90, infos['path']) \ if args.view else s % (2, num + 90, infos['name']) infos['nn'] = infos['nn'] if infos.get('nn') else 1 @@ -857,7 +857,7 @@ def _download_do(infos): @staticmethod def _play_do(infos): - num = random.randint(0, 7) % 7 + num = random.randint(0, 7) % 8 col = sizeof_fmt(infos['size']) \ + ' # ' \ + s % (2, num + 90, infos['path']) \ diff --git a/tumblr.py b/tumblr.py index 7654e25..1bb6bfd 100755 --- a/tumblr.py +++ b/tumblr.py @@ -179,7 +179,7 @@ def download(self): def run(i): if os.path.exists(i['filepath']): return - num = random.randint(0, 7) % 7 + num = random.randint(0, 7) % 8 col = s % (1, num + 90, i['filepath']) print ' ++ download: %s' % col cmd = [ diff --git a/xiami.py b/xiami.py index 85c4300..4456049 100755 --- a/xiami.py +++ b/xiami.py @@ -886,7 +886,7 @@ def download(self, songs, amount_songs=u'1', n=1): ii = 1 for i in songs: - num = random.randint(0, 100) % 7 + num = random.randint(0, 100) % 8 col = s % (2, num + 90, i['file_name']) t = modificate_file_name_for_wget(i['file_name']) file_name = os.path.join(dir_, t) diff --git a/yunpan.360.cn.py b/yunpan.360.cn.py index 55ede34..807cdc4 100755 --- a/yunpan.360.cn.py +++ b/yunpan.360.cn.py @@ -228,7 +228,7 @@ def download(infos): if os.path.exists(infos['file']): return 0 - num = random.randint(0, 7) % 7 + num = random.randint(0, 7) % 8 col = s % (2, num + 90, infos['file']) infos['nn'] = infos['nn'] if infos.get('nn') else 1 infos['total_file'] = infos['total_file'] if infos.get('total_file') else 1 From 72befef76f8837fc16dbe82dec2dfa89e2310cf4 Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年4月16日 15:02:56 +0800 Subject: [PATCH 035/158] https fucks gfw --- tumblr.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tumblr.py b/tumblr.py index 1bb6bfd..48dbd95 100755 --- a/tumblr.py +++ b/tumblr.py @@ -83,7 +83,7 @@ def save_json(self): def get_site_infos(self, postid=None): self.infos['photos'] = [] - self.url = 'http://api.tumblr.com/v2/blog/%s/posts/photo' \ + self.url = 'https://api.tumblr.com/v2/blog/%s/posts/photo' \ % self.infos['host'] params = { "offset": self.key if not postid else "", @@ -130,7 +130,7 @@ def get_site_infos(self, postid=None): def get_tag_infos(self): self.infos['photos'] = [] - self.url = 'http://api.tumblr.com/v2/tagged' + self.url = 'https://api.tumblr.com/v2/tagged' params = { "limit": 20, "type": "photo", @@ -186,7 +186,7 @@ def run(i): 'wget', '-c', '-q', '-O', '%s.tmp' % i['filepath'], '--user-agent', '"%s"' % headers['User-Agent'], - '%s' % i['durl'] + '%s' % i['durl'].replace('http:', 'https:') ] f = subprocess.Popen(cmd) return f, i['filepath'] From 6413e872b776298570457c5f527b2028e613d809 Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年4月20日 21:39:46 +0800 Subject: [PATCH 036/158] try to fix speed of downloading, using -t fs --- README.md | 15 ++++++++++++--- pan.baidu.com.py | 41 ++++++++++++++++++++++++++++++++--------- 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index ba89b6d..84624ce 100644 --- a/README.md +++ b/README.md @@ -191,6 +191,10 @@ shadowsocks # 用于加密上传。 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 ``` +#### other + +[解决百度网盘下载速度问题](https://github.com/PeterDing/iScript/wiki/解决百度网盘下载速度问题) + #### 2. 使用说明 pan.baidu.com.py 是一个百度网盘的命令行客户端。 @@ -406,6 +410,8 @@ jca 或 jobclearall # 清除 *全部任务* -f number, --from_ number 从第几个开始(用于download, play),eg: p /video -f 42 -t ext, --type_ ext 类型参数, 用 "," 分隔 eg: + -t fs # 换用下载服务器,用于下载、播放 + # 如果wiki中的速度解决方法不管用,可以试试加该参数 d -t dc # 下载并解密,覆盖加密文件(默认) d -t dc,no # 下载并解密,不覆盖加密文件 dc -t no # 解密,不覆盖加密文件 @@ -428,11 +434,11 @@ jca 或 jobclearall # 清除 *全部任务* -l amount, --limit amount 下载速度限制,eg: -l 100k -m {o,c}, --mode {o,c} 模式: o # 重新上传. c # 连续上传. 加密方法: https://github.com/shadowsocks/shadowsocks/wiki/Encryption --R, --recursive 递归, 用于download, play, ls, find, rmre, rnre, rmre, cpre +-R, --recursive 递归, 用于download, play, upload, ls, find, rmre, rnre, rmre, cpre -H HEADS, --head HEADS 匹配开头的字符,eg: -H Head1 Head2 -T TAILS, --tail TAILS 匹配结尾的字符,eg: -T Tail1 Tail2 --I INCLUDES, --include INCLUDES 不排除匹配到表达的文件名, 可以是正则表达式,eg: -I "*.mp3" "*.avi" --E EXCLUDES, --exclude EXCLUDES 排除匹配到表达的文件名, 可以是正则表达式,eg: -E "*.html" "*.jpg" +-I INCLUDES, --include INCLUDES 不排除匹配到表达的文件名, 可以是正则表达式,eg: -I ".*.mp3" ".*.avi" +-E EXCLUDES, --exclude EXCLUDES 排除匹配到表达的文件名, 可以是正则表达式,eg: -E ".*.html" ".*.jpg" -c {on, off}, --ls_color {on, off} ls 颜色,默认是on # -t, -H, -T, -I, -E 都能用于 download, play, ls, find, rnre, rmre, cpre, mvre @@ -491,6 +497,9 @@ bp cd ... #### 下载: ``` +## 下载、播放速度慢? +如果wiki中的速度解决方法不管用,可以试试加该参数 -t fs + # 下载当前工作目录 (递归) bp d . -R diff --git a/pan.baidu.com.py b/pan.baidu.com.py index 75702a3..48f63f5 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -156,7 +156,7 @@ def cd(cwd, part): cwd = os.path.join(cwd, part) return cwd - if path[0] == '/': + if not path or path[0] == '/': return path else: parts = path.split('/') @@ -164,6 +164,22 @@ def cd(cwd, part): cwd = cd(cwd, p) return cwd +def fast_pcs_server(j): + if 'fs' not in args.type_: + return j + + do = lambda dlink: \ + re.sub(r'://[^/]+?/', '://www.baidupcs.com/', dlink) + #re.sub(r'://[^/]+?/', '://c.pcs.baidu.com/', dlink) + + if isinstance(j, dict) and j.get('info') and len(j['info'])> 0: + for i in xrange(len(j['info'])): + if j['info'][i].get('dlink'): + j['info'][i]['dlink'] = do(j['info'][i]['dlink']) + else: + j = do(j) + return j + # https://stackoverflow.com/questions/1094841/reusable-library-to-get-human-readable-version-of-file-size def sizeof_fmt(num): for x in ['B','KB','MB','GB']: @@ -374,8 +390,9 @@ def login(self, username, password): def save_cookies(self, username=None, on=0, tocwd=False): if not username: username = self.user accounts = self.accounts - accounts[username] = {} - accounts[username]['cookies'] = ss.cookies.get_dict() + accounts[username] = accounts.get(username, {}) + accounts[username]['cookies'] = \ + accounts[username].get('cookies', ss.cookies.get_dict()) accounts[username]['on'] = on quota = self._get_quota() capacity = '%s/%s' % (sizeof_fmt(quota['used']), sizeof_fmt(quota['total'])) @@ -818,6 +835,7 @@ def _download_do(infos): print '\n ++ download: #', s % (1, 97, infos['nn']), '/', \ s % (1, 97, infos['total_file']), '#', col + #cookie = 'BDUSS=%s' % ss.cookies.get('BDUSS') if args.aria2c: quiet = ' --quiet=true' if args.quiet else '' taria2c = ' -x %s -s %s' % (args.aria2c, args.aria2c) @@ -944,8 +962,12 @@ def _meta(self, file_list, dlink=0): r = ss.post(url, params=p, data=data) js = r.json() if js['errno'] == 0 and i == 0: + if dlink: + js = fast_pcs_server(js) j = js elif js['errno'] == 0: + if dlink: + js = fast_pcs_server(js) j['info'].append(js['info']) else: return False @@ -1930,7 +1952,7 @@ def _filemanager(self, opera, data): elif j['errno'] == 12: print s % (1, 91, ' !! Error at filemanager:'), "部分文件已存在于目标文件夹中" else: - print s % (1, 91, ' !! Error at filemanager') + print s % (1, 91, ' !! Error at filemanager'), j def move(self, paths, remotepath, check=True): paths = [ make_server_path(self.cwd, path) for path in paths ] @@ -2023,7 +2045,7 @@ def rename(self, path, remotepath): s % (1, 91, 'is existed.') sys.exit(1) - base_dir = os.path.split(remotepath)[0] + base_dir, newname = os.path.split(remotepath) meta = self._meta([base_dir]) if not meta: self._make_dir(base_dir) @@ -2035,7 +2057,7 @@ def rename(self, path, remotepath): t = [{ 'path': path, 'dest': base_dir, - 'newname': os.path.basename(remotepath) + 'newname': newname }] data = 'filelist=' + urllib.quote_plus(json.dumps(t)) self._filemanager('move', data) @@ -2793,7 +2815,8 @@ def get_infos(self): r = ss.post(url, data=data, params=self.params) j = r.json() if not j['errno']: - self.infos['dlink'] = j['dlink'].encode('utf8') + dlink = fast_pcs_server(j['dlink'].encode('utf8')) + self.infos['dlink'] = dlink if args.play: panbaiducom_HOME._play_do(self.infos) else: @@ -2815,7 +2838,7 @@ def get_infos2(self, path): 'name': name, 'file': os.path.join(os.getcwd(), name), 'dir_': os.getcwd(), - 'dlink': dlink.group(1) + 'dlink': fast_pcs_server(dlink.group(1)) } if args.play: panbaiducom_HOME._play_do(self.infos) @@ -2846,7 +2869,7 @@ def do4(self, paths): 'name': name, 'file': os.path.join(os.getcwd(), name), 'dir_': os.getcwd(), - 'dlink': path + 'dlink': fast_pcs_server(path) } if args.play: From b10d94cdc69776ffb8e2eaf0d2d0473df68372f8 Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年4月22日 23:39:45 +0800 Subject: [PATCH 037/158] add is_wensintishi --- pan.baidu.com.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index 48f63f5..376746d 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -180,6 +180,14 @@ def fast_pcs_server(j): j = do(j) return j +def is_wenxintishi(dlink): + r = ss.get(dlink, stream=True) + url = r.url + if 'wenxintishi' in url: + return True + else: + return False + # https://stackoverflow.com/questions/1094841/reusable-library-to-get-human-readable-version-of-file-size def sizeof_fmt(num): for x in ['B','KB','MB','GB']: @@ -719,7 +727,7 @@ def _get_m3u8(self, info): url = "https://pcs.baidu.com/rest/2.0/pcs/file" - r = ss.get(url, params=p) + r = ss.get(url, params=p, verify=VERIFY) m3u8 = r.content if 'baidupcs.com/video' in m3u8: return m3u8 @@ -887,6 +895,9 @@ def _play_do(infos): % (s % (1, 92, ' m3u8') if infos.get('m3u8') else ''), \ s % (1, 97, infos['nn']), '/', \ s % (1, 97, infos['total_file']), '#', col + if is_wenxintishi(infos['dlink']): + print s % (1, 93, ' !! 百度8秒 !!') + return if not infos.get('m3u8'): if os.path.splitext(infos['file'])[-1].lower() == '.wmv': From cd694c73c5f5af155940cd9653b23c9b31c6c9f7 Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年4月25日 22:04:01 +0800 Subject: [PATCH 038/158] [91porn] exchange ss.get to requests.get --- 91porn.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/91porn.py b/91porn.py index 53c84be..4ee5735 100755 --- a/91porn.py +++ b/91porn.py @@ -60,15 +60,19 @@ def get_infos(self): params = { 'VID': n1.group(1), - 'mp4': 1, + 'mp4': '1', 'seccode': n2.group(1), 'max_vid': n3.group(1), } - r = ss.get(apiurl, params=params) + #tapiurl = apiurl + '?' + \ + #'&'.join(['='.join(item) for item in params.items()]) + #print tapiurl + + r = requests.get(apiurl, params=params) if r.ok: dlink = re.search( - r'file=(http.+?\.mp4)', r.content).group(1) + r'file=(http.+?)&', r.content).group(1) name = re.search( r'viewkey=([\d\w]+)', self.url).group(1) infos = { From bdea64926ff27aa4503d9562ffb00e9ab90dc2e6 Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年4月25日 22:06:23 +0800 Subject: [PATCH 039/158] [pan.baidu.com] fix: find then play --- pan.baidu.com.py | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index 376746d..18e5576 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -181,7 +181,12 @@ def fast_pcs_server(j): return j def is_wenxintishi(dlink): - r = ss.get(dlink, stream=True) + while True: + try: + r = ss.get(dlink, stream=True) + break + except requests.exceptions.ConnectionError: + time.sleep(2) url = r.url if 'wenxintishi' in url: return True @@ -729,10 +734,10 @@ def _get_m3u8(self, info): r = ss.get(url, params=p, verify=VERIFY) m3u8 = r.content - if 'baidupcs.com/video' in m3u8: - return m3u8 - else: + if '#EXTM3U' not in m3u8[:7]: return None + #m3u8 = fast_pcs_server(m3u8) + return m3u8 def download(self, paths): for path in paths: @@ -756,12 +761,12 @@ def download(self, paths): if args.play: j['list'] = [ i for i in j['list'] \ - if not i['isdir'] \ - and os.path.splitext( - i['server_filename'] - )[-1].lower() in mediatype] - if 's' in args.type_: - j['list'] = self._sift(j['list']) + if not i['isdir'] \ + and os.path.splitext( + i['server_filename'] + )[-1].lower() in mediatype] + if 's' in args.type_: + j['list'] = self._sift(j['list']) if args.heads or args.tails or args.includes \ or args.excludes: @@ -818,6 +823,9 @@ def download(self, paths): 'name': meta['info'][0]['server_filename'].encode('utf8'), 'size': meta['info'][0]['size'], } + if args.play: + if not os.path.splitext(infos['name'])[-1].lower() in mediatype: + continue self._download_do(infos) if 'dc' in args.type_: self.decrypt([infos['file']]) From 67d9772907854d122a54d122f5ac7113d2d0c0d2 Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年4月27日 11:24:41 +0800 Subject: [PATCH 040/158] [pan.baidu.com] change User-Agent for aria2, wget; #4 --- pan.baidu.com.py | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index 18e5576..121da6b 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -113,8 +113,8 @@ "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 i686) AppleWebKit/537.36 "\ - "(KHTML, like Gecko) Chrome/32.0.1700.77 Safari/537.36" + "User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 " \ + "(KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36" } ss = requests.session() @@ -856,20 +856,24 @@ 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%s%s%s ' \ + #'--user-agent "netdisk;4.4.0.6;PC;PC-Windows;6.2.9200;WindowsBaiduYunGuanJia" ' \ + #'--header "Referer:http://pan.baidu.com/disk/home " ' \ + cmd = 'aria2c -c -k 1M%s%s%s ' \ '-o "%s.tmp" -d "%s" ' \ '--user-agent "netdisk;4.4.0.6;PC;PC-Windows;6.2.9200;WindowsBaiduYunGuanJia" ' \ - '--header "Referer:http://pan.baidu.com/disk/home" "%s"' \ - % (quiet, taria2c, tlimit, infos['name'], \ + '"%s"' \ + % (quiet, taria2c, tlimit, infos['name'], infos['dir_'], 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;4.4.0.6;PC;PC-Windows;6.2.9200;WindowsBaiduYunGuanJia" ' \ - '--header "Referer:http://pan.baidu.com/disk/home" "%s"' \ - % (quiet, tlimit, infos['file'], infos['dlink']) + '--user-agent "%s" ' \ + '--header "Referer:http://pan.baidu.com/disk/home" ' \ + '"%s"' \ + % (quiet, tlimit, infos['file'], + headers['User-Agent'], infos['dlink']) status = os.system(cmd) exit = True @@ -910,24 +914,25 @@ def _play_do(infos): if not infos.get('m3u8'): if os.path.splitext(infos['file'])[-1].lower() == '.wmv': quiet = ' -really-quiet' if args.quiet else '' + #'-http-header-fields "User-Agent:netdisk;4.4.0.6;PC;PC-Windows;6.2.9200;WindowsBaiduYunGuanJia" ' \ cmd = 'mplayer%s -cache 10000 ' \ - '-http-header-fields "user-agent:netdisk;4.4.0.6;PC;PC-Windows;6.2.9200;WindowsBaiduYunGuanJia" ' \ + '-http-header-fields "User-Agent:%s" ' \ '-http-header-fields "Referer:http://pan.baidu.com/disk/home" "%s"' \ - % (quiet, infos['dlink']) + % (quiet, headers['User-Agent'], infos['dlink']) else: quiet = ' --really-quiet' if args.quiet else '' cmd = 'mpv%s --no-ytdl --cache 10000 --cache-default 10000 ' \ - '--http-header-fields "user-agent:netdisk;4.4.0.6;PC;PC-Windows;6.2.9200;WindowsBaiduYunGuanJia" ' \ + '--http-header-fields "User-Agent:%s" ' \ '--http-header-fields "Referer:http://pan.baidu.com/disk/home" "%s"' \ - % (quiet, infos['dlink']) + % (quiet, headers['User-Agent'], infos['dlink']) else: with open('/tmp/tmp_pan.baidu.com.py.m3u8', 'w') as g: g.write(infos['m3u8']) quiet = ' --really-quiet' if args.quiet else '' cmd = 'mpv%s --cache 10000 --cache-default 10000 ' \ - '--http-header-fields "user-agent:netdisk;4.4.0.6;PC;PC-Windows;6.2.9200;WindowsBaiduYunGuanJia" ' \ + '--http-header-fields "User-Agent:%s" ' \ '--http-header-fields "Referer:http://pan.baidu.com/disk/home" "%s"' \ - % (quiet, '/tmp/tmp_pan.baidu.com.py.m3u8') + % (quiet, headers['User-Agent'], '/tmp/tmp_pan.baidu.com.py.m3u8') os.system(cmd) timeout = 1 From a99484e84538a525c27fe3afbc7fb91f85d05329 Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年4月27日 11:25:16 +0800 Subject: [PATCH 041/158] [91porn] add --proxy --- 91porn.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/91porn.py b/91porn.py index 4ee5735..358851c 100755 --- a/91porn.py +++ b/91porn.py @@ -140,6 +140,11 @@ def do(self): self.get_infos() def main(url): + if args.proxy: + ss.proxies = { + 'http': args.proxy, + 'https': args.proxy + } x = nrop19(url) x.do() @@ -153,5 +158,7 @@ def main(url): help='play with mpv') p.add_argument('-u', '--get_url', action='store_true', \ help='print download_url without download') + p.add_argument('--proxy', action='store', type=str, default=None, \ + help='print download_url without download') args = p.parse_args() main(args.url) From 6ff53033310619db5b43f658791f7085a63b5ea3 Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年4月28日 08:15:34 +0800 Subject: [PATCH 042/158] [pan.baidu.com] random = SystemRandom() --- pan.baidu.com.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index 121da6b..95a274f 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -12,7 +12,8 @@ import re import time import argparse -import random +from random import SystemRandom +random = SystemRandom() import select import base64 import md5 From 99519d4c790014a8a9023d85c79b7e509ce51b41 Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年4月28日 09:10:09 +0800 Subject: [PATCH 043/158] [pan.baidu.com] remove mplayer --- README.md | 4 +--- pan.baidu.com.py | 28 ++++++++-------------------- 2 files changed, 9 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 84624ce..1b40a60 100644 --- a/README.md +++ b/README.md @@ -181,8 +181,6 @@ requests-toolbelt (https://github.com/sigmavirus24/requests-toolbelt) mpv (http://mpv.io) -mplayer # 我的linux上mpv播放wmv出错,换用mplayer - # 可选依赖 shadowsocks # 用于加密上传。 # 用 python2 的 pip 安装 @@ -193,7 +191,7 @@ https://github.com/PeterDing/iScript/wiki/%E6%89%8B%E5%8A%A8%E8%A7%A3%E5%86%B3pa #### other -[解决百度网盘下载速度问题](https://github.com/PeterDing/iScript/wiki/解决百度网盘下载速度问题) +[尝试解决百度网盘下载速度问题](https://github.com/PeterDing/iScript/wiki/解决百度网盘下载速度问题) #### 2. 使用说明 diff --git a/pan.baidu.com.py b/pan.baidu.com.py index 95a274f..b59dea1 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -912,28 +912,16 @@ def _play_do(infos): print s % (1, 93, ' !! 百度8秒 !!') return - if not infos.get('m3u8'): - if os.path.splitext(infos['file'])[-1].lower() == '.wmv': - quiet = ' -really-quiet' if args.quiet else '' - #'-http-header-fields "User-Agent:netdisk;4.4.0.6;PC;PC-Windows;6.2.9200;WindowsBaiduYunGuanJia" ' \ - cmd = 'mplayer%s -cache 10000 ' \ - '-http-header-fields "User-Agent:%s" ' \ - '-http-header-fields "Referer:http://pan.baidu.com/disk/home" "%s"' \ - % (quiet, headers['User-Agent'], infos['dlink']) - else: - quiet = ' --really-quiet' if args.quiet else '' - cmd = 'mpv%s --no-ytdl --cache 10000 --cache-default 10000 ' \ - '--http-header-fields "User-Agent:%s" ' \ - '--http-header-fields "Referer:http://pan.baidu.com/disk/home" "%s"' \ - % (quiet, headers['User-Agent'], infos['dlink']) - else: + if infos.get('m3u8'): with open('/tmp/tmp_pan.baidu.com.py.m3u8', 'w') as g: g.write(infos['m3u8']) - quiet = ' --really-quiet' if args.quiet else '' - cmd = 'mpv%s --cache 10000 --cache-default 10000 ' \ - '--http-header-fields "User-Agent:%s" ' \ - '--http-header-fields "Referer:http://pan.baidu.com/disk/home" "%s"' \ - % (quiet, headers['User-Agent'], '/tmp/tmp_pan.baidu.com.py.m3u8') + infos['dlink'] = '/tmp/tmp_pan.baidu.com.py.m3u8' + + 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 "Referer:http://pan.baidu.com/disk/home" "%s"' \ + % (quiet, headers['User-Agent'], infos['dlink']) os.system(cmd) timeout = 1 From 8189ec957c4f98c366b9193307efeaa3255aa436 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Wed, 6 May 2015 11:45:34 +0800 Subject: [PATCH 044/158] [xiami] support luoo.net/music --- README.md | 6 ++++++ xiami.py | 34 ++++++++++++++++++++++++++++++++-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1b40a60..33ef4b1 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,8 @@ mpv (http://mpv.io) xiami.py 是一个虾米音乐的命令行(CLI)客户端。提供登录、下载、播放、收藏的功能。 +**提供对[落网 luoo.net](luoo.net)的分析** + 初次使用需要登录 xm login (原xiami账号) **支持淘宝账户** xm logintaobao @@ -131,6 +133,10 @@ xm d http://www.xiami.com/chart/index/c/2?spm=a1z1s.2943549.6827465.6.VrEAoY # 下载 风格 genre, radio xm d http://www.xiami.com/genre/detail/gid/2?spm=a1z1s.3057857.6850221.1.g9ySan xm d http://www.xiami.com/genre/detail/sid/2970?spm=a1z1s.3057857.6850221.4.pkepgt + +# 下载落网期刊 +# 分析落网期刊的音乐后,在虾米上搜索并下载 +xm d http://www.luoo.net/music/706 ``` #### 播放: diff --git a/xiami.py b/xiami.py index 4456049..fc1d3c5 100755 --- a/xiami.py +++ b/xiami.py @@ -476,6 +476,9 @@ def get_artist_id(url): elif code == 'r': self.download_genre_radio(url_genre) + elif 'luoo.net' in url: + self.hack_luoo(url) + else: print(s % (2, 91, u' 请正确输入虾米网址.')) @@ -829,6 +832,30 @@ def download_genre_radio(self, url_genre): self.disc_description_archives = {} n += 1 + def hack_luoo(self, url): + # parse luoo.net + theaders = headers + theaders.pop('Referer') + r = requests.get(url) + if not r.ok: + return None + cn = r.content + songs_info = re.findall(r'

(.+?)

\s+' + r'

(.+?)

\s+' + r'

(.+?)

', 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 = ss.get(url) + if not r.ok and r.content == null: + print s % (1, 93, ' !! no find:'), ' - '.join(info) + return None + j = r.json() + self.song_id = j[0]['id'] + self.download_song() + def display_infos(self, i, nn, n): print '\n ----------------' print '>>', n, '/', nn @@ -922,12 +949,13 @@ def download(self, songs, amount_songs=u'1', n=1): print ' |--', s % (1, 97, 'MP3-Quality:'), s % (1, 91, 'Low') file_name_for_wget = file_name.replace('`', '\`') - cmd = 'wget -c -nv ' \ + quiet = ' -q' if args.quiet else ' -nv' + cmd = 'wget -c%s ' \ '-U "%s" ' \ '--header "Referer:http://img.xiami.com' \ '/static/swf/seiya/1.4/player.swf?v=%s" ' \ '-O "%s.tmp" %s' \ - % (headers['User-Agent'], int(time.time()*1000), + % (quiet, headers['User-Agent'], int(time.time()*1000), file_name_for_wget, durl) cmd = cmd.encode('utf8') status = os.system(cmd) @@ -1011,6 +1039,8 @@ def main(argv): help='play with mpv') p.add_argument('-l', '--low', action='store_true', \ help='low mp3') + p.add_argument('-q', '--quiet', action='store_true', \ + help='quiet for download') p.add_argument('-f', '--from_', action='store', \ default=1, type=int, \ help='从第几个开始下载,eg: -f 42') From 597e82301326199d0bfc99f88b47b86be338af7a Mon Sep 17 00:00:00 2001 From: PeterDing Date: Wed, 6 May 2015 11:47:43 +0800 Subject: [PATCH 045/158] missing me --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 33ef4b1..46c1454 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ mpv (http://mpv.io) xiami.py 是一个虾米音乐的命令行(CLI)客户端。提供登录、下载、播放、收藏的功能。 -**提供对[落网 luoo.net](luoo.net)的分析** +**提供对[落网 luoo.net](http://www.luoo.net)的分析** 初次使用需要登录 xm login (原xiami账号) From 99da27220d13ac57a35c08f0145de3e6062c4552 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Wed, 6 May 2015 12:25:29 +0800 Subject: [PATCH 046/158] [xiami] fix hack_luoo --- xiami.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/xiami.py b/xiami.py index fc1d3c5..e9aa464 100755 --- a/xiami.py +++ b/xiami.py @@ -841,18 +841,18 @@ def hack_luoo(self, url): return None cn = r.content songs_info = re.findall(r'

(.+?)

\s+' - r'

(.+?)

\s+' - r'

(.+?)

', cn) + r'

Artist: (.+?)

\s+' + 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 = ss.get(url) - if not r.ok and r.content == null: - print s % (1, 93, ' !! no find:'), ' - '.join(info) - return None j = r.json() + if not r.ok or not j: + print s % (1, 93, ' !! no find:'), ' - '.join(info) + continue self.song_id = j[0]['id'] self.download_song() From e1cc03c4d46c9c8dadec213979aedadf450c6c3c Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年5月13日 16:52:00 +0800 Subject: [PATCH 047/158] [flv] change flvxz to flvgo --- README.md | 17 +++-- flv_cmd.py | 186 ++++++++++++++++++++++++++++++++++++++++++++++++ flvxz_cl.py | 199 ---------------------------------------------------- 3 files changed, 194 insertions(+), 208 deletions(-) create mode 100755 flv_cmd.py delete mode 100755 flvxz_cl.py diff --git a/README.md b/README.md index 46c1454..3bb9ca1 100644 --- a/README.md +++ b/README.md @@ -1121,8 +1121,8 @@ nm -p url --- - -### flvxz_cl.py - flvxz.com 视频解析 client - 支持下载、播放 + +### flv_cmd.py - 基于在线服务的视频解析 client - 支持下载、播放 #### 1. 依赖 @@ -1136,16 +1136,15 @@ mpv (http://mpv.io) #### 2. 使用说明 -flvxz.com 视频解析 +~~flvxz.com 视频解析~~ 不能用。 + +flvgo.com 视频解析 **不提供视频合并操作** #### 支持的网站: -""" -已知支持120个以上视频网站,覆盖大多数国内视频站点,少量国外视频站点 - """ - -- flvxz.com +http://flvgo.com/sites 关于播放操作: @@ -1171,12 +1170,12 @@ fl url -p #### 4. 相关脚本: +> https://github.com/soimort/you-get + > https://github.com/iambus/youku-lixian > https://github.com/rg3/youtube-dl -> https://github.com/soimort/you-get - --- diff --git a/flv_cmd.py b/flv_cmd.py new file mode 100755 index 0000000..48fb3d0 --- /dev/null +++ b/flv_cmd.py @@ -0,0 +1,186 @@ +#!/usr/bin/env python2 +# vim: set fileencoding=utf8 + +import re +import requests +import os +import sys +import argparse +import random +from HTMLParser import HTMLParser +import urllib +import select + +s = '\x1b[%d;%dm%s\x1b[0m' # terminual color template +parser = HTMLParser() + +############################################################ +# wget exit status +wget_es = { + 0: "No problems occurred.", + 2: "User interference.", + 1<<8: "Generic error code.", + 2<<8: "Parse error - for instance, \ + when parsing command-line optio.wgetrc or .netrc...", + 3<<8: "File I/O error.", + 4<<8: "Network failure.", + 5<<8: "SSL verification failure.", + 6<<8: "Username/password authentication failure.", + 7<<8: "Protocol errors.", + 8<<8: "Server issued an error response." +} +############################################################ + +headers = { + "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) " \ + "AppleWebKit/537.36 (KHTML, like Gecko) " \ + "Chrome/40.0.2214.91 Safari/537.36", + "Accept": "text/html,application/xhtml+xml,application/xml;" \ + "q=0.9,image/webp,*/*;q=0.8", + "Accept-Encoding": "gzip, deflate, sdch", + "Accept-Language": "en-US,en;q=0.8", + "Referer": "http://flvgo.com/download" +} + +ss = requests.session() +ss.headers.update(headers) + +def download(info): + if not os.path.exists(info['dir_']): + os.mkdir(info['dir_']) + + #else: + #if os.path.exists(info['filename']): + #return 0 + + num = random.randint(0, 7) % 8 + col = s % (2, num + 90, os.path.basename(info['filename'])) + print '\n ++ 正在下载:', '#', \ + s % (1, 97, info['n']), '/', \ + s % (1, 97, info['amount']), \ + '#', col + + print info['durl'] + cmd = 'wget -c -nv --user-agent "%s" -O "%s" "%s"' \ + % (headers['User-Agent'], info['filename'], info['durl']) + status = os.system(cmd) + + if status != 0: # other http-errors, such as 302. + wget_exit_status_info = wget_es[status] + print('\n\n ----### \x1b[1;91mERROR\x1b[0m ==> \ + \x1b[1;91m%d (%s)\x1b[0m ###--- \n\n' \ + % (status, wget_exit_status_info)) + print s % (1, 91, ' ===> '), cmd + sys.exit(1) + +def play(info): + num = random.randint(0, 7) % 8 + col = s % (2, num + 90, os.path.basename(info['filename'])) + print '\n ++ play:', '#', \ + s % (1, 97, info['n']), '/', \ + s % (1, 97, info['amount']), \ + '#', col + + cmd = 'mpv --really-quiet --cache 8140 --cache-default 8140 ' \ + '--http-header-fields "User-Agent:%s" ' \ + '"%s"' % (headers['User-Agent'], info['durl']) + #'"%s"' % parser.unescape(info['durl']) + + os.system(cmd) + timeout = 1 + ii, _, _ = select.select([sys.stdin], [], [], timeout) + if ii: + sys.exit(0) + else: + pass + +def flvxz_parser(cn): + blocks = cn.split('playerContainer')[1:] + infos = {} + title = re.search(r'class="name">(.+?)<', cn).group(1) + infos['title'] = title + infos['data'] = {} + for bc in blocks: + quality = re.search(r'视频格式:(\w+)', bc).group(1) + size = sum([float(s) for s in re.findall(r'>([\d.]+) MB<', bc)]) + durls = re.findall(r'
', bc) + infos['data'][quality] = { + 'size': size, + 'durls': durls + } + return infos + +def pickup(infos): + print s % (1, 97, infos['title']) + print s % (1, 97, ' ++ pick a quality:') + sizes = [(infos['data'][q]['size'], q) for q in infos['data']] + sizes.sort() + sizes.reverse() + for i in xrange(len(sizes)): + print s % (1, 91, ' %s' % (i+1)), \ + str(sizes[i][0]) + 'MB\t', sizes[i][1] + + p = raw_input(s % (1, 92, ' Enter') + ' (1): ') + if p == '': + return sizes[0][1] + if not p.isdigit(): + print s % (1, 91, ' !! enter error') + sys.exit() + p = int(p) + if p <= len(infos['data']): + print s % (2, 92, ' -- %s' % sizes[p-1][1]) + return sizes[p-1][1] + else: + print s % (1, 91, ' !! enter error') + sys.exit() + +def getext(durl): + if durl.find('flv'): + return '.flv' + elif durl.find('mp4'): + return '.mp4' + elif durl.find('m3u8'): + return '.m3u8' + else: + return '.flv' + +def main(purl): + apiurl = 'http://flvgo.com/download?url=%s' % urllib.quote(purl) + ss.get('http://flvgo.com') + cn = ss.get(apiurl).content + infos = flvxz_parser(cn) + title = infos['title'] + quality = pickup(infos) + durls = infos['data'][quality]['durls'] + + yes = True if len(durls)> 1 else False + dir_ = os.path.join(os.getcwd(), infos['title']) if yes else os.getcwd() + + n = args.from_ - 1 + amount = len(durls) + + for i in xrange(n, amount): + info = { + 'title': title, + 'filename': os.path.join(dir_, str(i+1) + getext(durls[i])), + 'durl': durls[i], + 'dir_': dir_, + 'amount': amount, + 'n': n + } + if args.play: + play(info) + else: + download(info) + n += 1 + +if __name__ == '__main__': + p = argparse.ArgumentParser(description='flvxz') + p.add_argument('url', help='site url') + p.add_argument('-p', '--play', action='store_true', \ + help='play with mpv') + p.add_argument('-f', '--from_', action='store', \ + default=1, type=int, \ + help='从第几个开始下载,eg: -f 42') + args = p.parse_args() + main(args.url) diff --git a/flvxz_cl.py b/flvxz_cl.py deleted file mode 100755 index 942adf9..0000000 --- a/flvxz_cl.py +++ /dev/null @@ -1,199 +0,0 @@ -#!/usr/bin/env python2 -# vim: set fileencoding=utf8 - -import re -import base64 -import requests -import os -import sys -import argparse -import random -import json -from HTMLParser import HTMLParser -import select - -s = '\x1b[%d;%dm%s\x1b[0m' # terminual color template -parser = HTMLParser() - -############################################################ -# wget exit status -wget_es = { - 0: "No problems occurred.", - 2: "User interference.", - 1<<8: "Generic error code.", - 2<<8: "Parse error - for instance, \ - when parsing command-line optio.wgetrc or .netrc...", - 3<<8: "File I/O error.", - 4<<8: "Network failure.", - 5<<8: "SSL verification failure.", - 6<<8: "Username/password authentication failure.", - 7<<8: "Protocol errors.", - 8<<8: "Server issued an error response." -} -############################################################ - -headers = { - "Host": "www.flvxz.com", - "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) \ - AppleWebKit/537.36 (KHTML, like Gecko) \ - Chrome/40.0.2214.91 Safari/537.36", - "Accept": "*/*", - "Accept-Language": "en-US,en;q=0.5", - "Accept-Encoding": "gzip, deflate", - "Referer": "http://flvxz.com", - "Connection": "keep-alive" -} - -ss = requests.session() -ss.headers.update(headers) - -api = 'https://www.flvxz.com/getFlv.php?url=%s' - -def download(infos): - if not os.path.exists(infos['dir_']): - os.mkdir(infos['dir_']) - - #else: - #if os.path.exists(infos['filename']): - #return 0 - - num = random.randint(0, 7) % 8 - col = s % (2, num + 90, os.path.basename(infos['filename'])) - print '\n ++ 正在下载:', '#', \ - s % (1, 97, infos['n']), '/', \ - s % (1, 97, infos['amount']), \ - '#', col - - cmd = 'wget -c -nv --user-agent "%s" -O "%s" "%s"' \ - % (headers['User-Agent'], infos['filename'], - parser.unescape(infos['durl'])) - status = os.system(cmd) - - if status != 0: # other http-errors, such as 302. - wget_exit_status_info = wget_es[status] - print('\n\n ----### \x1b[1;91mERROR\x1b[0m ==> \ - \x1b[1;91m%d (%s)\x1b[0m ###--- \n\n' \ - % (status, wget_exit_status_info)) - print s % (1, 91, ' ===> '), cmd - sys.exit(1) - -def play(infos): - num = random.randint(0, 7) % 8 - col = s % (2, num + 90, os.path.basename(infos['filename'])) - print '\n ++ play:', '#', \ - s % (1, 97, infos['n']), '/', \ - s % (1, 97, infos['amount']), \ - '#', col - - cmd = 'mpv --really-quiet --cache 8140 --cache-default 8140 ' \ - '--http-header-fields "User-Agent:%s" ' \ - '"%s"' % (headers['User-Agent'], infos['durl']) - #'"%s"' % parser.unescape(infos['durl']) - - os.system(cmd) - timeout = 1 - ii, _, _ = select.select([sys.stdin], [], [], timeout) - if ii: - sys.exit(0) - else: - pass - -def decrypt(encrypted_cn): - c = encrypted_cn.split('}(') - x = re.search(r'(\[.+\])\)\);', c[1]).group(1) - y = re.search(r'(\[.+\])..\)\);if', c[2]).group(1) - - a, b = json.loads('[' + x + ']') - for i in xrange(len(b)): - b[i] = a[b[i]] - t = ''.join(b[::-1])[8:-1] - b = json.loads(t) - - a = json.loads(y) - for i in xrange(len(b)): - b[i] = a[b[i]] - decrypted_cn = ''.join(b[::-1]) - - return decrypted_cn - -def flvxz_parser(cn): - cn = decrypt(cn) - qualities = re.findall(r'"quality">\[(.+?)\]<', cn) - if not qualities: return {} - - j = {} - chucks = re.split(r'"quality">\[.+?\]<', cn)[1:] - - for i in xrange(len(qualities)): - parts = re.findall(r'data-clipboard-', chucks[i]) - t = re.findall(r'rel="noreferrer" href="/index.cgi/contrast/https://github.com/tenfar/iScript/compare/(.+?)"', chucks[i]) - urls = [ii for ii in t if 'flvxz' not in ii] - - j[qualities[i]] = zip(parts, urls) - - return j - -def pickup(j): - print s % (1, 97, ' ++ pick a quality:') - qualities = j.keys() - qualities.sort() - for i in xrange(len(qualities)): - print s % (1, 91, ' %s' % (i+1)), qualities[i] - - p = raw_input(s % (1, 92, ' Enter: ')) - if p.isdigit(): - p = int(p) - if p <= len(j): - print s % (2, 92, ' -- %s' % qualities[p-1]) - return j[qualities[p-1]] - else: - print s % (1, 91, ' !! enter error') - sys.exit() - else: - print s % (1, 91, ' !! enter error') - sys.exit() - -def main(url): - encode_url = base64.urlsafe_b64encode(url.replace('://', ':##')) - url = api % encode_url - - cn = ss.get(url).content - j = flvxz_parser(cn) - if j: - j = pickup(j) - else: - print s % (1, 91, ' !! Can\'t get videos') - sys.exit() - - title = re.search(r'media-heading">(.+?) 1 else False - dir_ = os.path.join(os.getcwd(), title) if yes else os.getcwd() - - n = args.from_ - amount = len(j) - j = j[args.from_ - 1:] - for i in j: - infos = { - 'filename': os.path.join(dir_, i[0]), - 'durl': i[1], - 'dir_': dir_, - 'amount': amount, - 'n': n - } - if args.play: - play(infos) - else: - download(infos) - n += 1 - -if __name__ == '__main__': - p = argparse.ArgumentParser(description='flvxz') - p.add_argument('url', help='site url') - p.add_argument('-p', '--play', action='store_true', \ - help='play with mpv') - p.add_argument('-f', '--from_', action='store', \ - default=1, type=int, \ - help='从第几个开始下载,eg: -f 42') - args = p.parse_args() - main(args.url) From 836651331d4613274f29424a2a21476a5173ea93 Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年5月13日 17:05:00 +0800 Subject: [PATCH 048/158] missing me --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3bb9ca1..53e74e4 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ *[L]* | [yunpan.360.cn.py](#yunpan.360.cn.py) | 360网盘的下载 | *[L]* | [music.baidu.com.py](#music.baidu.com.py) | 下载或播放高品质百度音乐(music.baidu.com) | *[L]* | [music.163.com.py](#music.163.com.py) | 下载或播放高品质网易音乐(music.163.com) | -*[L]* | [flvxz_cl.py](#flvxz_cl.py) | flvxz.com 视频解析 client - 支持下载、播放 | +*[L]* | [flv_cmd.py](#flv_cmd.py) | 基于在线服务的视频解析 client - 支持下载、播放 | *[L]* | [tumblr.py](#tumblr.py) | 下载某个tumblr.com的所有图片 | *[L]* | [unzip.py](#unzip.py) | 解决linux下unzip乱码的问题 | *[L]* | [ed2k_search.py](#ed2k_search.py) | 基于 donkey4u.com 的emule搜索 | From d104a6e6e9c83dab1bff65eda5ec6b05f99db2bc Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年5月30日 12:33:36 +0800 Subject: [PATCH 049/158] change type of filetype to list --- 115.py | 4 ++-- pan.baidu.com.py | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/115.py b/115.py index 8cb93b7..6156e0d 100755 --- a/115.py +++ b/115.py @@ -32,13 +32,13 @@ ############################################################ # file extensions -mediatype = { +mediatype = [ ".wma", ".wav", ".mp3", ".aac", ".ra", ".ram", ".mp2", ".ogg", ".aif", ".mpega", ".amr", ".mid", ".midi", ".m4a", ".m4v", ".wmv", ".rmvb", ".mpeg4", ".mpeg2", ".flv", ".avi", ".3gp", ".mpga", ".qt", ".rm", ".wmz", ".wmd", ".wvx", ".wmx", ".wm", ".swf", ".mpg", ".mp4", ".mkv", ".mpeg", ".mov", ".mdf", ".iso", ".asf" -} +] s = '\x1b[%d;%dm%s\x1b[0m' # terminual color template diff --git a/pan.baidu.com.py b/pan.baidu.com.py index b59dea1..be73cbd 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -68,24 +68,24 @@ ############################################################ # file extensions -mediatype = { +mediatype = [ ".wma", ".wav", ".mp3", ".aac", ".ra", ".ram", ".mp2", ".ogg", \ ".aif", ".mpega", ".amr", ".mid", ".midi", ".m4a", ".m4v", ".wmv", \ ".rmvb", ".mpeg4", ".mpeg2", ".flv", ".avi", ".3gp", ".mpga", ".qt", \ ".rm", ".wmz", ".wmd", ".wvx", ".wmx", ".wm", ".swf", ".mpg", ".mp4", \ ".mkv", ".mpeg", ".mov", ".mdf", ".iso", ".asf", ".vob" -} -imagetype = { +] +imagetype = [ ".jpg", ".jpeg", ".gif", ".bmp", ".png", ".jpe", ".cur", ".svg", \ ".svgz", ".tif", ".tiff", ".ico" -} -doctype = { +] +doctype = [ ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".vsd", ".txt", ".pdf", \ ".ods", ".ots", ".odt", ".rtf", ".dot", ".dotx", ".odm", ".pps", ".pot", \ ".xlt", ".xltx", ".csv", ".ppsx", ".potx", ".epub", ".apk", ".exe", \ ".msi", ".ipa", ".torrent", ".mobi" -} -archivetype = { +] +archivetype = [ ".7z", ".a", ".ace", ".afa", ".alz", ".android", ".apk", ".ar", \ ".arc", ".arj", ".b1", ".b1", ".ba", ".bh", ".bz2", ".cab", ".cab", \ ".cfs", ".chm", ".cpio", ".cpt", ".cqm", ".dar", ".dd", ".dgc", ".dmg", \ @@ -99,7 +99,7 @@ ".uc0", ".uc2", ".uca", ".ucn", ".ue2", ".uha", ".ur2", ".war", ".web", \ ".wim", ".x", ".xar", ".xp3", ".xz", ".yz1", ".z", ".zip", ".zipx", \ ".zoo", ".zpaq", ".zz" -} +] s = '\x1b[%s;%sm%s\x1b[0m' # terminual color template From aa907517184797773d6e9170843cd5c0585c2758 Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年5月30日 12:35:40 +0800 Subject: [PATCH 050/158] [pan baidu] sort username --- 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 be73cbd..87119f9 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -1804,9 +1804,7 @@ def do(): kw = keyword.decode('utf8', 'ignore') self.highlights.append({'text': kw, 'is_regex': 0}) infos = {i['fs_id']: i for i in infos}.values() - infos = self._sift(infos, name=arguments.get('name'), \ - size=arguments.get('size'), time=arguments.get('time'), \ - desc=arguments.get('desc')) + infos = self._sift(infos, **arguments) if not infos: return if not arguments.get('pipe'): @@ -3017,10 +3015,10 @@ def handle_command(comd, xxx): comd == 'user': accounts = panbaiducom_HOME._check_cookie_file() if accounts: - cu = zip(range(len(accounts)), [u for u in accounts]) + cu = zip(range(len(accounts)), sorted([u for u in accounts])) for i, u in cu: print s % (1, 92, i+1) if accounts[u]['on'] else s % (1, 91, i+1), \ - accounts[u]['capacity'], \ + accounts[u]['capacity'].ljust(15), \ s % (2, 92, u) if accounts[u]['on'] else s % (2, 97, u) if comd == 'userdelete' or comd == 'ud': print s % (2, 97, 0), s % (2, 91, 'ALL') elif comd == 'user': sys.exit() From b51616ff37ecbbdb146bb3d39c97aed32f9e6ed2 Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年5月30日 12:38:01 +0800 Subject: [PATCH 051/158] [bt] fix proxy --- bt.py | 69 +++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 39 insertions(+), 30 deletions(-) diff --git a/bt.py b/bt.py index 918082f..ab4d562 100755 --- a/bt.py +++ b/bt.py @@ -11,6 +11,10 @@ import urlparse import argparse +s = '\x1b[%d;%dm%s\x1b[0m' # terminual color template +letters = [i for i in '.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' \ + + '0123456789'] + ############################################################ headers = { "Connection": "keep-alive", @@ -27,11 +31,16 @@ ss = requests.session() ss.headers.update(headers) -s = u'\x1b[%d;%dm%s\x1b[0m' # terminual color template -letters = [i for i in '.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' \ - + '0123456789'] +def save_img(url, ext): + path = os.path.join(os.path.expanduser('~'), 'vcode.%s' % ext) + with open(path, 'w') as g: + data = requests.get(url).content + g.write(data) + print " ++ 验证码已保存至", s % (1, 97, path) + input_code = raw_input(s % (2, 92, " 输入验证码: ")) + return input_code -class bt(object): +class Bt(object): def transfer(self, string, tpath, foo=None, bar=None): self.dir_dict = {} self.sub_dir_index = 0 @@ -113,21 +122,22 @@ def _check_ext(self, ext): def get_torrent(self, hh): print s % (1, 93, '\n ++ get torrent from web') - def do(url, proxies=None, data=None, timeout=None): + def do(url, data=None, timeout=None): try: + proxies = {'http': args.proxy} if args.proxy else None r = ss.get(url, proxies=proxies, timeout=timeout) cnt = r.content if r.ok and cnt and ' ' not in cnt \ and '4:name' in cnt: - print s % (1, 92, u' √ get torrent.') + print s % (1, 92, ' √ get torrent.') return cnt else: - print s % (1, 91, u' ×ばつ not get.') + print s % (1, 91, ' ×ばつ not get.') return None except: return None - ## with xunlei + ## xunlei print s % (1, 94, '>> try:'), 'bt.box.n0808.com' url = 'http://bt.box.n0808.com/%s/%s/%s.torrent' \ % (hh[:2], hh[-2:], hh) @@ -135,37 +145,37 @@ def do(url, proxies=None, data=None, timeout=None): result = do(url) if result: return result - ## with https://torrage.com + ## https://torrage.com if ss.headers.get('Referer'): del ss.headers['Referer'] - if args.proxy: - print s % (1, 94, '>> try:'), 'torrage.com' - proxies = { - 'http': args.proxy} if args.proxy else None - url = 'http://torrage.com/torrent/%s.torrent' % hh - try: - result = do(url, proxies=proxies) - if result: return result - except: - print s % (1, 91, ' !! proxy doesn\'t work:'), args.proxy + print s % (1, 94, '>> try:'), 'torrage.com' + url = 'http://torrage.com/torrent/%s.torrent' % hh + try: + result = do(url) + if result: return result + except: + pass - ## with http://btcache.me + ## http://btcache.me if ss.headers.get('Referer'): del ss.headers['Referer'] print s % (1, 94, '>> try:'), 'btcache.me' url = 'http://btcache.me/torrent/%s' % hh r = ss.get(url) key = re.search(r'name="key" value="(.+?)"', r.content) if key: + url = 'http://btcache.me/captcha' + vcode = save_img(url, 'png') data = { - "key": key.group(1) + "key": key.group(1), + "captcha": vcode } ss.headers['Referer'] = url url = 'http://btcache.me/download' - result = do(url, data=data, proxies=proxies) + result = do(url, data=data) if result: return result else: - print s % (1, 91, u' ×ばつ not get.') + print s % (1, 91, ' ×ばつ not get.') - ## some torrent stores + ## torrent stores if ss.headers.get('Referer'): del ss.headers['Referer'] urls = [ #'http://www.sobt.org/Tool/downbt?info=%s', @@ -274,7 +284,7 @@ def do(): tpath = os.path.join(dir_, 'change_' + i) self.transfer(string, tpath, foo=foo, bar=bar) - paths.update(ipath) + # ??? paths.update(ipath) if os.getcwd() == os.path.abspath(dir_): do() elif os.getcwd() != os.path.abspath(dir_) and \ @@ -334,7 +344,6 @@ def main(argv): p.add_argument('-i', '--import_from', type=str, nargs='*', help='import magnet from local.') p.add_argument('-p', '--proxy', action='store', - default='socks5://127.0.0.1:8883', type=str, help='proxy for torrage.com, \ eg: -p "sooks5://127.0.0.1:8883"') p.add_argument('-d', '--directory', action='store', default=None, @@ -356,18 +365,18 @@ def main(argv): if comd == 'm' or comd == 'mt': # magnet to torrent urls = xxx if not args.import_from \ else import_magnet(args.import_from) - x = bt() + x = Bt() x.magnet2torrent(urls, dir_) elif comd == 't' or comd == 'tm': # torrent ot magnet paths = xxx - x = bt() + x = Bt() x.torrent2magnet(paths) elif comd == 'c' or comd == 'ct': # change ups = xxx if not args.import_from \ else import_magnet(args.import_from) - x = bt() + x = Bt() x.change(ups, dir_, foo=None, bar=None) elif comd == 'cr' or comd == 'ctre': # change @@ -375,7 +384,7 @@ def main(argv): bar = xxx[1] ups = xxx[2:] if not args.import_from \ else import_magnet(args.import_from) - x = bt() + x = Bt() x.change(ups, dir_, foo=foo, bar=bar) else: From 1fd36c8dc6f9941e00882b8cadba68ebed18b460 Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年5月30日 12:39:23 +0800 Subject: [PATCH 052/158] [tumblr] use collection.deque --- tumblr.py | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/tumblr.py b/tumblr.py index 48dbd95..b660448 100755 --- a/tumblr.py +++ b/tumblr.py @@ -5,6 +5,7 @@ import sys import re import json +from collections import deque import requests import argparse import random @@ -64,17 +65,18 @@ def async(tasks, queue, run=None, cb=None, num=10): nsize = num - len(queue) for i in xrange(nsize): try: - task = tasks.pop(0) + task = tasks.popleft() except IndexError: break f = run(task) if f: queue.append(f) - return tasks, queue + #return tasks, queue class tumblr(object): def __init__(self): self.queue = [] - self.tasks = [] + self.tasks = deque() + self.key = 0 def save_json(self): with open(self.json_path, 'w') as g: @@ -95,7 +97,6 @@ def get_site_infos(self, postid=None): "api_key": api_key } - r = None while True: try: r = ss.get(self.url, params=params) @@ -194,14 +195,11 @@ def run(i): def callback(filepath): os.rename('%s.tmp' % filepath, filepath) - tasks = self.infos['photos'] + self.tasks - self.tasks = [] + self.tasks.extend(self.infos['photos']) while True: - tasks, self.queue = async( - tasks, self.queue, run=run, + async(self.tasks, self.queue, run=run, cb=callback, num=self.processes) - if len(tasks) <= self.processes: - self.tasks = tasks + if len(self.tasks) <= self.processes: break def download_site(self, url): @@ -213,15 +211,12 @@ def download_site(self, url): if not os.path.exists(self.infos['dir_']): os.makedirs(self.infos['dir_']) self.json_path = os.path.join(self.infos['dir_'], 'json.json') - self.key = 0 print s % (1, 92, '\n ## begin'), 'key = %s' % self.key else: self.json_path = os.path.join(self.infos['dir_'], 'json.json') if os.path.exists(self.json_path): self.key = json.loads(open(self.json_path).read())['key'] - 20 print s % (1, 92, '\n ## begin'), 'key = %s' % self.key - else: - self.key = 0 if args.check: t = os.listdir(self.infos['dir_']) From 814d47d2dc9bd6a164b4b8393e6757a45b94f726 Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年5月30日 12:41:47 +0800 Subject: [PATCH 053/158] [bt] update --- README.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 53e74e4..c9d1222 100644 --- a/README.md +++ b/README.md @@ -734,6 +734,11 @@ magnet 和 torrent 的相互转换 过滤敏.感.词功能用于净网时期的 baidu, xunlei +在中国大陆使用代理可能有更好的效果: +使用代理有两种方法: +1. shadowsocks + proxychains +2. -p protocol://ip:port + ~~8.30日后,无法使用。 见 http://tieba.baidu.com/p/3265467666~~ [**百度云疑似解封,百度网盘内八秒视频部分恢复**](http://fuli.ba/baiduyunhuifuguankan.html) @@ -794,7 +799,7 @@ cr 或 ctre foo bar magnet_link1 /path/to/torrent1 [-d /path/to/save] #### 参数: ``` --p PROXY, --proxy PROXY proxy for torrage.com, eg: -p 127.0.0.1:8087 (默认) +-p PROXY, --proxy PROXY proxy for torrage.com, eg: -p "sooks5://127.0.0.1:8883" -t TYPE_, --type_ TYPE_ 类型参数: -t n (默认) 用数字替换文件名 -t be64 用base64加密文件名,torrent用百度下载后,可用 pan.baidu.com.py rnr /path -t f,bd64 改回原名字 @@ -1152,7 +1157,7 @@ http://flvgo.com/sites #### 3. 用法 -fl是flvxz_cl.py的马甲 (alias fl='python2 /path/to/flvxz_cl.py') +fl是flv_cmd.py的马甲 (alias fl='python2 /path/to/flv_cmd.py') #### 下载: From 48fa5f564a25ec276d336993d2c8f27ff8ae09e4 Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年5月31日 01:00:55 +0800 Subject: [PATCH 054/158] [91porn] youtube-dl support 91porn --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c9d1222..876edcc 100644 --- a/README.md +++ b/README.md @@ -1289,7 +1289,9 @@ mpv (http://mpv.io) #### 2. 使用说明 -> 没有解决 *7个/day* 限制 +> youtube-dl 已支持91porn + +没有解决每个ip *10个/day* 限制 下载工具默认为wget, 可用参数-a选用aria2 From 36537a9350407275e091faf35f6655b39a5daa3e Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年6月13日 08:34:03 +0800 Subject: [PATCH 055/158] [tumblr] change checking method --- tumblr.py | 94 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 48 insertions(+), 46 deletions(-) diff --git a/tumblr.py b/tumblr.py index b660448..a2868dc 100755 --- a/tumblr.py +++ b/tumblr.py @@ -104,31 +104,32 @@ def get_site_infos(self, postid=None): except Exception as e: print s % (1, 91, ' !! Error at get_infos'), e time.sleep(5) - if r.ok: - j = r.json() - if j['response']['posts']: - for i in j['response']['posts']: - index = 1 - for ii in i['photos']: - durl = ii['original_size']['url'].encode('utf8') - filepath = os.path.join(self.infos['dir_'], '%s_%s.%s' \ - % (i['id'], index, durl.split('.')[-1])) - filename = os.path.split(filepath)[-1] - t = { - 'filepath': filepath, - 'durl': durl, - 'filename': filename - } - index += 1 - self.infos['photos'].append(t) - else: - print s % (1, 92, '\n --- job over ---') - sys.exit(0) - else: + + if not r.ok: print s % (1, 91, '\n !! Error, get_infos') print r.status_code, r.content sys.exit(1) + j = r.json() + if not j['response']['posts']: + print s % (1, 92, '\n --- job over ---') + sys.exit(0) + + for i in j['response']['posts']: + index = 1 + for ii in i['photos']: + durl = ii['original_size']['url'].encode('utf8') + filepath = os.path.join(self.infos['dir_'], '%s_%s.%s' \ + % (i['id'], index, durl.split('.')[-1])) + filename = os.path.split(filepath)[-1] + t = { + 'filepath': filepath, + 'durl': durl, + 'filename': filename + } + index += 1 + self.infos['photos'].append(t) + def get_tag_infos(self): self.infos['photos'] = [] self.url = 'https://api.tumblr.com/v2/tagged' @@ -148,38 +149,39 @@ def get_tag_infos(self): except Exception as e: print s % (1, 91, ' !! Error at get_infos'), e time.sleep(5) - if r.ok: - j = r.json() - if j['response']: - for i in j['response']: - index = 1 - if i.get('photos'): - for ii in i['photos']: - durl = ii['original_size']['url'].encode('utf8') - filepath = os.path.join( - self.infos['dir_'], '%s_%s.%s' \ - % (i['id'], index, durl.split('.')[-1])) - filename = os.path.split(filepath)[-1] - t = { - 'filepath': filepath, - 'durl': durl, - 'filename': filename, - 'key': i['timestamp'] - } - index += 1 - self.infos['photos'].append(t) - else: - print s % (1, 92, '\n --- job over ---') - sys.exit(0) - else: + + if not r.ok: print s % (1, 91, '\n !! Error, get_infos') print r.status_code, r.content sys.exit(1) + j = r.json() + if not j['response']: + print s % (1, 92, '\n --- job over ---') + sys.exit(0) + + for i in j['response']: + index = 1 + if i.get('photos'): + for ii in i['photos']: + durl = ii['original_size']['url'].encode('utf8') + filepath = os.path.join( + self.infos['dir_'], '%s_%s.%s' \ + % (i['id'], index, durl.split('.')[-1])) + filename = os.path.split(filepath)[-1] + t = { + 'filepath': filepath, + 'durl': durl, + 'filename': filename, + 'key': i['timestamp'] + } + index += 1 + self.infos['photos'].append(t) + def download(self): def run(i): if os.path.exists(i['filepath']): - return + return None num = random.randint(0, 7) % 8 col = s % (1, num + 90, i['filepath']) print ' ++ download: %s' % col From 7737185b56ad916e7db03f11f1e85a71f9e0f4b8 Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年6月13日 11:20:05 +0800 Subject: [PATCH 056/158] [pan.baidu.com] change login vcode.gif to vcode.jpg --- 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 87119f9..fd1b107 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -211,7 +211,7 @@ def print_process_bar(point, total, slice_size, speed = sizeof_fmt(slice_size / (now - start_time)) + '/s' t = int(nowpoint*length) - msg = '\r' + ' '.join([pre, '[%s%s]' % ('='*t, ' '*(length - t)), \ + msg = '\r' + ' '.join([pre, '|%s%s|' % ('='*t, ' '*(length - t)), \ str(percent) + '%', speed, msg, suf]) sys.stdout.write(msg) sys.stdout.flush() @@ -392,7 +392,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 1ae0532e2b6353177ef93d7815ded64f212d9412 Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年6月14日 09:00:41 +0800 Subject: [PATCH 057/158] [tumblr] restructure code --- tumblr.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tumblr.py b/tumblr.py index a2868dc..2786006 100755 --- a/tumblr.py +++ b/tumblr.py @@ -72,7 +72,22 @@ def async(tasks, queue, run=None, cb=None, num=10): if f: queue.append(f) #return tasks, queue -class tumblr(object): +class TumblrAPI(object): + def _request(self, base_hostname, target, params, type=''): + + def _info(self, base_hostname): + + def _followers(self, base_hostname): + + def _likes(self, base_hostname): + + def _photo(self, base_hostname, offset='', tag='', id=''): + + def _audio(self, base_hostname, offset='', tag='', id=''): + + def _video(self, base_hostname, offset='', tag='', id=''): + +class Tumblr(TumblrAPI): def __init__(self): self.queue = [] self.tasks = deque() From bce18c39ab2433407e8d9fee32efcd595cca8719 Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年6月14日 22:55:58 +0800 Subject: [PATCH 058/158] [tumblr] add -V -A to download videos and audios --- README.md | 52 +++++- tumblr.py | 495 ++++++++++++++++++++++++++++++++++-------------------- 2 files changed, 361 insertions(+), 186 deletions(-) diff --git a/README.md b/README.md index 876edcc..e75da58 100644 --- a/README.md +++ b/README.md @@ -1184,13 +1184,15 @@ fl url -p --- -### tumblr.py - 下载某个tumblr.com的所有图片 +### tumblr.py - 下载某个tumblr.com的所有图片、视频、音频 #### 1. 依赖 ``` wget +mpv (http://mpv.io) + python2-requests (https://github.com/kennethreitz/requests) ``` @@ -1204,7 +1206,7 @@ python2-requests (https://github.com/kennethreitz/requests) 下载的文件,保存在当前目录下。 -默认下载原图。 +默认下载图片(原图)。 支持连续下载,下载进度储存在下载文件夹内的 json.json。 @@ -1214,6 +1216,13 @@ python2-requests (https://github.com/kennethreitz/requests) -p PROCESSES, --processes PROCESSES 指定多进程数,默认为10个,最多为20个 eg: -p 20 -c, --check 尝试修复未下载成功的图片 -t TAG, --tag TAG 下载特定tag的图片, eg: -t beautiful + +-P, --play play with mpv +-A, --audio download audios +-V, --video download videos +-q, --quiet quiet + +-f OFFSET, --offset OFFSET 从第offset个开始,只对 -V 有用。 ``` #### 3. 用法 @@ -1221,13 +1230,40 @@ python2-requests (https://github.com/kennethreitz/requests) tm是tumblr.py的马甲 (alias tm='python2 /path/to/tumblr.py') ``` -# 下载某个tumblr -tm http://sosuperawesome.tumblr.com/ -tm http://sosuperawesome.tumblr.com/ -t beautiful +# 下载图片 +tm http://sosuperawesome.tumblr.com +tm http://sosuperawesome.tumblr.com -t beautiful + +# 下载单张图片 +tm http://sosuperawesome.tumblr.com/post/121467716523/murosvur-on-etsy + +# 下载视频 +tm url -V +tm url -V -f 42 +tm url -V -t tag + +# 下载单个视频 +tm url/post/1234567890 -V + +# 播放视频 +tm url -VP +tm url -VP -f 42 + +# 下载音频 +tm url -A +tm url -A -f 42 +tm url -A -t tag + +# 下载单个音频 +tm url/post/1234567890 -A + +# 播放音频 +tm url -AP +tm url -AP -f 42 + +# 播放音频(quiet) +tm url -APq -# 指定tag下载 -tm beautiful -tm cool ``` --- diff --git a/tumblr.py b/tumblr.py index 2786006..8f80342 100755 --- a/tumblr.py +++ b/tumblr.py @@ -1,16 +1,20 @@ #!/usr/bin/env python2 # vim: set fileencoding=utf8 +from __future__ import unicode_literals + import os import sys import re import json from collections import deque import requests +import urlparse import argparse import random import subprocess import time +import select api_key = 'fuiKNFp9vQFvjLNvx4sUwti4Yb5yGutBN4Xh10LXZhhRKjWlV4' @@ -59,7 +63,7 @@ def sleep(size, num): t = float(size) / num time.sleep(t) -def async(tasks, queue, run=None, cb=None, num=10): +def async(tasks, queue, dir_, run=None, cb=None, num=10): queue = check_queue(queue, cb) sleep(len(queue), num) nsize = num - len(queue) @@ -68,241 +72,376 @@ def async(tasks, queue, run=None, cb=None, num=10): task = tasks.popleft() except IndexError: break - f = run(task) + f = run(task, dir_) if f: queue.append(f) #return tasks, queue +class Error(Exception): + def __init__(self, msg): + self.msg = msg + def __str__(self): + return self.msg + class TumblrAPI(object): - def _request(self, base_hostname, target, params, type=''): + 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 + res = ss.get(api_url, params=params) + if not res: + raise Error(s % (1, 91, 'api request error.')) - def _info(self, base_hostname): + json_data = res.json() + if json_data['meta']['msg'].lower() != 'ok': + raise Error(s % (1, 91, json_data['meta']['msg'])) - def _followers(self, base_hostname): + return json_data['response'] - def _likes(self, base_hostname): + def _info(self, base_hostname): + return self._request(base_hostname, 'info', '', None) + + def _photo(self, base_hostname, offset='', tag='', post_id='', to_items=True): + def make_items(raw_data): + items = [] + for i in raw_data['posts']: + index = 1 + if i.get('photos'): + for ii in i['photos']: + durl = ii['original_size']['url'].replace('http:', 'https:') + filename = os.path.join( + '%s_%s.%s' % (i['id'], index, durl.split('.')[-1])) + t = { + 'durl': durl, + 'filename': filename, + 'key': i['timestamp'], + 'subdir': 'photos', + } + index += 1 + items.append(t) + return items - def _photo(self, base_hostname, offset='', tag='', id=''): + params = { + 'offset': offset, + 'before': offset if tag else '', + 'tag': tag, + 'id': post_id, + 'limit': 20 if not tag and not post_id else '', + 'filter': 'text' + } + raw_data = self._request(base_hostname, 'posts', 'photo', params) + if to_items: + return make_items(raw_data) + else: + return raw_data + + def _audio(self, base_hostname, offset='', tag='', post_id='', to_items=True): + def make_items(raw_data): + items = [] + for i in raw_data['posts']: + durl = i['audio_url'].replace('http:', 'https:') + filename = os.path.join( + '%s_%s.%s' % (i['id'], i['track_name'], durl.split('.')[-1])) + t = { + 'durl': durl, + 'filename': filename, + 'timestamp': i['timestamp'] if tag else '', + 'subdir': 'audios' + } + items.append(t) + return items - def _audio(self, base_hostname, offset='', tag='', id=''): + params = { + 'offset': offset, + 'before': offset if tag else '', + 'tag': tag, + 'id': post_id, + 'limit': 20 if not tag and not post_id else '', + 'filter': 'text' + } + raw_data = self._request(base_hostname, 'posts', 'audio', params) + if to_items: + return make_items(raw_data) + else: + return raw_data + + def _video(self, base_hostname, offset='', tag='', post_id='', to_items=True): + def make_items(raw_data): + items = [] + for i in raw_data['posts']: + if not i.get('video_url'): + continue + durl = i['video_url'].replace('http:', 'https:') + filename = os.path.join( + '%s.%s' % (i['id'], durl.split('.')[-1])) + t = { + 'durl': durl, + 'filename': filename, + 'timestamp': i['timestamp'] if tag else '', + 'subdir': 'videos' + } + items.append(t) + return items - def _video(self, base_hostname, offset='', tag='', id=''): + params = { + 'offset': offset, + 'before': offset if tag else '', + 'tag': tag, + 'id': post_id, + 'limit': 20 if not tag and not post_id else '', + 'filter': 'text' + } + raw_data = self._request(base_hostname, 'posts', 'video', params) + if to_items: + return make_items(raw_data) + else: + return raw_data class Tumblr(TumblrAPI): - def __init__(self): + def __init__(self, args): + self.args = args self.queue = [] self.tasks = deque() - self.key = 0 + self.offset = self.args.offset + self.processes = int(self.args.processes) + if self.args.play: + self.download = self.play def save_json(self): with open(self.json_path, 'w') as g: g.write(json.dumps( - {'key': self.key}, indent=4, sort_keys=True)) + {'offset': self.offset}, indent=4, sort_keys=True)) - def get_site_infos(self, postid=None): - self.infos['photos'] = [] - self.url = 'https://api.tumblr.com/v2/blog/%s/posts/photo' \ - % self.infos['host'] - params = { - "offset": self.key if not postid else "", - "limit": 20 if not postid else "", - "type": "photo", - "filter": "text", - "tag": args.tag, - "id": postid if postid else "", - "api_key": api_key - } + def init_infos(self, base_hostname, target_type, tag=''): + self.infos = {'host': base_hostname} + if not tag: + self.infos['dir_'] = os.path.join(os.getcwd(), self.infos['host']) - while True: - try: - r = ss.get(self.url, params=params) - break - except Exception as e: - print s % (1, 91, ' !! Error at get_infos'), e - time.sleep(5) - - if not r.ok: - print s % (1, 91, '\n !! Error, get_infos') - print r.status_code, r.content - sys.exit(1) - - j = r.json() - if not j['response']['posts']: - print s % (1, 92, '\n --- job over ---') - sys.exit(0) - - for i in j['response']['posts']: - index = 1 - for ii in i['photos']: - durl = ii['original_size']['url'].encode('utf8') - filepath = os.path.join(self.infos['dir_'], '%s_%s.%s' \ - % (i['id'], index, durl.split('.')[-1])) - filename = os.path.split(filepath)[-1] - t = { - 'filepath': filepath, - 'durl': durl, - 'filename': filename - } - index += 1 - self.infos['photos'].append(t) + if not os.path.exists(self.infos['dir_']): + if not self.args.play: + os.makedirs(self.infos['dir_']) + self.json_path = os.path.join(self.infos['dir_'], 'json.json') + else: + self.json_path = os.path.join(self.infos['dir_'], 'json.json') + if os.path.exists(self.json_path): + self.offset = json.load(open(self.json_path))['offset'] - 20 + else: + self.infos['dir_'] = os.path.join(os.getcwd(), 'tumblr-%s' % tag) - def get_tag_infos(self): - self.infos['photos'] = [] - self.url = 'https://api.tumblr.com/v2/tagged' - params = { - "limit": 20, - "type": "photo", - "tag": self.infos['tag'], - "before": self.key, - "api_key": api_key - } + if not os.path.exists(self.infos['dir_']): + if not self.args.play: + os.makedirs(self.infos['dir_']) + self.json_path = os.path.join(self.infos['dir_'], 'json.json') + self.offset = int(time.time()) + else: + self.json_path = os.path.join(self.infos['dir_'], 'json.json') + if os.path.exists(self.json_path): + self.offset = json.load(open(self.json_path))['offset'] - r = None - while True: - try: - r = ss.get(self.url, params=params) - break - except Exception as e: - print s % (1, 91, ' !! Error at get_infos'), e - time.sleep(5) - - if not r.ok: - print s % (1, 91, '\n !! Error, get_infos') - print r.status_code, r.content - sys.exit(1) - - j = r.json() - if not j['response']: - print s % (1, 92, '\n --- job over ---') - sys.exit(0) - - for i in j['response']: - index = 1 - if i.get('photos'): - for ii in i['photos']: - durl = ii['original_size']['url'].encode('utf8') - filepath = os.path.join( - self.infos['dir_'], '%s_%s.%s' \ - % (i['id'], index, durl.split('.')[-1])) - filename = os.path.split(filepath)[-1] - t = { - 'filepath': filepath, - 'durl': durl, - 'filename': filename, - 'key': i['timestamp'] - } - index += 1 - self.infos['photos'].append(t) + if not os.path.exists(os.path.join(self.infos['dir_'], target_type)): + if not self.args.play: + os.makedirs(os.path.join(self.infos['dir_'], target_type)) + + if self.args.offset: + self.offset = self.args.offset + + print s % (1, 92, '\n ## begin'), 'offset = %s' % self.offset def download(self): - def run(i): - if os.path.exists(i['filepath']): + def run(i, dir_): + filepath = os.path.join(dir_, i['subdir'], i['filename']) + if os.path.exists(filepath): return None num = random.randint(0, 7) % 8 - col = s % (1, num + 90, i['filepath']) + col = s % (1, num + 90, filepath) print ' ++ download: %s' % col cmd = [ 'wget', '-c', '-q', - '-O', '%s.tmp' % i['filepath'], + '-O', '%s.tmp' % filepath, '--user-agent', '"%s"' % headers['User-Agent'], '%s' % i['durl'].replace('http:', 'https:') ] f = subprocess.Popen(cmd) - return f, i['filepath'] + return f, filepath def callback(filepath): os.rename('%s.tmp' % filepath, filepath) - self.tasks.extend(self.infos['photos']) + self.tasks.extend(self.infos['items']) while True: - async(self.tasks, self.queue, run=run, + async(self.tasks, self.queue, dir_=self.infos['dir_'], run=run, cb=callback, num=self.processes) if len(self.tasks) <= self.processes: break - def download_site(self, url): - self.infos = { - 'host': re.search(r'http(s|)://(.+?)($|/)', url).group(2)} - self.infos['dir_'] = os.path.join(os.getcwd(), self.infos['host']) - self.processes = int(args.processes) + def play(self): + for item in self.infos['items']: + num = random.randint(0, 7) % 8 + col = s % (2, num + 90, item['durl']) + print ' ++ play:', col + quiet = ' --really-quiet' if self.args.quiet else '' + cmd = 'mpv%s --no-ytdl --cache-default 20480 --cache-secs 120 ' \ + '--http-header-fields "User-Agent:%s" ' \ + '"%s"' \ + % (quiet, headers['User-Agent'], item['durl']) + + os.system(cmd) + timeout = 1 + ii, _, _ = select.select([sys.stdin], [], [], timeout) + if ii: + sys.exit(0) + else: + pass + + def download_photos_by_offset(self, base_hostname, post_id): + self.init_infos(base_hostname, 'photos') + + while True: + self.infos['items'] = self._photo( + base_hostname, offset=self.offset if not post_id else '', post_id=post_id) + if not self.infos['items']: + break + self.offset += 20 + self.save_json() + self.download() + if post_id: break + + def download_photos_by_tag(self, base_hostname, tag): + self.init_infos(base_hostname, 'photos', tag=tag) + + while True: + self.info['items'] = self._photo(base_hostname, tag=tag, before=self.offset) + if not self.infos['items']: + break + self.offset = self.infos['items'][-1]['timestamp'] + self.save_json() + self.download() + + def download_videos_by_offset(self, base_hostname, post_id): + self.init_infos(base_hostname, 'videos') + + while True: + self.infos['items'] = self._video( + base_hostname, offset=self.offset, post_id=post_id) + if not self.infos['items']: + break + self.offset += 20 + self.save_json() if not self.args.play else None + self.download() + if post_id: break + + def download_videos_by_tag(self, base_hostname, tag): + self.init_infos(base_hostname, 'videos', tag) - if not os.path.exists(self.infos['dir_']): - os.makedirs(self.infos['dir_']) - self.json_path = os.path.join(self.infos['dir_'], 'json.json') - print s % (1, 92, '\n ## begin'), 'key = %s' % self.key + while True: + self.infos['items'] = self._video( + base_hostname, before=self.offset, tag=tag) + if not self.infos['items']: + break + self.offset = self.infos['items'][-1]['timestamp'] + self.save_json() if not self.args.play else None + self.download() + + def download_audios_by_offset(self, base_hostname, post_id): + self.init_infos(base_hostname, 'audios') + + while True: + self.infos['items'] = self._audio( + base_hostname, offset=self.offset if not post_id else '', post_id=post_id) + if not self.infos['items']: + break + self.offset += 20 + self.save_json() if not self.args.play else None + self.download() + if post_id: break + + def download_audios_by_tag(self, base_hostname, tag): + self.init_infos(base_hostname, 'audios', tag) + + while True: + self.infos['items'] = self._audio( + base_hostname, before=self.offset, tag=tag) + if not self.infos['items']: + break + self.offset = self.infos['items'][-1]['timestamp'] + self.save_json() if not self.args.play else None + self.download() + + def download_photos(self, base_hostname, post_id='', tag=''): + if tag: + self.download_photos_by_tag(base_hostname, tag) else: - self.json_path = os.path.join(self.infos['dir_'], 'json.json') - if os.path.exists(self.json_path): - self.key = json.loads(open(self.json_path).read())['key'] - 20 - print s % (1, 92, '\n ## begin'), 'key = %s' % self.key - - if args.check: - t = os.listdir(self.infos['dir_']) - t = [i[:i.find('_')] for i in t if i.endswith('.tmp')] - ltmp = list(set(t)) - for postid in ltmp: - self.get_site_infos(postid) - self.download() + self.download_photos_by_offset(base_hostname, post_id=post_id) + + def download_videos(self, base_hostname, post_id='', tag=''): + if tag: + self.download_videos_by_tag(base_hostname, tag) else: - while True: - self.get_site_infos() - self.key += 20 - self.save_json() - self.download() - - def download_tag(self, tag): - self.infos = {'tag': tag} - self.infos['dir_'] = os.path.join( - os.getcwd(), 'tumblr-%s' % self.infos['tag']) - self.processes = int(args.processes) - - if not os.path.exists(self.infos['dir_']): - os.makedirs(self.infos['dir_']) - self.json_path = os.path.join(self.infos['dir_'], 'json.json') - self.key = int(time.time()) - print s % (1, 92, '\n ## begin'), 'key = %s' % self.key + self.download_videos_by_offset(base_hostname, post_id=post_id) + + def download_audios(self, base_hostname, post_id='', tag=''): + if tag: + self.download_audios_by_tag(base_hostname, tag) else: - self.json_path = os.path.join(self.infos['dir_'], 'json.json') - if os.path.exists(self.json_path): - self.key = json.loads(open(self.json_path).read())['key'] - print s % (1, 92, '\n ## begin'), 'key = %s' % self.key + self.download_audios_by_offset(base_hostname, post_id=post_id) + + def fix_photos(self, base_hostname): + self.init_infos(base_hostname, 'photos') + t = os.listdir(os.path.join(self.infos['dir_'], 'photos')) + t = [i[:i.find('_')] for i in t if i.endswith('.tmp')] + ltmp = list(set(t)) + for post_id in ltmp: + self.infos['items'] = self._photo(base_hostname, post_id=post_id) + self.download() + + def parse_urls(self, urls): + for url in urls: + if not url.startswith('http'): + raise Error(s % (1, 91, 'url must start with http:// or https://')) + + base_hostname = urlparse.urlparse(url).netloc + if self.args.check: + self.fix_photos(base_hostname) + continue + + if re.search(r'post/(\d+)', url): + post_id = re.search(r'post/(\d+)', url).group(1) else: - self.key = int(time.time()) - - if args.check: - t = os.listdir(self.infos['dir_']) - t = [i[:i.find('_')] for i in t if i.endswith('.tmp')] - ltmp = list(set(t)) - for postid in ltmp: - self.get_site_infos(postid) - self.download() - else: - while True: - self.get_tag_infos() - self.key = self.infos['photos'][-1]['key'] - self.save_json() - self.download() + post_id = '' + + if self.args.video: + self.download_videos(base_hostname, post_id=post_id, tag=self.args.tag) + elif self.args.audio: + self.download_audios(base_hostname, post_id=post_id, tag=self.args.tag) + else: + self.download_photos(base_hostname, post_id=post_id, tag=self.args.tag) def main(argv): p = argparse.ArgumentParser( description='download from tumblr.com') - p.add_argument('xxx', help='xxx') - p.add_argument('-p', '--processes', action='store', default=10, - help='指定多进程数,默认为10个,最多为20个 eg: -p 20') + p.add_argument('xxx', type=str, nargs='*', help='命令对象.') + p.add_argument('-p', '--processes', action='store', type=int, default=10, + help='指定多进程数,默认为10个,最多为20个 eg: -p 20') + p.add_argument('-f', '--offset', action='store', type=int, default=0, + help='offset') + p.add_argument('-q', '--quiet', action='store_true', + help='quiet') p.add_argument('-c', '--check', action='store_true', - help='尝试修复未下载成功的图片') + help='尝试修复未下载成功的图片') + p.add_argument('-P', '--play', action='store_true', + help='play with mpv') + p.add_argument('-V', '--video', action='store_true', + help='download videos') + p.add_argument('-A', '--audio', action='store_true', + help='download audios') p.add_argument('-t', '--tag', action='/index.cgi/contrast/https://github.com/tenfar/iScript/compare/store', default=None, type=str, help='下载特定tag的图片, eg: -t beautiful') - global args + #global args args = p.parse_args(argv[1:]) xxx = args.xxx - if 'http:' in xxx: - x = tumblr() - x.download_site(xxx) - else: - x = tumblr() - x.download_tag(xxx) + tumblr = Tumblr(args) + tumblr.parse_urls(xxx) if __name__ == '__main__': argv = sys.argv From 811b14add21cc7d855c8a9e5f5268a8b008ae604 Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年6月14日 23:05:32 +0800 Subject: [PATCH 059/158] [README] add tumblr exposition --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e75da58..8fe23c7 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ *[L]* | [music.baidu.com.py](#music.baidu.com.py) | 下载或播放高品质百度音乐(music.baidu.com) | *[L]* | [music.163.com.py](#music.163.com.py) | 下载或播放高品质网易音乐(music.163.com) | *[L]* | [flv_cmd.py](#flv_cmd.py) | 基于在线服务的视频解析 client - 支持下载、播放 | -*[L]* | [tumblr.py](#tumblr.py) | 下载某个tumblr.com的所有图片 | +*[L]* | [tumblr.py](#tumblr.py) | 下载某个tumblr.com的所有图片、视频、音频 | *[L]* | [unzip.py](#unzip.py) | 解决linux下unzip乱码的问题 | *[L]* | [ed2k_search.py](#ed2k_search.py) | 基于 donkey4u.com 的emule搜索 | *[L]* | [91porn.py](#91porn.py) | 下载或播放91porn | From 0ee8c6ccca61357e3a019d4b8fa01ed672f6507e Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年6月19日 00:32:34 +0800 Subject: [PATCH 060/158] [pan.baidu.com] ss.head is fast than ss.get(stream=True) at is_wenxintishi --- 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 fd1b107..8d85b5c 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -184,12 +184,12 @@ def fast_pcs_server(j): def is_wenxintishi(dlink): while True: try: - r = ss.get(dlink, stream=True) + res = ss.head(dlink) break except requests.exceptions.ConnectionError: time.sleep(2) - url = r.url - if 'wenxintishi' in url: + location = res.headers.get('location', '') + if 'wenxintishi' in location: return True else: return False From ee481208b013d4a374228d234b1fafd463be5ba7 Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年6月28日 00:28:27 +0800 Subject: [PATCH 061/158] [leetcode] add leetcode_problems.py --- README.md | 28 +++++++++++ leetcode_problems.py | 109 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100755 leetcode_problems.py diff --git a/README.md b/README.md index 8fe23c7..de1554a 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ | | | --------|---------|---------| +*[L]* | [leetcode_problems.py](#leetcode_problems.py) | 下载Leetcode的算法题 | *[L]* | [xiami.py](#xiami.py) | 下载或播放高品质虾米音乐(xiami.com) | *[L]* | [pan.baidu.com.py](#pan.baidu.com.py) | 百度网盘的下载、离线下载、上传、播放、转存、文件操作 | *[L]* | [bt.py](#bt.py) | magnet torrent 互转、及 过滤敏.感.词 | @@ -25,6 +26,33 @@ --- + +### leetcode_problems.py - 下载Leetcode的算法题 + +#### 依赖 + +``` +python2-requests (https://github.com/kennethreitz/requests) + +python2-lxml + +``` + +#### 参数: + +``` + --index sort by index + --level sort by level + --tag sort by tag + --title sort by title + --rm_blank 移除题中的空行 + --line LINE 两题之间的空行 + -r, --redownload 重新下载数据 +``` + +下载的数据保持在 ./leecode_problems.pk +转成的txt在 './leecode problems.txt' + ### xiami.py - 下载或播放高品质虾米音乐(xiami.com) diff --git a/leetcode_problems.py b/leetcode_problems.py new file mode 100755 index 0000000..f08a8bd --- /dev/null +++ b/leetcode_problems.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python + +import sys +import re +import os +import argparse +import requests +from lxml import html + +try: + import cPickle as pk +except ImportError: + import pickle as pk + +class LeetcodeProblems(object): + def get_problems_info(self): + leetcode_url = 'https://leetcode.com/problemset/algorithms' + res = requests.get(leetcode_url) + if not res.ok: + print('request error') + sys.exit() + cm = res.text + indexs = re.findall(r' (\d+)', cm) + problem_urls = ['https://leetcode.com' + url \ + for url in re.findall( + r'href="(/problems/.+?)"', cm)] + levels = re.findall(r" (.+?)", cm) + tinfos = zip(indexs, levels, problem_urls) + infos = [] + for info in tinfos: + res = requests.get(info[-1]) + if not res.ok: + print('request error') + sys.exit() + tree = html.fromstring(res.text) + title = tree.xpath('//meta[@property="og:title"]/@content')[0] + description = tree.xpath('//meta[@property="og:description"]/@content')[0] + if self.args.rm_blank: + description = re.sub(r'\n+', r'\n', description) + tags = tree.xpath('//div[@id="tags"]/following::a[@class="btn btn-xs btn-primary"]/text()') + infos.append( + { + 'title': title, + 'level': info[1], + 'index': int(info[0]), + 'description': description, + 'tags': tags + } + ) + + with open('leecode_problems.pk', 'wb') as g: + pk.dump(infos, g) + return infos + + def to_text(self, pm_infos): + if self.args.index: + key = 'index' + elif self.args.title: + key = 'title' + elif self.args.tag: + key = 'tags' + elif self.args.level: + key = 'level' + else: + key = 'index' + + infos = sorted(pm_infos, key=lambda i: i[key]) + + text_template = '## {index} - {title}\n' \ + '~{level}~ {tags}\n' \ + '{description}\n' + '\n' * self.args.line + text = '' + for info in infos: + text += text_template.format(**info) + + with open('leecode problems.txt', 'w') as g: + g.write(text) + + def run(self): + if os.path.exists('leecode_problems.pk') and not self.args.redownload: + with open('leecode_problems.pk', 'rb') as f: + pm_infos = pk.load(f) + else: + pm_infos = self.get_problems_info() + + print('find %s problems.' % len(pm_infos)) + self.to_text(pm_infos) + +def handle_args(argv): + p = argparse.ArgumentParser(description='extract all leecode problems to location') + p.add_argument('--index', action='store_true', help='sort by index') + p.add_argument('--level', action='store_true', help='sort by level') + p.add_argument('--tag', action='store_true', help='sort by tag') + p.add_argument('--title', action='store_true', help='sort by title') + p.add_argument('--rm_blank', action='store_true', help='remove blank') + p.add_argument('--line', action='store', type=int, default=10, help='blank of two problems') + p.add_argument('-r', '--redownload', action='store_true', help='redownload data') + args = p.parse_args(argv[1:]) + return args + +def main(argv): + args = handle_args(argv) + x = LeetcodeProblems() + x.args = args + x.run() + +if __name__ == '__main__': + argv = sys.argv + main(argv) From 28675143642ef54c3bbf7b1c7e42e19992f8b090 Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年6月30日 00:20:41 +0800 Subject: [PATCH 062/158] [leetcode] fix no find description --- leetcode_problems.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/leetcode_problems.py b/leetcode_problems.py index f08a8bd..73e7eba 100755 --- a/leetcode_problems.py +++ b/leetcode_problems.py @@ -1,11 +1,18 @@ #!/usr/bin/env python +# -*- coding=utf-8 -*- import sys import re import os import argparse import requests -from lxml import html +from lxml import html as lxml_html + +try: + import html +except ImportError: + import HTMLParser + html = HTMLParser.HTMLParser() try: import cPickle as pk @@ -23,7 +30,7 @@ def get_problems_info(self): indexs = re.findall(r' (\d+)', cm) problem_urls = ['https://leetcode.com' + url \ for url in re.findall( - r'href="(/problems/.+?)"', cm)] + r' Date: Wed, 1 Jul 2015 00:44:48 +0800 Subject: [PATCH 063/158] [leetcode] split("tbody>") --- leetcode_problems.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/leetcode_problems.py b/leetcode_problems.py index 73e7eba..1c94063 100755 --- a/leetcode_problems.py +++ b/leetcode_problems.py @@ -27,12 +27,14 @@ def get_problems_info(self): print('request error') sys.exit() cm = res.text - indexs = re.findall(r' (\d+)', cm) + cmt = cm.split('tbody>')[-2] + indexs = re.findall(r' (\d+)', cmt) problem_urls = ['https://leetcode.com' + url \ for url in re.findall( - r' Date: 2015年7月23日 11:28:57 +0800 Subject: [PATCH 064/158] [pan.baidu.com] fix 404 occurred at download. now downloading used web's useragent needs cookie. --- pan.baidu.com.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index 8d85b5c..d6a4a9e 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -852,7 +852,6 @@ def _download_do(infos): print '\n ++ download: #', s % (1, 97, infos['nn']), '/', \ s % (1, 97, infos['total_file']), '#', col - #cookie = 'BDUSS=%s' % ss.cookies.get('BDUSS') if args.aria2c: quiet = ' --quiet=true' if args.quiet else '' taria2c = ' -x %s -s %s' % (args.aria2c, args.aria2c) @@ -866,15 +865,18 @@ def _download_do(infos): % (quiet, taria2c, tlimit, infos['name'], infos['dir_'], 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" ' \ '--header "Referer:http://pan.baidu.com/disk/home" ' \ + '--header "%s" ' \ '"%s"' \ % (quiet, tlimit, infos['file'], - headers['User-Agent'], infos['dlink']) + headers['User-Agent'], cookie, infos['dlink']) status = os.system(cmd) exit = True From ea260e4fe57f22427d1c1ad1f09398a1baa40c60 Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年7月24日 13:19:51 +0800 Subject: [PATCH 065/158] [pan.baidu.com] add '-t 8s' for checking baidu 8s --- README.md | 1 + pan.baidu.com.py | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index de1554a..ee66079 100644 --- a/README.md +++ b/README.md @@ -448,6 +448,7 @@ jca 或 jobclearall # 清除 *全部任务* d -t dc,no # 下载并解密,不覆盖加密文件 dc -t no # 解密,不覆盖加密文件 d -t ie # ignore error, 忽略除Ctrl-C以外的下载错误 + d -t 8s # 检测文件是否是"百度8秒",如果是则不下载 p -t m3 # 播放流媒体(m3u8) s -t c # 连续转存 (如果转存出错,再次运行命令 # 可以从出错的地方开始,用于转存大量文件时) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index d6a4a9e..4e05a20 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -852,6 +852,10 @@ def _download_do(infos): print '\n ++ download: #', s % (1, 97, infos['nn']), '/', \ s % (1, 97, infos['total_file']), '#', col + if '8s' in args.type_ and is_wenxintishi(infos['dlink']): + print s % (1, 93, ' !! 百度8秒 !!') + return + if args.aria2c: quiet = ' --quiet=true' if args.quiet else '' taria2c = ' -x %s -s %s' % (args.aria2c, args.aria2c) @@ -919,11 +923,14 @@ def _play_do(infos): g.write(infos['m3u8']) infos['dlink'] = '/tmp/tmp_pan.baidu.com.py.m3u8' + cookie = 'Cookie: ' + '; '.join([ + 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"' \ - % (quiet, headers['User-Agent'], infos['dlink']) + % (quiet, headers['User-Agent'], cookie, infos['dlink']) os.system(cmd) timeout = 1 From 1b3b86f1cc55eda3790c7660712f915d2626491f Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年7月24日 13:26:34 +0800 Subject: [PATCH 066/158] [pan.baidu.com] update user-agent --- pan.baidu.com.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index 4e05a20..0673749 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -115,7 +115,7 @@ "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/42.0.2311.90 Safari/537.36" + "(KHTML, like Gecko) Chrome/44.0.2403.89 Safari/537.36" } ss = requests.session() @@ -861,10 +861,11 @@ def _download_do(infos): 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 "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" ' \ '"%s"' \ % (quiet, taria2c, tlimit, infos['name'], infos['dir_'], infos['dlink']) From c6136c74517166c2d8879576e88d50661d0714f4 Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年7月31日 10:08:03 +0800 Subject: [PATCH 067/158] [xiami] fix validation code --- xiami.py | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/xiami.py b/xiami.py index e9aa464..166c454 100755 --- a/xiami.py +++ b/xiami.py @@ -171,9 +171,20 @@ def login(self, email, password): 'LoginButton': '登录' } - url = 'http://www.xiami.com/web/login' - ss.post(url, data=data) - self.save_cookies() + url = 'https://login.xiami.com/web/login' + + for i in xrange(2): + res = ss.post(url, data=data) + if res.cookies.get('member_auth'): + self.save_cookies() + return True + else: + if 'checkcode' not in res.content: + return False + validate = self.get_validate(res.content) + data['validate'] = validate + + return False # {{{ code from https://github.com/ly0/xiami-tools/blob/master/xiami.py def login_taobao(self, username, password): @@ -251,16 +262,16 @@ def login_taobao(self, username, password): return # }}} - def get_validate(self): - url = 'https://login.xiami.com/coop/checkcode?forlogin=1&%s' \ - % int(time.time()) + def get_validate(self, cn): + #url = 'https://login.xiami.com/coop/checkcode?forlogin=1&%s' \ + #% int(time.time()) + url = re.search(r'src="(http.+checkcode.+?)"', cn).group(1) path = os.path.join(os.path.expanduser('~'), 'vcode.png') with open(path, 'w') as g: data = ss.get(url).content g.write(data) print " ++ 验证码已经保存至", s % (2, 91, path) - print s % (2, 92, u' 请输入验证码:') - validate = raw_input() + validate = raw_input(s % (2, 92, ' 请输入验证码: ')) return validate def save_cookies(self): From 127afb5cd51868a1ef598bdcd43e37030a013ebc Mon Sep 17 00:00:00 2001 From: PeterDing Date: Sat, 1 Aug 2015 17:55:14 +0800 Subject: [PATCH 068/158] [tumblr] kill zombie with threading --- tumblr.py | 429 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 264 insertions(+), 165 deletions(-) diff --git a/tumblr.py b/tumblr.py index 8f80342..dd33e07 100755 --- a/tumblr.py +++ b/tumblr.py @@ -7,16 +7,18 @@ import sys import re import json -from collections import deque +import collections +import Queue +from threading import Thread import requests -import urlparse import argparse import random -import subprocess import time import select -api_key = 'fuiKNFp9vQFvjLNvx4sUwti4Yb5yGutBN4Xh10LXZhhRKjWlV4' +API_KEY = 'fuiKNFp9vQFvjLNvx4sUwti4Yb5yGutBN4Xh10LXZhhRKjWlV4' + +PID_PATH = '/tmp/tumblr.py.pid' ############################################################ # wget exit status @@ -51,47 +53,94 @@ ss = requests.session() ss.headers.update(headers) -def check_queue(queue, cb): - for f in queue: - st = f[0].poll() - if st is not None: - if st == 0: cb(f[1]) - queue.remove(f) - return queue - -def sleep(size, num): - t = float(size) / num - time.sleep(t) - -def async(tasks, queue, dir_, run=None, cb=None, num=10): - queue = check_queue(queue, cb) - sleep(len(queue), num) - nsize = num - len(queue) - for i in xrange(nsize): - try: - task = tasks.popleft() - except IndexError: - break - f = run(task, dir_) - if f: queue.append(f) - #return tasks, queue - class Error(Exception): def __init__(self, msg): self.msg = msg def __str__(self): return self.msg +def play_do(items, quiet): + for item in items: + num = random.randint(0, 7) % 8 + col = s % (2, num + 90, item['durl']) + print ' ++ play:', col + quiet = ' --really-quiet' if quiet else '' + cmd = 'mpv%s --no-ytdl --cache-default 20480 --cache-secs 120 ' \ + '--http-header-fields "User-Agent:%s" ' \ + '"%s"' \ + % (quiet, headers['User-Agent'], item['durl']) + + os.system(cmd) + timeout = 1 + ii, _, _ = select.select([sys.stdin], [], [], timeout) + if ii: + sys.exit(0) + else: + pass + +def download_run(item): + filepath = os.path.join(item['dir_'], item['subdir'], item['filename']) + # if os.path.exists(filepath): + # return None + num = random.randint(0, 7) % 8 + col = s % (1, num + 90, filepath) + print ' ++ download: %s' % col + cmd = ' '.join([ + 'wget', '-c', '-q', + '-O', '%s.tmp' % filepath, + '--user-agent', '"%s"' % headers['User-Agent'], + '%s' % item['durl'].replace('http:', 'https:') + ]) + status = os.system(cmd) + return status, filepath + +def callback(filepath): + os.rename('%s.tmp' % filepath, filepath) + +def remove_downloaded_items(items): + N = len(items) + for i in range(N): + item = items.pop() + filepath = os.path.join(item['dir_'], item['subdir'], item['filename']) + if not os.path.exists(filepath): + items.appendleft(item) + +class Downloader(Thread): + def __init__(self, queue): + Thread.__init__(self) + self.queue = queue + self.daemon = True + + def run(self): + while True: + item = self.queue.get() + self.queue.task_done() + if not item: + break + status = download_run(item) + if not status: # file was downloaded. + continue + status, filepath = status + if status != 0: + print s % (1, 93, '[Error %s] at wget' % status), wget_es[status] + else: + callback(filepath) + class TumblrAPI(object): 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 - res = ss.get(api_url, params=params) - if not res: - raise Error(s % (1, 91, 'api request error.')) - - json_data = res.json() + params['api_key'] = API_KEY + while True: + try: + res = ss.get(api_url, params=params, timeout=10) + json_data = res.json() + break + except KeyboardInterrupt: + sys.exit() + except Exception as e: + print s % (1, 93, '[Error at requests]:'), e + time.sleep(5) if json_data['meta']['msg'].lower() != 'ok': raise Error(s % (1, 91, json_data['meta']['msg'])) @@ -102,7 +151,7 @@ def _info(self, base_hostname): def _photo(self, base_hostname, offset='', tag='', post_id='', to_items=True): def make_items(raw_data): - items = [] + items = collections.deque() for i in raw_data['posts']: index = 1 if i.get('photos'): @@ -136,7 +185,7 @@ def make_items(raw_data): def _audio(self, base_hostname, offset='', tag='', post_id='', to_items=True): def make_items(raw_data): - items = [] + items = collections.deque() for i in raw_data['posts']: durl = i['audio_url'].replace('http:', 'https:') filename = os.path.join( @@ -166,7 +215,7 @@ def make_items(raw_data): def _video(self, base_hostname, offset='', tag='', post_id='', to_items=True): def make_items(raw_data): - items = [] + items = collections.deque() for i in raw_data['posts']: if not i.get('video_url'): continue @@ -197,14 +246,10 @@ def make_items(raw_data): return raw_data class Tumblr(TumblrAPI): - def __init__(self, args): + def __init__(self, args, url): self.args = args - self.queue = [] - self.tasks = deque() self.offset = self.args.offset - self.processes = int(self.args.processes) - if self.args.play: - self.download = self.play + self.make_items = self.parse_urls(url) def save_json(self): with open(self.json_path, 'w') as g: @@ -223,7 +268,9 @@ def init_infos(self, base_hostname, target_type, tag=''): else: self.json_path = os.path.join(self.infos['dir_'], 'json.json') if os.path.exists(self.json_path): - self.offset = json.load(open(self.json_path))['offset'] - 20 + self.offset = json.load(open(self.json_path))['offset'] - 60 \ + if not self.args.update else self.args.offset + if self.offset < 0: self.offset = 0 else: self.infos['dir_'] = os.path.join(os.getcwd(), 'tumblr-%s' % tag) @@ -235,187 +282,160 @@ def init_infos(self, base_hostname, target_type, tag=''): else: self.json_path = os.path.join(self.infos['dir_'], 'json.json') if os.path.exists(self.json_path): - self.offset = json.load(open(self.json_path))['offset'] + self.offset = json.load(open(self.json_path))['offset'] \ + if not self.args.update else int(time.time()) - if not os.path.exists(os.path.join(self.infos['dir_'], target_type)): - if not self.args.play: - os.makedirs(os.path.join(self.infos['dir_'], target_type)) + if not os.path.exists(os.path.join(self.infos['dir_'], target_type)) \ + and not self.args.play: + os.makedirs(os.path.join(self.infos['dir_'], target_type)) if self.args.offset: self.offset = self.args.offset print s % (1, 92, '\n ## begin'), 'offset = %s' % self.offset - def download(self): - def run(i, dir_): - filepath = os.path.join(dir_, i['subdir'], i['filename']) - if os.path.exists(filepath): - return None - num = random.randint(0, 7) % 8 - col = s % (1, num + 90, filepath) - print ' ++ download: %s' % col - cmd = [ - 'wget', '-c', '-q', - '-O', '%s.tmp' % filepath, - '--user-agent', '"%s"' % headers['User-Agent'], - '%s' % i['durl'].replace('http:', 'https:') - ] - f = subprocess.Popen(cmd) - return f, filepath - - def callback(filepath): - os.rename('%s.tmp' % filepath, filepath) - - self.tasks.extend(self.infos['items']) - while True: - async(self.tasks, self.queue, dir_=self.infos['dir_'], run=run, - cb=callback, num=self.processes) - if len(self.tasks) <= self.processes: - break - - def play(self): - for item in self.infos['items']: - num = random.randint(0, 7) % 8 - col = s % (2, num + 90, item['durl']) - print ' ++ play:', col - quiet = ' --really-quiet' if self.args.quiet else '' - cmd = 'mpv%s --no-ytdl --cache-default 20480 --cache-secs 120 ' \ - '--http-header-fields "User-Agent:%s" ' \ - '"%s"' \ - % (quiet, headers['User-Agent'], item['durl']) - - os.system(cmd) - timeout = 1 - ii, _, _ = select.select([sys.stdin], [], [], timeout) - if ii: - sys.exit(0) - else: - pass - def download_photos_by_offset(self, base_hostname, post_id): self.init_infos(base_hostname, 'photos') - while True: - self.infos['items'] = self._photo( + def do(): + items = self._photo( base_hostname, offset=self.offset if not post_id else '', post_id=post_id) - if not self.infos['items']: - break + if not items: + return [] self.offset += 20 self.save_json() - self.download() - if post_id: break + return items + return do def download_photos_by_tag(self, base_hostname, tag): self.init_infos(base_hostname, 'photos', tag=tag) - while True: - self.info['items'] = self._photo(base_hostname, tag=tag, before=self.offset) - if not self.infos['items']: - break - self.offset = self.infos['items'][-1]['timestamp'] + def do(): + items = self._photo(base_hostname, tag=tag, before=self.offset) + if not items: + return [] + self.offset = items[-1]['timestamp'] self.save_json() - self.download() + return items + return do def download_videos_by_offset(self, base_hostname, post_id): self.init_infos(base_hostname, 'videos') - while True: - self.infos['items'] = self._video( + def do(): + items = self._video( base_hostname, offset=self.offset, post_id=post_id) - if not self.infos['items']: - break + if not items: + return [] self.offset += 20 - self.save_json() if not self.args.play else None - self.download() - if post_id: break + if not self.args.play: + self.save_json() + return items + return do def download_videos_by_tag(self, base_hostname, tag): self.init_infos(base_hostname, 'videos', tag) - while True: - self.infos['items'] = self._video( + def do(): + items = self._video( base_hostname, before=self.offset, tag=tag) - if not self.infos['items']: - break - self.offset = self.infos['items'][-1]['timestamp'] - self.save_json() if not self.args.play else None - self.download() + if not items: + return [] + self.offset = items[-1]['timestamp'] + if not self.args.play: + self.save_json() + return items + return do def download_audios_by_offset(self, base_hostname, post_id): self.init_infos(base_hostname, 'audios') - while True: - self.infos['items'] = self._audio( + def do(): + items = self._audio( base_hostname, offset=self.offset if not post_id else '', post_id=post_id) - if not self.infos['items']: - break + if not items: + return [] self.offset += 20 - self.save_json() if not self.args.play else None - self.download() - if post_id: break + if not self.args.play: + self.save_json() + return items + return do def download_audios_by_tag(self, base_hostname, tag): self.init_infos(base_hostname, 'audios', tag) - while True: - self.infos['items'] = self._audio( + def do(): + items = self._audio( base_hostname, before=self.offset, tag=tag) if not self.infos['items']: - break + return [] self.offset = self.infos['items'][-1]['timestamp'] - self.save_json() if not self.args.play else None - self.download() + if not self.args.play: + self.save_json() + return items + return do def download_photos(self, base_hostname, post_id='', tag=''): if tag: - self.download_photos_by_tag(base_hostname, tag) + return self.download_photos_by_tag(base_hostname, tag) else: - self.download_photos_by_offset(base_hostname, post_id=post_id) + return self.download_photos_by_offset(base_hostname, post_id=post_id) def download_videos(self, base_hostname, post_id='', tag=''): if tag: - self.download_videos_by_tag(base_hostname, tag) + return self.download_videos_by_tag(base_hostname, tag) else: - self.download_videos_by_offset(base_hostname, post_id=post_id) + return self.download_videos_by_offset(base_hostname, post_id=post_id) def download_audios(self, base_hostname, post_id='', tag=''): if tag: - self.download_audios_by_tag(base_hostname, tag) + return self.download_audios_by_tag(base_hostname, tag) else: - self.download_audios_by_offset(base_hostname, post_id=post_id) + return self.download_audios_by_offset(base_hostname, post_id=post_id) def fix_photos(self, base_hostname): self.init_infos(base_hostname, 'photos') + t = os.listdir(os.path.join(self.infos['dir_'], 'photos')) t = [i[:i.find('_')] for i in t if i.endswith('.tmp')] - ltmp = list(set(t)) - for post_id in ltmp: - self.infos['items'] = self._photo(base_hostname, post_id=post_id) - self.download() - - def parse_urls(self, urls): - for url in urls: - if not url.startswith('http'): - raise Error(s % (1, 91, 'url must start with http:// or https://')) - - base_hostname = urlparse.urlparse(url).netloc - if self.args.check: - self.fix_photos(base_hostname) - continue + self.post_ids = list(set(t)) - if re.search(r'post/(\d+)', url): - post_id = re.search(r'post/(\d+)', url).group(1) + def do(): + if len(self.post_ids): + post_id = self.post_ids.pop() + return self._photo(base_hostname, post_id=post_id) else: - post_id = '' + return [] + return do + + def parse_urls(self, url): + _mod = re.search(r'(http://|https://|)(?P.+\.tumblr.com)', url) + if not _mod: + print s % ('[Error]:'), 'url is illegal' + sys.exit(1) + base_hostname = _mod.group('hostname') + if self.args.check: + return self.fix_photos(base_hostname) + + if re.search(r'post/(\d+)', url): + post_id = re.search(r'post/(\d+)', url).group(1) + else: + post_id = '' - if self.args.video: - self.download_videos(base_hostname, post_id=post_id, tag=self.args.tag) - elif self.args.audio: - self.download_audios(base_hostname, post_id=post_id, tag=self.args.tag) - else: - self.download_photos(base_hostname, post_id=post_id, tag=self.args.tag) + if self.args.video: + return self.download_videos(base_hostname, post_id=post_id, tag=self.args.tag) + elif self.args.audio: + return self.download_audios(base_hostname, post_id=post_id, tag=self.args.tag) + else: + return self.download_photos(base_hostname, post_id=post_id, tag=self.args.tag) -def main(argv): + def get_item_generator(self): + items = self.make_items() + for item in items: + item['dir_'] = self.infos['dir_'] + return items + +def args_handler(argv): p = argparse.ArgumentParser( description='download from tumblr.com') p.add_argument('xxx', type=str, nargs='*', help='命令对象.') @@ -436,12 +456,91 @@ def main(argv): p.add_argument('-t', '--tag', action='store', default=None, type=str, help='下载特定tag的图片, eg: -t beautiful') + p.add_argument('--stop', action='store_true', + help='stop') + p.add_argument('--update', action='store_true', + help='update new things') + p.add_argument('--redownload', action='store_true', + help='redownload all things') #global args args = p.parse_args(argv[1:]) xxx = args.xxx - tumblr = Tumblr(args) - tumblr.parse_urls(xxx) + if args.redownload: args.update = True + return args, xxx + +def boot_set(stop, end=False): + # process end + if end: + os.remove(PID_PATH) + return + + # exit, here + if stop: + if not os.path.exists(PID_PATH): + sys.exit() + + with open(PID_PATH, 'r') as f: + pids = f.read().strip(',').split(',') + for pid in pids: + if not os.path.exists('/proc/%s' % pid): + continue + os.kill(int(pid), 15) + + os.remove(PID_PATH) + sys.exit() + + # save pid + with open(PID_PATH, 'a') as g: + g.write('%s,' % os.getpid()) + +def main(argv): + args, xxx = args_handler(argv) + boot_set(args.stop) + + queue = Queue.Queue(maxsize=args.processes) + for i in range(args.processes): + thr = Downloader(queue) + thr.start() + + for url in xxx: + tumblr = Tumblr(args, url) + not_add = 0 + while True: + items = tumblr.get_item_generator() + if not items: + break + + # Check the downloaded items. + # It will be exited, if there is no new item to download + # in 5 loops, unless with --redownload + remove_downloaded_items(items) + if not args.redownload: + if not items: + not_add += 1 + if not_add> 5: + print s % (1, 93, '[Warning]:'), \ + 'There is nothing new to download in 5 loops.\n', \ + 'If you want to scan all resources, using --redownload\n' \ + 'or running the script again to next 5 loops.' + break + continue + else: + not_add = 0 + + if not args.play: + for item in items: + queue.put(item) + else: + play_do(items, args.quiet) + + while not queue.empty(): + time.sleep(2) + + for i in range(args.processes): + queue.put(None) + + boot_set(False, end=True) if __name__ == '__main__': argv = sys.argv From 54478dcfa074cfc670f5ba4cc7b949884929de85 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Sat, 1 Aug 2015 18:04:45 +0800 Subject: [PATCH 069/158] [tumblr] update --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index ee66079..393d56d 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,8 @@ python2-lxml 下载的数据保持在 ./leecode_problems.pk 转成的txt在 './leecode problems.txt' +--- + ### xiami.py - 下载或播放高品质虾米音乐(xiami.com) @@ -1239,6 +1241,9 @@ python2-requests (https://github.com/kennethreitz/requests) 支持连续下载,下载进度储存在下载文件夹内的 json.json。 +**正确退出程序使用 tumblr --stop** +**下载更新用 tumblr --update URL, 或 删除 json.json** + #### 参数: ``` @@ -1251,6 +1256,10 @@ python2-requests (https://github.com/kennethreitz/requests) -V, --video download videos -q, --quiet quiet +--stop 退出 +--update 下载新发布的东西 +--redownload 重新遍历所有的东西,如果有漏掉的东西则下载 + -f OFFSET, --offset OFFSET 从第offset个开始,只对 -V 有用。 ``` From c372e1a2a746a8d1100225761fc06c06c6f2641a Mon Sep 17 00:00:00 2001 From: PeterDing Date: Sat, 1 Aug 2015 18:25:09 +0800 Subject: [PATCH 070/158] [xiami] support xiami widget --- README.md | 3 +++ xiami.py | 13 +++++++++++++ 2 files changed, 16 insertions(+) diff --git a/README.md b/README.md index 393d56d..04eab80 100644 --- a/README.md +++ b/README.md @@ -164,6 +164,9 @@ xm d http://www.xiami.com/chart/index/c/2?spm=a1z1s.2943549.6827465.6.VrEAoY xm d http://www.xiami.com/genre/detail/gid/2?spm=a1z1s.3057857.6850221.1.g9ySan xm d http://www.xiami.com/genre/detail/sid/2970?spm=a1z1s.3057857.6850221.4.pkepgt +# 下载 widget (虾米播播) +xm d http://www.xiami.com/widget/player-multi?uid=4350663&sid=1774531852,378713,3294421,1771778464,378728,378717,378727,1773346501,&width=990&height=346&mainColor=e29833&backColor=60362a&widget_from=4350663 + # 下载落网期刊 # 分析落网期刊的音乐后,在虾米上搜索并下载 xm d http://www.luoo.net/music/706 diff --git a/xiami.py b/xiami.py index 166c454..8b7488f 100755 --- a/xiami.py +++ b/xiami.py @@ -490,6 +490,12 @@ def get_artist_id(url): elif 'luoo.net' in url: self.hack_luoo(url) + elif 'sid=' in url: + _mod = re.search(r'sid=([\d+,]+\d)', url) + if _mod: + song_ids = _mod.group(1).split(',') + self.download_songs(song_ids) + else: print(s % (2, 91, u' 请正确输入虾米网址.')) @@ -619,6 +625,13 @@ def download_song(self): #self.song_infos = [song_info] self.download(songs) + def download_songs(self, song_ids): + for song_id in song_ids: + self.song_id = song_id + songs = self.get_song(self.song_id) + self.download(songs) + + def download_album(self): songs = self.get_songs(self.album_id) song = songs[0] From 659a247b2490e223524b2da20f110ec2a2032a26 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Sun, 2 Aug 2015 11:34:34 +0800 Subject: [PATCH 071/158] [tumblr] change flowing output to static statistic output --- tumblr.py | 133 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 96 insertions(+), 37 deletions(-) diff --git a/tumblr.py b/tumblr.py index dd33e07..59e5d28 100755 --- a/tumblr.py +++ b/tumblr.py @@ -9,7 +9,7 @@ import json import collections import Queue -from threading import Thread +import threading import requests import argparse import random @@ -20,6 +20,13 @@ PID_PATH = '/tmp/tumblr.py.pid' +NET_ERRORS = 0 +UNCOMPLETION = 0 +DOWNLOAD_ERRORS = 0 +DOWNLOADS = 0 +COMPLETION = 0 +OFFSET = 0 + ############################################################ # wget exit status wget_es = { @@ -63,7 +70,7 @@ def play_do(items, quiet): for item in items: num = random.randint(0, 7) % 8 col = s % (2, num + 90, item['durl']) - print ' ++ play:', col + # print ' ++ play:', col quiet = ' --really-quiet' if quiet else '' cmd = 'mpv%s --no-ytdl --cache-default 20480 --cache-secs 120 ' \ '--http-header-fields "User-Agent:%s" ' \ @@ -78,15 +85,23 @@ def play_do(items, quiet): else: pass +def remove_downloaded_items(items): + N = len(items) + for i in range(N): + item = items.pop() + filepath = os.path.join(item['dir_'], item['subdir'], item['filename']) + if not os.path.exists(filepath): + items.appendleft(item) + def download_run(item): filepath = os.path.join(item['dir_'], item['subdir'], item['filename']) # if os.path.exists(filepath): # return None num = random.randint(0, 7) % 8 col = s % (1, num + 90, filepath) - print ' ++ download: %s' % col + # print ' ++ download: %s' % col cmd = ' '.join([ - 'wget', '-c', '-q', + 'wget', '-c', '-q', '-T', '10', '-O', '%s.tmp' % filepath, '--user-agent', '"%s"' % headers['User-Agent'], '%s' % item['durl'].replace('http:', 'https:') @@ -97,21 +112,17 @@ def download_run(item): def callback(filepath): os.rename('%s.tmp' % filepath, filepath) -def remove_downloaded_items(items): - N = len(items) - for i in range(N): - item = items.pop() - filepath = os.path.join(item['dir_'], item['subdir'], item['filename']) - if not os.path.exists(filepath): - items.appendleft(item) - -class Downloader(Thread): - def __init__(self, queue): - Thread.__init__(self) +class Downloader(threading.Thread): + def __init__(self, queue, lock): + threading.Thread.__init__(self) self.queue = queue self.daemon = True + self.lock = lock def run(self): + global UNCOMPLETION + global DOWNLOADS + global DOWNLOAD_ERRORS while True: item = self.queue.get() self.queue.task_done() @@ -122,8 +133,15 @@ def run(self): continue status, filepath = status if status != 0: - print s % (1, 93, '[Error %s] at wget' % status), wget_es[status] + # print s % (1, 93, '[Error %s] at wget' % status), wget_es[status] + self.lock.acquire() + UNCOMPLETION += 1 + DOWNLOAD_ERRORS += 1 + self.lock.release() else: + self.lock.acquire() + DOWNLOADS += 1 + self.lock.release() callback(filepath) class TumblrAPI(object): @@ -131,6 +149,7 @@ 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 + global NET_ERRORS while True: try: res = ss.get(api_url, params=params, timeout=10) @@ -139,7 +158,8 @@ def _request(self, base_hostname, target, type, params): except KeyboardInterrupt: sys.exit() except Exception as e: - print s % (1, 93, '[Error at requests]:'), e + NET_ERRORS += 1 # count errors + # print s % (1, 93, '[Error at requests]:'), e time.sleep(5) if json_data['meta']['msg'].lower() != 'ok': raise Error(s % (1, 91, json_data['meta']['msg'])) @@ -259,40 +279,52 @@ def save_json(self): def init_infos(self, base_hostname, target_type, tag=''): self.infos = {'host': base_hostname} if not tag: - self.infos['dir_'] = os.path.join(os.getcwd(), self.infos['host']) + dir_ = os.path.join(os.getcwd(), self.infos['host']) + json_path = os.path.join(dir_, 'json.json') - if not os.path.exists(self.infos['dir_']): + if not os.path.exists(dir_): if not self.args.play: - os.makedirs(self.infos['dir_']) - self.json_path = os.path.join(self.infos['dir_'], 'json.json') + os.makedirs(dir_) else: - self.json_path = os.path.join(self.infos['dir_'], 'json.json') - if os.path.exists(self.json_path): - self.offset = json.load(open(self.json_path))['offset'] - 60 \ + if os.path.exists(json_path): + self.offset = json.load(open(json_path))['offset'] - 60 \ if not self.args.update else self.args.offset if self.offset < 0: self.offset = 0 else: - self.infos['dir_'] = os.path.join(os.getcwd(), 'tumblr-%s' % tag) + dir_ = os.path.join(os.getcwd(), 'tumblr-%s' % tag) + json_path = os.path.join(dir_, 'json.json') - if not os.path.exists(self.infos['dir_']): + if not os.path.exists(dir_): if not self.args.play: - os.makedirs(self.infos['dir_']) - self.json_path = os.path.join(self.infos['dir_'], 'json.json') + os.makedirs(dir_) self.offset = int(time.time()) else: - self.json_path = os.path.join(self.infos['dir_'], 'json.json') - if os.path.exists(self.json_path): - self.offset = json.load(open(self.json_path))['offset'] \ + if os.path.exists(json_path): + self.offset = json.load(open(json_path))['offset'] \ if not self.args.update else int(time.time()) - if not os.path.exists(os.path.join(self.infos['dir_'], target_type)) \ - and not self.args.play: - os.makedirs(os.path.join(self.infos['dir_'], target_type)) + self.infos['dir_'] = dir_ + self.json_path = json_path + subdir = os.path.join(dir_, target_type) + if not os.path.exists(subdir) and not self.args.play: + os.makedirs(subdir) + + if not self.args.play: + global UNCOMPLETION + global COMPLETION + for fl in os.listdir(subdir): + if not fl.endswith('.tmp'): + COMPLETION += 1 + else: + UNCOMPLETION += 1 if self.args.offset: self.offset = self.args.offset - print s % (1, 92, '\n ## begin'), 'offset = %s' % self.offset + print s % (1, 92, '## begin'), 'offset = %s' % self.offset + print s % (1, 97, 'INFO:\n') + \ + 'D = Downloads, R = Repair_Need\n' + \ + 'C = Completion, NE = Net_Errors, O = Offset' def download_photos_by_offset(self, base_hostname, post_id): self.init_infos(base_hostname, 'photos') @@ -494,19 +526,43 @@ def boot_set(stop, end=False): with open(PID_PATH, 'a') as g: g.write('%s,' % os.getpid()) +def print_msg(tumblr, args): + global NET_ERRORS + global UNCOMPLETION + global COMPLETION + global DOWNLOADS + global DOWNLOAD_ERRORS + + msg = "\r%s, %s, %s, %s, %s " % \ + ( + 'D: ' + s % (1, 92, DOWNLOADS), + 'R: ' + s % (1, 93, UNCOMPLETION \ + if not args.check \ + else UNCOMPLETION - DOWNLOAD_ERRORS - DOWNLOADS), + 'C: ' + s % (1, 97, COMPLETION), + 'NE: ' + s % (1, 91, NET_ERRORS), + 'O: %s' % tumblr.offset + ) + sys.stdout.write(msg) + sys.stdout.flush() + def main(argv): args, xxx = args_handler(argv) boot_set(args.stop) + lock = threading.Lock() queue = Queue.Queue(maxsize=args.processes) + thrs = [] for i in range(args.processes): - thr = Downloader(queue) + thr = Downloader(queue, lock) thr.start() + thrs.append(thr) for url in xxx: tumblr = Tumblr(args, url) not_add = 0 while True: + print_msg(tumblr, args) items = tumblr.get_item_generator() if not items: break @@ -519,7 +575,7 @@ def main(argv): if not items: not_add += 1 if not_add> 5: - print s % (1, 93, '[Warning]:'), \ + print s % (1, 93, '\n[Warning]:'), \ 'There is nothing new to download in 5 loops.\n', \ 'If you want to scan all resources, using --redownload\n' \ 'or running the script again to next 5 loops.' @@ -540,6 +596,9 @@ def main(argv): for i in range(args.processes): queue.put(None) + for thr in thrs: + thr.join() + boot_set(False, end=True) if __name__ == '__main__': From 6af7bccfed478d91a54f5e90caaf7420d21533cf Mon Sep 17 00:00:00 2001 From: PeterDing Date: Mon, 3 Aug 2015 10:25:11 +0800 Subject: [PATCH 072/158] [tumblr] play without threading --- tumblr.py | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/tumblr.py b/tumblr.py index 59e5d28..eff271e 100755 --- a/tumblr.py +++ b/tumblr.py @@ -66,11 +66,20 @@ def __init__(self, msg): def __str__(self): return self.msg +def play(urls, args): + for url in urls: + tumblr = Tumblr(args, url) + while True: + items = tumblr.get_item_generator() + if not items: + break + play_do(items, args.quiet) + def play_do(items, quiet): for item in items: num = random.randint(0, 7) % 8 col = s % (2, num + 90, item['durl']) - # print ' ++ play:', col + print ' ++ play:', col quiet = ' --really-quiet' if quiet else '' cmd = 'mpv%s --no-ytdl --cache-default 20480 --cache-secs 120 ' \ '--http-header-fields "User-Agent:%s" ' \ @@ -97,8 +106,8 @@ def download_run(item): filepath = os.path.join(item['dir_'], item['subdir'], item['filename']) # if os.path.exists(filepath): # return None - num = random.randint(0, 7) % 8 - col = s % (1, num + 90, filepath) + # num = random.randint(0, 7) % 8 + # col = s % (1, num + 90, filepath) # print ' ++ download: %s' % col cmd = ' '.join([ 'wget', '-c', '-q', '-T', '10', @@ -550,6 +559,10 @@ def main(argv): args, xxx = args_handler(argv) boot_set(args.stop) + if args.play: + play(xxx, args) + boot_set(False, end=True) + lock = threading.Lock() queue = Queue.Queue(maxsize=args.processes) thrs = [] @@ -584,11 +597,8 @@ def main(argv): else: not_add = 0 - if not args.play: - for item in items: - queue.put(item) - else: - play_do(items, args.quiet) + for item in items: + queue.put(item) while not queue.empty(): time.sleep(2) From 539696bb43744c698fa0ff3a7860d74ca7a65a0c Mon Sep 17 00:00:00 2001 From: PeterDing Date: Mon, 3 Aug 2015 23:47:52 +0800 Subject: [PATCH 073/158] [tumblr] add a thread to show infos --- tumblr.py | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/tumblr.py b/tumblr.py index eff271e..c7a1bac 100755 --- a/tumblr.py +++ b/tumblr.py @@ -471,6 +471,8 @@ def parse_urls(self, url): return self.download_photos(base_hostname, post_id=post_id, tag=self.args.tag) def get_item_generator(self): + global OFFSET + OFFSET = self.offset items = self.make_items() for item in items: item['dir_'] = self.infos['dir_'] @@ -535,25 +537,28 @@ def boot_set(stop, end=False): with open(PID_PATH, 'a') as g: g.write('%s,' % os.getpid()) -def print_msg(tumblr, args): +def print_msg(check): global NET_ERRORS global UNCOMPLETION global COMPLETION global DOWNLOADS global DOWNLOAD_ERRORS - - msg = "\r%s, %s, %s, %s, %s " % \ - ( - 'D: ' + s % (1, 92, DOWNLOADS), - 'R: ' + s % (1, 93, UNCOMPLETION \ - if not args.check \ - else UNCOMPLETION - DOWNLOAD_ERRORS - DOWNLOADS), - 'C: ' + s % (1, 97, COMPLETION), - 'NE: ' + s % (1, 91, NET_ERRORS), - 'O: %s' % tumblr.offset - ) - sys.stdout.write(msg) - sys.stdout.flush() + global OFFSET + + while True: + msg = "\r%s, %s, %s, %s, %s " % \ + ( + 'D: ' + s % (1, 92, DOWNLOADS), + 'R: ' + s % (1, 93, UNCOMPLETION \ + if not check \ + else UNCOMPLETION - DOWNLOAD_ERRORS - DOWNLOADS), + 'C: ' + s % (1, 97, COMPLETION), + 'NE: ' + s % (1, 91, NET_ERRORS), + 'O: %s' % OFFSET + ) + sys.stdout.write(msg) + sys.stdout.flush() + time.sleep(2) def main(argv): args, xxx = args_handler(argv) @@ -571,11 +576,15 @@ def main(argv): thr.start() thrs.append(thr) + # massage thread + msg_thr = threading.Thread(target=print_msg, args=(args.check,)) + msg_thr.setDaemon(True) + msg_thr.start() + for url in xxx: tumblr = Tumblr(args, url) not_add = 0 while True: - print_msg(tumblr, args) items = tumblr.get_item_generator() if not items: break @@ -609,6 +618,8 @@ def main(argv): for thr in thrs: thr.join() + msg_thr._Thread__stop() + boot_set(False, end=True) if __name__ == '__main__': From c4b803bf2941db4916d3d6efc53efbddfff82aca Mon Sep 17 00:00:00 2001 From: PeterDing Date: Tue, 4 Aug 2015 10:13:43 +0800 Subject: [PATCH 074/158] [tumblr] update COMPLETION += DOWNLOADS --- tumblr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tumblr.py b/tumblr.py index c7a1bac..e1b3eb1 100755 --- a/tumblr.py +++ b/tumblr.py @@ -552,7 +552,7 @@ def print_msg(check): 'R: ' + s % (1, 93, UNCOMPLETION \ if not check \ else UNCOMPLETION - DOWNLOAD_ERRORS - DOWNLOADS), - 'C: ' + s % (1, 97, COMPLETION), + 'C: ' + s % (1, 97, COMPLETION + DOWNLOADS), 'NE: ' + s % (1, 91, NET_ERRORS), 'O: %s' % OFFSET ) From 1da2ba38330c515d90b476442ca8044d2c82726a Mon Sep 17 00:00:00 2001 From: PeterDing Date: Tue, 4 Aug 2015 17:12:02 +0800 Subject: [PATCH 075/158] [tumblr] add an initial interval at print_msg --- tumblr.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tumblr.py b/tumblr.py index e1b3eb1..fcc195d 100755 --- a/tumblr.py +++ b/tumblr.py @@ -111,9 +111,9 @@ def download_run(item): # print ' ++ download: %s' % col cmd = ' '.join([ 'wget', '-c', '-q', '-T', '10', - '-O', '%s.tmp' % filepath, + '-O', '"%s.tmp"' % filepath, '--user-agent', '"%s"' % headers['User-Agent'], - '%s' % item['durl'].replace('http:', 'https:') + '"%s"' % item['durl'].replace('http:', 'https:') ]) status = os.system(cmd) return status, filepath @@ -545,6 +545,8 @@ def print_msg(check): global DOWNLOAD_ERRORS global OFFSET + time.sleep(2) # initial interval + while True: msg = "\r%s, %s, %s, %s, %s " % \ ( From e98289135c67957d58a272403df5480538269ca1 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Sun, 9 Aug 2015 08:28:23 +0800 Subject: [PATCH 076/158] [tumblr] PID_PATH safe --- tumblr.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tumblr.py b/tumblr.py index fcc195d..7bc0ac1 100755 --- a/tumblr.py +++ b/tumblr.py @@ -515,7 +515,8 @@ def args_handler(argv): def boot_set(stop, end=False): # process end if end: - os.remove(PID_PATH) + if os.path.exists(PID_PATH): + os.remove(PID_PATH) return # exit, here From 6fdf627120dce726ba8fd719ea42c3439ee946f0 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Sun, 9 Aug 2015 09:48:11 +0800 Subject: [PATCH 077/158] [pan.baidu.com] fix 'login success but warning' at def login --- pan.baidu.com.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index 0673749..a0ca90c 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -293,7 +293,8 @@ def init(self): def save_img(url, ext): path = os.path.join(os.path.expanduser('~'), 'vcode.%s' % ext) with open(path, 'w') as g: - data = urllib.urlopen(url).read() + res = ss.get(url) + data = res.content g.write(data) print " ++ 验证码已保存至", s % (1, 97, path) input_code = raw_input(s % (2, 92, " 输入验证码: ")) @@ -349,16 +350,18 @@ def login(self, username, password): # Construct post body data = { - "staticpage": "http://www.baidu.com/cache/user/html/v3Jump.html", + "staticpage": "https://www.baidu.com/cache/user/html/v3Jump.html", "charset": "UTF-8", "token": token, - "tpl": "pp", + "tpl": "mn", "subpro": "", "apiver": "v3", "tt": int(time.time()), "codestring": "", "safeflg": "0", + "u": "https://www.baidu.com/", "isPhone": "", + "detect": "1", "quick_user": "0", "logintype": "dialogLogin", "logLoginType": "pc_loginDialog", @@ -368,11 +371,10 @@ def login(self, username, password): "username": username, "password": password_encoded, "verifycode": "", - "mem_pass": "on", "rsakey": str(rsakey), "crypttype": "12", - "ppui_logintime": "40228", - "callback": "parent.bd__pcbs__uvwly2", + "ppui_logintime": "155983", + "callback": "parent.bd__pcbs__pld7nd", } while True: @@ -384,7 +386,7 @@ 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': + if errno == '0' or ss.cookies.get('BDUSS'): break elif errno in ('257', '3', '6'): print s % (1, 91, ' ! Error %s:' % errno), \ @@ -392,13 +394,13 @@ 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, 'jpg') if codestring != "" else "" + verifycode = self.save_img(vcurl, 'gif') if codestring != "" else "" data['codestring'] = codestring data['verifycode'] = verifycode #self.save_cookies() else: print s % (1, 91, ' ! Error %s:' % errno), \ - login_error_msg[errno] + login_error_msg.get(errno, "unknow, please feedback to author") sys.exit(1) def save_cookies(self, username=None, on=0, tocwd=False): From b86571ad11b7d78bac0c9a8b6a432580c533d43f Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年8月11日 16:30:07 +0800 Subject: [PATCH 078/158] [tumblr] update boot_set; add reset_statistic_params --- tumblr.py | 56 ++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 45 insertions(+), 11 deletions(-) diff --git a/tumblr.py b/tumblr.py index 7bc0ac1..f589e5a 100755 --- a/tumblr.py +++ b/tumblr.py @@ -15,11 +15,13 @@ import random import time import select +import cPickle as pk API_KEY = 'fuiKNFp9vQFvjLNvx4sUwti4Yb5yGutBN4Xh10LXZhhRKjWlV4' PID_PATH = '/tmp/tumblr.py.pid' +# statistic parameters NET_ERRORS = 0 UNCOMPLETION = 0 DOWNLOAD_ERRORS = 0 @@ -66,6 +68,21 @@ def __init__(self, msg): def __str__(self): return self.msg +def reset_statistic_params(): + global NET_ERRORS + global UNCOMPLETION + global DOWNLOAD_ERRORS + global DOWNLOADS + global COMPLETION + global OFFSET + + NET_ERRORS = 0 + UNCOMPLETION = 0 + DOWNLOAD_ERRORS = 0 + DOWNLOADS = 0 + COMPLETION = 0 + OFFSET = 0 + def play(urls, args): for url in urls: tumblr = Tumblr(args, url) @@ -513,30 +530,46 @@ def args_handler(argv): return args, xxx def boot_set(stop, end=False): + if not os.path.exists(PID_PATH): + has_pid_file = False + pids = set() + else: + has_pid_file = True + with open(PID_PATH) as f: + pids = pk.load(f) + # process end if end: - if os.path.exists(PID_PATH): - os.remove(PID_PATH) + if pids: + if len(pids) == 1: + os.remove(PID_PATH) + else: + pids.remove(os.getpid()) + if not pids: + os.remove(PID_PATH) + return + + with open(PID_PATH, 'w') as g: + pk.dump(pids, g) return # exit, here if stop: - if not os.path.exists(PID_PATH): + if not has_pid_file: sys.exit() - with open(PID_PATH, 'r') as f: - pids = f.read().strip(',').split(',') - for pid in pids: - if not os.path.exists('/proc/%s' % pid): - continue - os.kill(int(pid), 15) + for pid in pids: + if not os.path.exists('/proc/%s' % pid): + continue + os.kill(int(pid), 15) os.remove(PID_PATH) sys.exit() # save pid - with open(PID_PATH, 'a') as g: - g.write('%s,' % os.getpid()) + with open(PID_PATH, 'w') as g: + pids.add(os.getpid()) + pk.dump(pids, g) def print_msg(check): global NET_ERRORS @@ -585,6 +618,7 @@ def main(argv): msg_thr.start() for url in xxx: + reset_statistic_params() tumblr = Tumblr(args, url) not_add = 0 while True: From dfb8286ab6f0713f88a6ea3482bf03983d1b2cf2 Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年8月12日 19:51:32 +0800 Subject: [PATCH 079/158] [tumblr] gracely pass illegal url --- tumblr.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tumblr.py b/tumblr.py index f589e5a..441849e 100755 --- a/tumblr.py +++ b/tumblr.py @@ -469,8 +469,8 @@ def do(): def parse_urls(self, url): _mod = re.search(r'(http://|https://|)(?P.+\.tumblr.com)', url) if not _mod: - print s % ('[Error]:'), 'url is illegal' - sys.exit(1) + print s % (1, 91, '[Error]:'), 'url is illegal.', '\n' + url + return lambda: [] base_hostname = _mod.group('hostname') if self.args.check: return self.fix_photos(base_hostname) From 7dd675f8352e1561aa3fcad6880608e2f2ccd4b3 Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年8月12日 20:41:00 +0800 Subject: [PATCH 080/158] [tumblr] url needs to decode with utf8 --- tumblr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tumblr.py b/tumblr.py index 441849e..7b00f17 100755 --- a/tumblr.py +++ b/tumblr.py @@ -469,7 +469,7 @@ def do(): def parse_urls(self, url): _mod = re.search(r'(http://|https://|)(?P.+\.tumblr.com)', url) if not _mod: - print s % (1, 91, '[Error]:'), 'url is illegal.', '\n' + url + print s % (1, 91, '[Error]:'), 'url is illegal.', '\n' + url.decode('utf8', 'ignore') return lambda: [] base_hostname = _mod.group('hostname') if self.args.check: From dfdcaa75a8d2f2c360ab9db1cba0a8d2d5e03261 Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年8月29日 09:55:51 +0800 Subject: [PATCH 081/158] [tumblr] add signal handle for exit, removing --stop --- README.md | 3 +-- tumblr.py | 76 +++++++++++++++++++------------------------------------ 2 files changed, 27 insertions(+), 52 deletions(-) diff --git a/README.md b/README.md index 04eab80..a6e4572 100644 --- a/README.md +++ b/README.md @@ -1244,7 +1244,7 @@ python2-requests (https://github.com/kennethreitz/requests) 支持连续下载,下载进度储存在下载文件夹内的 json.json。 -**正确退出程序使用 tumblr --stop** +**正确退出程序使用 Ctrl-C** **下载更新用 tumblr --update URL, 或 删除 json.json** #### 参数: @@ -1259,7 +1259,6 @@ python2-requests (https://github.com/kennethreitz/requests) -V, --video download videos -q, --quiet quiet ---stop 退出 --update 下载新发布的东西 --redownload 重新遍历所有的东西,如果有漏掉的东西则下载 diff --git a/tumblr.py b/tumblr.py index 7b00f17..03d6524 100755 --- a/tumblr.py +++ b/tumblr.py @@ -15,7 +15,7 @@ import random import time import select -import cPickle as pk +import signal API_KEY = 'fuiKNFp9vQFvjLNvx4sUwti4Yb5yGutBN4Xh10LXZhhRKjWlV4' @@ -347,7 +347,7 @@ def init_infos(self, base_hostname, target_type, tag=''): if self.args.offset: self.offset = self.args.offset - print s % (1, 92, '## begin'), 'offset = %s' % self.offset + print s % (1, 92, '## begin:'), 'offset = %s,' % self.offset, base_hostname print s % (1, 97, 'INFO:\n') + \ 'D = Downloads, R = Repair_Need\n' + \ 'C = Completion, NE = Net_Errors, O = Offset' @@ -516,8 +516,6 @@ def args_handler(argv): p.add_argument('-t', '--tag', action='store', default=None, type=str, help='下载特定tag的图片, eg: -t beautiful') - p.add_argument('--stop', action='store_true', - help='stop') p.add_argument('--update', action='store_true', help='update new things') p.add_argument('--redownload', action='store_true', @@ -529,48 +527,6 @@ def args_handler(argv): if args.redownload: args.update = True return args, xxx -def boot_set(stop, end=False): - if not os.path.exists(PID_PATH): - has_pid_file = False - pids = set() - else: - has_pid_file = True - with open(PID_PATH) as f: - pids = pk.load(f) - - # process end - if end: - if pids: - if len(pids) == 1: - os.remove(PID_PATH) - else: - pids.remove(os.getpid()) - if not pids: - os.remove(PID_PATH) - return - - with open(PID_PATH, 'w') as g: - pk.dump(pids, g) - return - - # exit, here - if stop: - if not has_pid_file: - sys.exit() - - for pid in pids: - if not os.path.exists('/proc/%s' % pid): - continue - os.kill(int(pid), 15) - - os.remove(PID_PATH) - sys.exit() - - # save pid - with open(PID_PATH, 'w') as g: - pids.add(os.getpid()) - pk.dump(pids, g) - def print_msg(check): global NET_ERRORS global UNCOMPLETION @@ -596,13 +552,35 @@ def print_msg(check): sys.stdout.flush() time.sleep(2) +def sighandler(signum, frame): + print s % (1, 91, " !! Signal:"), signum + #print s % (1, 91, " !! Frame: %s" % frame) + sys.exit(1) + +def handle_signal(): + signal.signal(signal.SIGBUS, sighandler) + signal.signal(signal.SIGHUP, sighandler) + # http://stackoverflow.com/questions/14207708/ioerror-errno-32-broken-pipe-python + signal.signal(signal.SIGPIPE, signal.SIG_DFL) + signal.signal(signal.SIGQUIT, sighandler) + signal.signal(signal.SIGSYS, sighandler) + + signal.signal(signal.SIGABRT, sighandler) + signal.signal(signal.SIGFPE, sighandler) + signal.signal(signal.SIGILL, sighandler) + signal.signal(signal.SIGINT, sighandler) + signal.signal(signal.SIGSEGV, sighandler) + signal.signal(signal.SIGTERM, sighandler) + def main(argv): + # Only main thread can capture signal, + # all child threads not be affected + # whether they are daemonic or not. + handle_signal() args, xxx = args_handler(argv) - boot_set(args.stop) if args.play: play(xxx, args) - boot_set(False, end=True) lock = threading.Lock() queue = Queue.Queue(maxsize=args.processes) @@ -657,8 +635,6 @@ def main(argv): msg_thr._Thread__stop() - boot_set(False, end=True) - if __name__ == '__main__': argv = sys.argv main(argv) From 05345c3d929bbb6e5fc7aea2eecb2fe691f99e7a Mon Sep 17 00:00:00 2001 From: PeterDing Date: Tue, 8 Sep 2015 14:52:52 +0800 Subject: [PATCH 082/158] [tumblr] add a new line for signal handler --- tumblr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tumblr.py b/tumblr.py index 03d6524..2b79cda 100755 --- a/tumblr.py +++ b/tumblr.py @@ -553,7 +553,7 @@ def print_msg(check): time.sleep(2) def sighandler(signum, frame): - print s % (1, 91, " !! Signal:"), signum + print s % (1, 91, "\n !! Signal:"), signum #print s % (1, 91, " !! Frame: %s" % frame) sys.exit(1) From c91866ff631f3e72c450829a2bc997058d3ef1fa Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年9月15日 20:40:16 +0800 Subject: [PATCH 083/158] [xiami] remove taobao login; add add_member_auth --- README.md | 17 +++++++++-------- xiami.py | 48 +++++++++++++++++++++++++++++------------------- 2 files changed, 38 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index a6e4572..0ba9dda 100644 --- a/README.md +++ b/README.md @@ -78,9 +78,11 @@ xiami.py 是一个虾米音乐的命令行(CLI)客户端。提供登录、下载 初次使用需要登录 xm login (原xiami账号) -**支持淘宝账户** xm logintaobao +~~**支持淘宝账户** xm logintaobao~~ -**对于淘宝账户,登录后只保存有关虾米的cookies,删除了有关淘宝的cookies** +~~**对于淘宝账户,登录后只保存有关虾米的cookies,删除了有关淘宝的cookies**~~ + +**淘宝登录加密算法无法破解,需要手动获取cookies (方法见下 手动添加cookie登录)** **vip账户**支持高品质音乐的下载和播放。 @@ -103,12 +105,6 @@ login login username login username password -# 淘宝账号登录 -gt -logintaobao -logintaobao username -logintaobao username password - signout # 退出登录 d 或 download url1 url2 # 下载 @@ -139,6 +135,11 @@ xm login 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,再执行。 + # 退出登录 xm signout diff --git a/xiami.py b/xiami.py index 8b7488f..d1265b8 100755 --- a/xiami.py +++ b/xiami.py @@ -133,14 +133,13 @@ def __init__(self): def init(self): if os.path.exists(cookie_file): try: - t = json.loads(open(cookie_file).read()) - ss.cookies.update(t.get('cookies', t)) + cookies = json.load(open(cookie_file)) + ss.cookies.update(cookies.get('cookies', cookies)) if not self.check_login(): print s % (1, 91, ' !! cookie is invalid, please login\n') sys.exit(1) except: - g = open(cookie_file, 'w') - g.close() + open(cookie_file, 'w').close() print s % (1, 97, ' please login') sys.exit(1) else: @@ -153,12 +152,19 @@ def check_login(self): r = ss.get(url) if r.content: #print s % (1, 92, ' -- check_login success\n') - self.save_cookies() + # self.save_cookies() return True else: print s % (1, 91, ' -- login fail, please check email and password\n') return False + # manually, add cookies + # you must know how to get the cookie + def add_member_auth(self, member_auth): + member_auth = member_auth.rstrip(';') + self.save_cookies(member_auth) + ss.cookies.update({'member_auth': member_auth}) + def login(self, email, password): print s % (1, 97, '\n -- login') @@ -175,7 +181,7 @@ def login(self, email, password): for i in xrange(2): res = ss.post(url, data=data) - if res.cookies.get('member_auth'): + if ss.cookies.get('member_auth'): self.save_cookies() return True else: @@ -274,14 +280,12 @@ def get_validate(self, cn): validate = raw_input(s % (2, 92, ' 请输入验证码: ')) return validate - def save_cookies(self): + def save_cookies(self, member_auth=None): + if not member_auth: + member_auth = ss.cookies.get_dict()['member_auth'] with open(cookie_file, 'w') as g: - c = { - 'cookies': { - 'member_auth': ss.cookies.get_dict()['member_auth'] - } - } - g.write(json.dumps(c, indent=4, sort_keys=True)) + cookies = { 'cookies': { 'member_auth': member_auth } } + json.dump(cookies, g) def get_durl(self, id_): while True: @@ -1079,14 +1083,23 @@ def main(argv): comd = argv[1] xxx = args.xxx - if comd == 'login' or comd == 'g' \ - or comd == 'logintaobao' or comd == 'gt': + if comd == 'login' or comd == 'g': + # or comd == 'logintaobao' or comd == 'gt': + # taobao has updated login algorithms which is hard to hack + # so remove it. if len(xxx) < 1: email = raw_input(s % (1, 97, ' username: ') \ if comd == 'logintaobao' or comd == 'gt' \ else s % (1, 97, ' email: ')) password = getpass(s % (1, 97, ' password: ')) 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: ')) elif len(xxx) == 2: @@ -1095,10 +1108,7 @@ def main(argv): else: print s % (1, 91, ' login\n login email\n \ - login email password\n \ - logintaobao\n \ - logintaobao username\n \ - logintaobao username password') + login email password') x = xiami() if comd == 'logintaobao' or comd == 'gt': From 978ad77911bd7791efbcbc20edd9267674659132 Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年9月20日 14:09:06 +0800 Subject: [PATCH 084/158] [readme] update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0ba9dda..df09a71 100644 --- a/README.md +++ b/README.md @@ -1246,7 +1246,7 @@ python2-requests (https://github.com/kennethreitz/requests) 支持连续下载,下载进度储存在下载文件夹内的 json.json。 **正确退出程序使用 Ctrl-C** -**下载更新用 tumblr --update URL, 或 删除 json.json** +**下载 更新的图片或其他 用 tumblr --update URL, 或 删除 json.json** #### 参数: From 3d29026e378fa2cafc156df0efe3e02cd7368ff3 Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年9月26日 11:02:21 +0800 Subject: [PATCH 085/158] [tumblr] change threading to multiprocessing --- tumblr.py | 99 ++++++++++++++++++++----------------------------------- 1 file changed, 36 insertions(+), 63 deletions(-) diff --git a/tumblr.py b/tumblr.py index 2b79cda..7951cc7 100755 --- a/tumblr.py +++ b/tumblr.py @@ -8,8 +8,7 @@ import re import json import collections -import Queue -import threading +import multiprocessing import requests import argparse import random @@ -22,12 +21,12 @@ PID_PATH = '/tmp/tumblr.py.pid' # statistic parameters -NET_ERRORS = 0 -UNCOMPLETION = 0 -DOWNLOAD_ERRORS = 0 -DOWNLOADS = 0 -COMPLETION = 0 -OFFSET = 0 +NET_ERRORS = multiprocessing.Value('i', 0) +UNCOMPLETION = multiprocessing.Value('i', 0) +DOWNLOAD_ERRORS = multiprocessing.Value('i', 0) +DOWNLOADS = multiprocessing.Value('i', 0) +COMPLETION = multiprocessing.Value('i', 0) +OFFSET = multiprocessing.Value('i', 0) ############################################################ # wget exit status @@ -69,19 +68,12 @@ def __str__(self): return self.msg def reset_statistic_params(): - global NET_ERRORS - global UNCOMPLETION - global DOWNLOAD_ERRORS - global DOWNLOADS - global COMPLETION - global OFFSET - - NET_ERRORS = 0 - UNCOMPLETION = 0 - DOWNLOAD_ERRORS = 0 - DOWNLOADS = 0 - COMPLETION = 0 - OFFSET = 0 + NET_ERRORS.value = 0 + UNCOMPLETION.value = 0 + DOWNLOAD_ERRORS.value = 0 + DOWNLOADS.value = 0 + COMPLETION.value = 0 + OFFSET.value = 0 def play(urls, args): for url in urls: @@ -138,20 +130,16 @@ def download_run(item): def callback(filepath): os.rename('%s.tmp' % filepath, filepath) -class Downloader(threading.Thread): +class Downloader(multiprocessing.Process): def __init__(self, queue, lock): - threading.Thread.__init__(self) + super(Downloader, self).__init__() self.queue = queue self.daemon = True self.lock = lock def run(self): - global UNCOMPLETION - global DOWNLOADS - global DOWNLOAD_ERRORS while True: item = self.queue.get() - self.queue.task_done() if not item: break status = download_run(item) @@ -161,12 +149,12 @@ def run(self): if status != 0: # print s % (1, 93, '[Error %s] at wget' % status), wget_es[status] self.lock.acquire() - UNCOMPLETION += 1 - DOWNLOAD_ERRORS += 1 + UNCOMPLETION.value += 1 + DOWNLOAD_ERRORS.value += 1 self.lock.release() else: self.lock.acquire() - DOWNLOADS += 1 + DOWNLOADS.value += 1 self.lock.release() callback(filepath) @@ -175,7 +163,6 @@ 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 - global NET_ERRORS while True: try: res = ss.get(api_url, params=params, timeout=10) @@ -184,7 +171,7 @@ def _request(self, base_hostname, target, type, params): except KeyboardInterrupt: sys.exit() except Exception as e: - NET_ERRORS += 1 # count errors + NET_ERRORS.value += 1 # count errors # print s % (1, 93, '[Error at requests]:'), e time.sleep(5) if json_data['meta']['msg'].lower() != 'ok': @@ -336,13 +323,11 @@ def init_infos(self, base_hostname, target_type, tag=''): os.makedirs(subdir) if not self.args.play: - global UNCOMPLETION - global COMPLETION for fl in os.listdir(subdir): if not fl.endswith('.tmp'): - COMPLETION += 1 + COMPLETION.value += 1 else: - UNCOMPLETION += 1 + UNCOMPLETION.value += 1 if self.args.offset: self.offset = self.args.offset @@ -488,8 +473,7 @@ def parse_urls(self, url): return self.download_photos(base_hostname, post_id=post_id, tag=self.args.tag) def get_item_generator(self): - global OFFSET - OFFSET = self.offset + OFFSET.value = self.offset items = self.make_items() for item in items: item['dir_'] = self.infos['dir_'] @@ -520,7 +504,6 @@ def args_handler(argv): help='update new things') p.add_argument('--redownload', action='store_true', help='redownload all things') - #global args args = p.parse_args(argv[1:]) xxx = args.xxx @@ -528,34 +511,27 @@ def args_handler(argv): return args, xxx def print_msg(check): - global NET_ERRORS - global UNCOMPLETION - global COMPLETION - global DOWNLOADS - global DOWNLOAD_ERRORS - global OFFSET - time.sleep(2) # initial interval while True: msg = "\r%s, %s, %s, %s, %s " % \ ( - 'D: ' + s % (1, 92, DOWNLOADS), - 'R: ' + s % (1, 93, UNCOMPLETION \ + 'D: ' + s % (1, 92, DOWNLOADS.value), + 'R: ' + s % (1, 93, UNCOMPLETION.value \ if not check \ - else UNCOMPLETION - DOWNLOAD_ERRORS - DOWNLOADS), - 'C: ' + s % (1, 97, COMPLETION + DOWNLOADS), - 'NE: ' + s % (1, 91, NET_ERRORS), - 'O: %s' % OFFSET + else UNCOMPLETION.value - DOWNLOAD_ERRORS.value - DOWNLOADS.value), + 'C: ' + s % (1, 97, COMPLETION.value + DOWNLOADS.value), + 'NE: ' + s % (1, 91, NET_ERRORS.value), + 'O: %s' % OFFSET.value ) sys.stdout.write(msg) sys.stdout.flush() time.sleep(2) def sighandler(signum, frame): - print s % (1, 91, "\n !! Signal:"), signum - #print s % (1, 91, " !! Frame: %s" % frame) - sys.exit(1) + # print s % (1, 91, "\n !! Signal:"), signum + # print s % (1, 91, " !! Frame: %s" % frame) + sys.exit() def handle_signal(): signal.signal(signal.SIGBUS, sighandler) @@ -573,17 +549,14 @@ def handle_signal(): signal.signal(signal.SIGTERM, sighandler) def main(argv): - # Only main thread can capture signal, - # all child threads not be affected - # whether they are daemonic or not. handle_signal() args, xxx = args_handler(argv) if args.play: play(xxx, args) - lock = threading.Lock() - queue = Queue.Queue(maxsize=args.processes) + lock = multiprocessing.Lock() + queue = multiprocessing.Queue(maxsize=args.processes) thrs = [] for i in range(args.processes): thr = Downloader(queue, lock) @@ -591,8 +564,8 @@ def main(argv): thrs.append(thr) # massage thread - msg_thr = threading.Thread(target=print_msg, args=(args.check,)) - msg_thr.setDaemon(True) + msg_thr = multiprocessing.Process(target=print_msg, args=(args.check,)) + msg_thr.daemon = True msg_thr.start() for url in xxx: @@ -633,7 +606,7 @@ def main(argv): for thr in thrs: thr.join() - msg_thr._Thread__stop() + msg_thr.terminate() if __name__ == '__main__': argv = sys.argv From d2dcf316e5de23f5f230a2bc75e03243f04e0443 Mon Sep 17 00:00:00 2001 From: PeterDing Date: Mon, 5 Oct 2015 11:40:43 +0800 Subject: [PATCH 086/158] [pan.baidu.com] modify _task_display --- pan.baidu.com.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index a0ca90c..8a367f6 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -2447,19 +2447,20 @@ def add_tasks(self, urls, remotepath): } def _task_display(self, infos): + cross_line = '—' * int(os.popen('tput cols').read()) template = '%s %s\n' \ '%s %s\n' \ '%s %s\n' \ '%s %s\n' \ '%s %s\n' \ '%s %s\n' \ - '------------------------------\n' \ - % (s % (2, 97, ' id:'), s % (1, 97, "%s"), \ - s % (1, 97, ' status:'), s % (2, "%s", "%s"), \ - s % (1, 97, ' done:'), s % (3, 93, "%s"), \ - s % (2, 97, ' name:'), "%s", \ - s % (2, 97, ' path:'), "%s", \ - s % (2, 97, ' source:'), "%s") + '%s\n' \ + % (s % (2, 97, ' id:'), s % (1, 97, "%s"), \ + s % (1, 97, 'status:'), s % (1, "%s", "%s"), \ + s % (1, 97, ' done:'), s % (2, 93, "%s"), \ + s % (2, 97, ' name:'), "%s", \ + s % (2, 97, ' path:'), "%s", \ + s % (2, 97, 'source:'), "%s", cross_line) for i in infos: if i['result'] == 0: From 3f78ce24941b205f9c4b2e80ca58286023f3bbab Mon Sep 17 00:00:00 2001 From: PeterDing Date: Mon, 5 Oct 2015 11:47:20 +0800 Subject: [PATCH 087/158] [xiami] modify display_infos --- xiami.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/xiami.py b/xiami.py index d1265b8..bacba28 100755 --- a/xiami.py +++ b/xiami.py @@ -885,17 +885,16 @@ def hack_luoo(self, url): self.download_song() def display_infos(self, i, nn, n): - print '\n ----------------' - 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 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'] if i['durl_is_H'] == 'h': - print '>>', s % (1, 97, 'MP3-Quality:'), s % (1, 91, 'High') + print s % (1, 97, 'MP3-Quality:'), s % (1, 92, 'High') else: - print '>>', s % (1, 97, 'MP3-Quality:'), s % (1, 91, 'Low') - print '' + print s % (1, 97, 'MP3-Quality:'), s % (1, 91, 'Low') + 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: From 1f9cda0c6a0f7e6959cf5f2b76da65200fae4111 Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2015年10月30日 10:11:23 +0800 Subject: [PATCH 088/158] [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: 2015年10月30日 15:13:56 +0800 Subject: [PATCH 089/158] [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: 2015年11月17日 10:23:53 +0800 Subject: [PATCH 090/158] [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: 2015年12月20日 21:15:30 +0800 Subject: [PATCH 091/158] 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: 2015年12月26日 17:40:01 +0800 Subject: [PATCH 092/158] [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: 2016年2月15日 18:42:46 +0800 Subject: [PATCH 093/158] 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: 2016年2月26日 09:42:01 +0800 Subject: [PATCH 094/158] [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: 2016年2月26日 09:46:02 +0800 Subject: [PATCH 095/158] [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: 2016年3月31日 15:46:22 +0800 Subject: [PATCH 096/158] [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: 2016年5月21日 10:47:22 +0800 Subject: [PATCH 097/158] [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: 2016年5月28日 19:12:51 +0800 Subject: [PATCH 098/158] [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=unknow1円', 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=unknow1円', 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: 2016年5月28日 19:26:34 +0800 Subject: [PATCH 099/158] [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: 2016年5月29日 01:54:10 +0800 Subject: [PATCH 100/158] [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=unknow1円', 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=unknow1円', 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')], d @@ -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, d + p = { "channel": "chunlei", "app_id": "250528", @@ -981,7 +1036,7 @@ def _meta(self, file_list, d "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, d 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: 2016年5月31日 15:15:32 +0800 Subject: [PATCH 101/158] [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: 2016年6月20日 10:52:59 +0800 Subject: [PATCH 102/158] [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'> Date: Sat, 9 Jul 2016 02:34:21 +0000 Subject: [PATCH 103/158] Add Gitter badge --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index db3e167..955c96d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ ## iScript +[![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) + > *[L]* *[W]* *[LW]* 分别表示,在linux, windows, linux和windows 下通过测试。 From a4e462875201656cc094b253f2697a80830c520a Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2016年9月26日 17:55:18 +0800 Subject: [PATCH 104/158] [music.163.com] update api, issue #87, thanks @hoseahsu --- music.163.com.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/music.163.com.py b/music.163.com.py index 1392f25..20c4369 100755 --- a/music.163.com.py +++ b/music.163.com.py @@ -119,7 +119,7 @@ def get_durl(self, i): if i[q]: dfsId = str(i[q]['dfsId']) edfsId = encrypted_id(dfsId) - durl = u'http://m2.music.126.net/%s/%s.mp3' \ + durl = u'http://p1.music.126.net/%s/%s.mp3' \ % (edfsId, dfsId) return durl, q[0] return None, None From a6c31e7b063a2f22b1537c732d390e764bca732e Mon Sep 17 00:00:00 2001 From: PeterDing Date: Sat, 8 Oct 2016 11:01:47 +0800 Subject: [PATCH 105/158] [xiami] [Fix] #74 --- xiami.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/xiami.py b/xiami.py index 07bd707..4084a41 100755 --- a/xiami.py +++ b/xiami.py @@ -586,7 +586,11 @@ def get_songs(self, album_id, song_id=None): for i in xrange(len(tracks)): song_info = {} song_info['song_id'] = song_ids[i] - song_info['song_played'] = song_played[i] + if len(song_played)> i: + song_info['song_played'] = song_played[i] + else: + song_info['song_played'] = 0 + song_info['album_id'] = album_id song_info['song_url'] = u'http://www.xiami.com/song/' \ + song_ids[i] @@ -1063,7 +1067,14 @@ def save(self, urls): 'http://www.xiami.com/song/' + song_id result = self._save_do(song_id, 3, tags) + elif '/u/' in url: + user_id = re.search(r'/u/(\d+)', url).group(1) + print s % (1, 97, u'\n ++ save user:'), \ + 'http://www.xiami.com/u/' + user_id + result = self._save_do(user_id, 1, tags) + else: + result = -1 print(s % (2, 91, u' 请正确输入虾米网址.')) if result == 0: From b1a8c39020d82371baee5f3bea37a804d5dd4d89 Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2016年10月19日 00:04:39 +0800 Subject: [PATCH 106/158] [pan.baidu.com] [Fix] fix save_share and remove files, #89 --- pan.baidu.com.py | 106 +++++++++++++++++++++++------------------------ 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/pan.baidu.com.py b/pan.baidu.com.py index d7527a7..5c22edd 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -1467,36 +1467,34 @@ def _share_transfer(self, info): j = {'errno': 'file has exist'} return j - theaders = headers - theaders.update( - { - 'Referer': 'http://pan.baidu.com/share/link?shareid=%s&uk=%s' \ - % (self.shareid, self.uk) - } - ) - - p = { - "app_id": 250528, - "channel": "chunlei", - "clienttype": 0, - "web": 1, - "ondup": "overwrite", - "async": 1, - "from": self.uk, - "shareid": self.shareid, - "bdstoken": self._get_bdstoken() - } - data = "path=" \ - + urllib.quote_plus(info['remotepath'].encode('utf8')) \ - + '&' \ - + "filelist=" \ - + urllib.quote_plus( - '["%s"]' % info['path'].encode('utf8') + data = ('filelist=' \ + + urllib.quote_plus('["%s"]' % info['path'].encode('utf8')) \ + + '&path=' \ + + urllib.quote_plus(info['remotepath'].encode('utf8')) ) - url = 'http://pan.baidu.com/share/transfer' - r = ss.post(url, params=p, data=data, headers=theaders) + url = ('https://pan.baidu.com/share/transfer?' + 'shareid={}&from={}&bdstoken={}&channel=chunlei' + '&clienttype=0&web=1&app_id=250528'.format( + self.shareid, + self.uk, + self._get_bdstoken())) + + theaders = { + '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/', + 'X-Requested-With': 'XMLHttpRequest', + 'Connection': 'keep-alive', + } + r = ss.post(url, data=data, headers=theaders) j = r.json() + #if j['errno'] == 0: #return ENoError #else: @@ -1536,6 +1534,7 @@ def _get_share_list(self, info): def _get_share_infos(self, url, remotepath, infos): r = ss.get(url) + ss.cookies.update(r.cookies.get_dict()) html = r.content info = panbaiducom.get_web_fileinfo(html, url) @@ -1543,8 +1542,7 @@ def _get_share_infos(self, url, remotepath, infos): self.shareid = info['shareid'] self.bdstoken = info['bdstoken'] - fileinfo = info['fileinfo'] - j = json.loads(fileinfo) + j = info['file_list']['list'] isdirs = [x['isdir'] for x in j] paths = [x['path'] for x in j] z = zip(isdirs, paths) @@ -2025,6 +2023,7 @@ def _filemanager(self, opera, data): "channel": "chunlei", "clienttype": 0, "web": 1, + "async": "2", "opera": opera, "bdstoken": self._get_bdstoken(), } @@ -2847,12 +2846,8 @@ def get_web_fileinfo(cm, url): info['timestamp'] = re.search(r'timestamp="(\d+)"', 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['timestamp'] = re.search(r'yunData.TIMESTAMP = "(.+?)"', cm).group(1) - info['sign'] = re.search(r'yunData.SIGN = "(.+?)"', cm).group(1) + info_str = re.search(r'yunData.setData\((.+?)\);', cm).group(1) + info = json.loads(info_str) return info @@ -2861,27 +2856,24 @@ def get_params(self, path): html = r.content info = self.get_web_fileinfo(html, path) - uk = info['uk'] - shareid = info['shareid'] - timestamp = info['timestamp'] - sign = info['sign'] + self.uk = str(info['uk']) + self.shareid = str(info['shareid']) + self.timestamp = str(info['timestamp']) + self.sign = info['sign'] + self.bdstoken = info['bdstoken'] self.params = { - #"bdstoken": bdstoken, - "uk": uk, - "shareid": shareid, - "timestamp": timestamp, - "sign": sign, - "channel": "chunlei", - "clienttype": 0, - "web": 1, + "bdstoken": self.bdstoken, + "uk": self.uk, + "shareid": self.shareid, + "timestamp": self.timestamp, + "sign": self.sign, "channel": "chunlei", "clienttype": 0, "web": 1 } - fileinfo = info['fileinfo'] - j = json.loads(fileinfo) + j = info['file_list']['list'] self.infos.update({ 'name': j[0]['server_filename'].encode('utf8'), @@ -2893,14 +2885,22 @@ def get_params(self, path): }) def get_infos(self): - url = 'http://pan.baidu.com/share/download' - data = 'fid_list=["%s"]' % self.infos['fs_id'] + 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']) + ) while True: - r = ss.post(url, data=data, params=self.params) + r = ss.post(url, data=data) j = r.json() if not j['errno']: - dlink = fast_pcs_server(j['dlink'].encode('utf8')) + dlink = fast_pcs_server(j['list']['dlink'].encode('utf8')) self.infos['dlink'] = dlink if args.play: panbaiducom_HOME._play_do(self.infos) From b31f8c9f8afe055d53281f6c28df3feb6cb049ca Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2016年10月30日 21:08:06 +0800 Subject: [PATCH 107/158] [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 108/158] [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: 2016年12月18日 19:43:50 +0800 Subject: [PATCH 109/158] [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='/index.cgi/contrast/https://github.com/tenfar/iScript/compare/store_true', help='verify') From ed829740e8495e1758c1328f58a9f5d6dd0eb69f Mon Sep 17 00:00:00 2001 From: PeterDing Date: 2016年12月18日 20:12:42 +0800 Subject: [PATCH 110/158] [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='/index.cgi/contrast/https://github.com/tenfar/iScript/compare/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: 2016年12月18日 20:13:05 +0800 Subject: [PATCH 111/158] [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: 2016年12月25日 10:29:48 +0800 Subject: [PATCH 112/158] [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: 2016年12月25日 10:40:55 +0800 Subject: [PATCH 113/158] [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: 2016年12月25日 10:53:11 +0800 Subject: [PATCH 114/158] [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: 2016年12月25日 19:41:23 +0800 Subject: [PATCH 115/158] [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: 2017年1月15日 00:57:30 +0800 Subject: [PATCH 116/158] [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: 2017年1月22日 23:20:40 +0800 Subject: [PATCH 117/158] [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: 2017年3月11日 11:22:04 +0800 Subject: [PATCH 118/158] [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: 2017年3月11日 14:54:55 +0800 Subject: [PATCH 119/158] [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: 2017年3月11日 18:20:09 +0800 Subject: [PATCH 120/158] [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 121/158] [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'(.+?)<', html).group(1) d = collect_name dir_ = os.path.join(os.getcwdu(), d) self.dir_ = modificate_file_name_for_wget(dir_) - song_ids = re.findall('/song/(\w+)" title', html) + song_ids = re.findall(r'/song/(\w+)"\s+title', html) amount_songs = unicode(len(song_ids)) song_ids = song_ids[args.from_ - 1:] print(s % (2, 97, u'\n>> ' + amount_songs + u' 首歌曲将要下载.')) \ From 6bb3a42de3fd4a9680d0c5069e5d2b7bbc9baaa6 Mon Sep 17 00:00:00 2001 From: PeterDing <dfhayst@gmail.com> Date: Sat, 8 Apr 2017 13:17:33 +0800 Subject: [PATCH 122/158] [README.md] [Fix] fix table --- README.md | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index f28abdc..722852a 100644 --- a/README.md +++ b/README.md @@ -7,24 +7,21 @@ > ***windows用户可在babun (https://github.com/babun/babun) 下运行。*** - | | | ---------|---------|---------| -*[L]* | [leetcode_problems.py](#leetcode_problems.py) | 下载Leetcode的算法题 | -*[L]* | [xiami.py](#xiami.py) | 下载或播放高品质虾米音乐(xiami.com) | -*[L]* | [pan.baidu.com.py](#pan.baidu.com.py) | 百度网盘的下载、离线下载、上传、播放、转存、文件操作 | -*[L]* | [bt.py](#bt.py) | magnet torrent 互转、及 过滤敏.感.词 | -*[L]* | [115.py](#115.py) | 115网盘的下载和播放 | -*[L]* | [yunpan.360.cn.py](#yunpan.360.cn.py) | 360网盘的下载 | -*[L]* | [music.baidu.com.py](#music.baidu.com.py) | 下载或播放高品质百度音乐(music.baidu.com) | -*[L]* | [music.163.com.py](#music.163.com.py) | 下载或播放高品质网易音乐(music.163.com) | -*[L]* | [flv_cmd.py](#flv_cmd.py) | 基于在线服务的视频解析 client - 支持下载、播放 | -*[L]* | [tumblr.py](#tumblr.py) | 下载某个tumblr.com的所有图片、视频、音频 | -*[L]* | [unzip.py](#unzip.py) | 解决linux下unzip乱码的问题 | -*[L]* | [ed2k_search.py](#ed2k_search.py) | 基于 donkey4u.com 的emule搜索 | -*[L]* | [91porn.py](#91porn.py) | 下载或播放91porn | -*[L]* | [ThunderLixianExporter.user.js](#ThunderLixianExporter.user.js) | A fork of https://github.com/binux/ThunderLixianExporter - 增加了mpv和mplayer的导出。 | - | 待续 | | ---- + +*[L]* - [leetcode_problems.py](#leetcode_problems.py) - 下载Leetcode的算法题 +*[L]* - [xiami.py](#xiami.py) - 下载或播放高品质虾米音乐(xiami.com) +*[L]* - [pan.baidu.com.py](#pan.baidu.com.py) - 百度网盘的下载、离线下载、上传、播放、转存、文件操作 +*[L]* - [bt.py](#bt.py) - magnet torrent 互转、及 过滤敏.感.词 +*[L]* - [115.py](#115.py) - 115网盘的下载和播放 +*[L]* - [yunpan.360.cn.py](#yunpan.360.cn.py) - 360网盘的下载 +*[L]* - [music.baidu.com.py](#music.baidu.com.py) - 下载或播放高品质百度音乐(music.baidu.com) +*[L]* - [music.163.com.py](#music.163.com.py) - 下载或播放高品质网易音乐(music.163.com) +*[L]* - [flv_cmd.py](#flv_cmd.py) - 基于在线服务的视频解析 client - 支持下载、播放 +*[L]* - [tumblr.py](#tumblr.py) - 下载某个tumblr.com的所有图片、视频、音频 +*[L]* - [unzip.py](#unzip.py) - 解决linux下unzip乱码的问题 +*[L]* - [ed2k_search.py](#ed2k_search.py) - 基于 donkey4u.com 的emule搜索 +*[L]* - [91porn.py](#91porn.py) - 下载或播放91porn +*[L]* - [ThunderLixianExporter.user.js](#ThunderLixianExporter.user.js) - A fork of https://github.com/binux/ThunderLixianExporter - 增加了mpv和mplayer的导出。 --- From a669d913569427de358623ddf118310b6306408f Mon Sep 17 00:00:00 2001 From: Jason Chen <nzjachen@gmail.com> Date: Mon, 7 Aug 2017 22:25:10 +0800 Subject: [PATCH 123/158] music.baidu.com.py: fix album only can get 10 songs issue; adjust filename format --- music.baidu.com.py | 51 ++++++++++++++++------------------------------ 1 file changed, 18 insertions(+), 33 deletions(-) diff --git a/music.baidu.com.py b/music.baidu.com.py index 0185b46..c2c9016 100755 --- a/music.baidu.com.py +++ b/music.baidu.com.py @@ -85,13 +85,10 @@ def __init__(self, url): self.download = self.play if args.play else self.download - def get_songidlist(self, id_): - html = self.opener.open(self.template_album % id_).read() + def get_songidlist(self, song_id): + html = self.opener.open(self.template_album % song_id).read() songidlist = re.findall(r'/song/(\d+)', html) - api_json = self.opener.open(self.template_api % ','.join(songidlist)).read() - api_json = json.loads(api_json) - infos = api_json['data']['songList'] - return infos + return songidlist def get_cover(self, url): i = 1 @@ -122,53 +119,40 @@ def url_parser(self): elif '/song/' in self.url: self.song_id = re.search(r'/song/(\d+)', self.url).group(1) #print(s % (2, 92, u'\n -- 正在分析歌曲信息 ...')) - self.get_song_infos() + self.get_song_infos(self.song_id) else: print(s % (2, 91, u' 请正确输入baidu网址.')) + self.download() - def get_song_infos(self): - api_json = self.opener.open(self.template_api % self.song_id).read() + def get_song_infos(self, song_id, track_number=''): + api_json = self.opener.open(self.template_api % song_id).read() j = json.loads(api_json) song_info = {} song_info['song_id'] = unicode(j['data']['songList'][0]['songId']) - song_info['track'] = u'' + song_info['track'] = unicode(track_number) song_info['song_url'] = u'http://music.baidu.com/song/' + song_info['song_id'] song_info['song_name'] = modificate_text(j['data']['songList'][0]['songName']).strip() song_info['album_name'] = modificate_text(j['data']['songList'][0]['albumName']).strip() song_info['artist_name'] = modificate_text(j['data']['songList'][0]['artistName']).strip() song_info['album_pic_url'] = j['data']['songList'][0]['songPicRadio'] + song_info['file_name'] = song_info['artist_name'] + ' - ' + song_info['song_name'] + if song_info['track']: + song_info['file_name'] = song_info['track'].zfill(2) + '.' + song_info['file_name'] if args.flac: - song_info['file_name'] = song_info['song_name'] + ' - ' + song_info['artist_name'] + '.flac' + song_info['file_name'] = song_info['file_name'] + '.flac' else: - song_info['file_name'] = song_info['song_name'] + ' - ' + song_info['artist_name'] + '.mp3' + song_info['file_name'] = song_info['file_name'] + '.mp3' song_info['durl'] = j['data']['songList'][0]['songLink'] self.song_infos.append(song_info) - self.download() def get_album_infos(self): songidlist = self.get_songidlist(self.album_id) - z = z_index(songidlist) - ii = 1 + track_number = 1 for i in songidlist: - song_info = {} - song_info['song_id'] = unicode(i['songId']) - song_info['song_url'] = u'http://music.baidu.com/song/' + song_info['song_id'] - song_info['track'] = unicode(ii) - song_info['song_name'] = modificate_text(i['songName']).strip() - song_info['artist_name'] = modificate_text(i['artistName']).strip() - song_info['album_pic_url'] = i['songPicRadio'] - if args.flac: - song_info['file_name'] = song_info['track'].zfill(z) + '.' + song_info['song_name'] + ' - ' + song_info['artist_name'] + '.flac' - else: - song_info['file_name'] = song_info['track'].zfill(z) + '.' + song_info['song_name'] + ' - ' + song_info['artist_name'] + '.mp3' - song_info['album_name'] = modificate_text(i['albumName']).strip() \ - if i['albumName'] else modificate_text(self.song_infos[0]['album_name']) - song_info['durl'] = i['songLink'] - self.song_infos.append(song_info) - ii += 1 - d = modificate_text(self.song_infos[0]['album_name'] + ' - ' + self.song_infos[0]['artist_name']) + self.get_song_infos(i, track_number) + track_number += 1 + d = modificate_text(self.song_infos[0]['artist_name'] + ' - ' + self.song_infos[0]['album_name']) self.dir_ = os.path.join(os.getcwd().decode('utf8'), d) - self.download() def display_infos(self, i): print '\n ----------------' @@ -203,6 +187,7 @@ def download(self): file_name = os.path.join(dir_, t) if os.path.exists(file_name): ## if file exists, no get_durl ii += 1 + print(u'\n 文件已存在~') continue file_name_for_wget = file_name.replace('`', '\`') if 'zhangmenshiting.baidu.com' in i['durl'] or \ From 795b02bb67db3bb9b7339f658f049d04fb260146 Mon Sep 17 00:00:00 2001 From: PeterDing <dfhayst@gmail.com> Date: 2017年9月25日 18:50:19 +0800 Subject: [PATCH 124/158] [xiami.py] now, xiami requires cookies --- xiami.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/xiami.py b/xiami.py index 72ae035..759dcb0 100755 --- a/xiami.py +++ b/xiami.py @@ -661,7 +661,6 @@ def download_songs(self, song_ids): songs = self.get_song(self.song_id) self.download(songs) - def download_album(self): songs = self.get_songs(self.album_id) song = songs[0] @@ -946,10 +945,14 @@ def play(self, songs, nn=u'1', n=1): cmd = 'mpv --really-quiet ' \ '--cache 8146 ' \ '--user-agent "%s" ' \ - '--http-header-fields="Referer:http://img.xiami.com' \ - '/static/swf/seiya/1.4/player.swf?v=%s" ' \ + '--http-header-fields "Referer: http://img.xiami.com' \ + '/static/swf/seiya/1.4/player.swf?v=%s",' \ + '"Cookie: member_auth=%s" ' \ '"%s"' \ - % (headers['User-Agent'], int(time.time()*1000), durl) + % (headers['User-Agent'], + int(time.time()*1000), + ss.cookies.get('member_auth'), + durl) os.system(cmd) timeout = 1 ii, _, _ = select.select([sys.stdin], [], [], timeout) @@ -1008,9 +1011,13 @@ def download(self, songs, amount_songs=u'1', n=1): '-U "%s" ' \ '--header "Referer:http://img.xiami.com' \ '/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), - file_name_for_wget, durl) + % (quiet, headers['User-Agent'], + int(time.time()*1000), + ss.cookies.get('member_auth'), + file_name_for_wget, + durl) cmd = cmd.encode('utf8') status = os.system(cmd) if status != 0: # other http-errors, such as 302. From 931514f52bbdd84c4ce8483afd3cf770663db2cc Mon Sep 17 00:00:00 2001 From: PeterDing <dfhayst@gmail.com> Date: 2017年9月26日 11:19:19 +0800 Subject: [PATCH 125/158] [xiami.py] add new ruler for 320 kbps --- xiami.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xiami.py b/xiami.py index 759dcb0..f30db9f 100755 --- a/xiami.py +++ b/xiami.py @@ -923,7 +923,10 @@ def display_infos(self, i, nn, n, durl): 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 or '_h.mp3' in durl: + if 'm3.file.xiami.com' in durl \ + or 'm6.file.xiami.com' in durl \ + or '_h.mp3' in durl \ + or 'm320.xiami.net' in durl: return 'h' else: return 'l' From 48db583b456a8aa3b7208dc397532d4236baaa73 Mon Sep 17 00:00:00 2001 From: PeterDing <dfhayst@gmail.com> Date: 2017年10月24日 11:37:15 +0800 Subject: [PATCH 126/158] [xiami.py] [Update] get all songs from xiami "collect", #127 --- xiami.py | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/xiami.py b/xiami.py index f30db9f..df2a4eb 100755 --- a/xiami.py +++ b/xiami.py @@ -18,7 +18,7 @@ url_song = "http://www.xiami.com/song/%s" url_album = "http://www.xiami.com/album/%s" -url_collect = "http://www.xiami.com/collect/%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_lib_songs = "http://www.xiami.com/space/lib-song/u/%s/page/%s" @@ -676,13 +676,28 @@ def download_album(self): self.download(songs, amount_songs, args.from_) def download_collect(self): - html = ss.get(url_collect % self.collect_id).text + page = 1 + song_ids = [] + while True: + params = { + 'id': self.collect_id, + 'p': page, + 'limit': 50, + } + infos = ss.get(url_collect, params=params).json() + for info in infos['result']['data']: + song_ids.append(str(info['song_id'])) + + if infos['result']['total_page'] == page: + break + page += 1 + + html = ss.get('http://www.xiami.com/collect/%s' % self.collect_id).text html = html.split('<div id="wall"')[0] collect_name = re.search(r'<title>(.+?)<', html).group(1) d = collect_name dir_ = os.path.join(os.getcwdu(), d) self.dir_ = modificate_file_name_for_wget(dir_) - song_ids = re.findall(r'/song/(\w+)"\s+title', html) amount_songs = unicode(len(song_ids)) song_ids = song_ids[args.from_ - 1:] print(s % (2, 97, u'\n>> ' + amount_songs + u' 首歌曲将要下载.')) \ @@ -894,8 +909,8 @@ def hack_luoo(self, url): return None cn = r.content songs_info = re.findall(r'<p>(.+?)</p>\s+' - r'<p>Artist: (.+?)</p>\s+' - r'<p>Album: (.+?)</p>', cn) + r'<p>(?:Artist:|艺人:)(.+?)</p>\s+' + r'<p>(?:Album:|专辑:)(.+?)</p>', cn) # search song at xiami for info in songs_info: From 22cf8e272107c3e7c5d47721a8b87f4700bda814 Mon Sep 17 00:00:00 2001 From: PeterDing <dfhayst@gmail.com> Date: 2017年11月11日 11:12:48 +0800 Subject: [PATCH 127/158] [xiami.py] support listening ranking --- xiami.py | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/xiami.py b/xiami.py index df2a4eb..d5872df 100755 --- a/xiami.py +++ b/xiami.py @@ -457,9 +457,12 @@ def get_artist_id(url): elif '/u/' in url: self.user_id = re.search(r'/u/(\w+)', url).group(1) - code = raw_input('>> m # 该用户歌曲库.\n' \ + code = raw_input( + '>> m # 该用户歌曲库.\n' \ '>> c # 最近在听\n' \ '>> s # 分享的音乐\n' + '>> r # 歌曲试听排行 - 一周\n' + '>> rt # 歌曲试听排行 - 全部 \n' '>> rm # 私人电台:来源于"收藏的歌曲","收藏的专辑",\ "喜欢的艺人","收藏的精选集"\n' '>> rc # 虾米猜:基于试听行为所建立的个性电台\n>> ') @@ -472,6 +475,12 @@ def get_artist_id(url): url_shares = 'http://www.xiami.com' \ '/space/feed/u/%s/type/3/page/%s' % (self.user_id, '%s') self.download_user_shares(url_shares) + elif code == 'r': + url = 'http://www.xiami.com/space/charts/u/%s/c/song/t/week' % self.user_id + self.download_ranking_songs(url, 'week') + elif code == 'rt': + url = 'http://www.xiami.com/space/charts/u/%s/c/song/t/all' % self.user_id + self.download_ranking_songs(url, 'all') elif code == 'rm': #print(s % (2, 92, u'\n -- 正在分析该用户的虾米推荐 ...')) url_rndsongs = url_radio_my @@ -808,6 +817,25 @@ def download_user_shares(self, url_shares): if not shares: break page += 1 + def download_ranking_songs(self, url, tp): + d = modificate_text(u'%s 的试听排行 - %s' % (self.user_id, tp)) + dir_ = os.path.join(os.getcwdu(), d) + self.dir_ = modificate_file_name_for_wget(dir_) + page = 1 + n = 1 + while True: + html = ss.get(url + '/page/' + str(page)).text + song_ids = re.findall(r"play\('(\d+)'", html) + if not song_ids: + break + for song_id in song_ids: + songs = self.get_song(song_id) + self.download(songs, n=n) + self.html = '' + self.disc_description_archives = {} + n += 1 + page += 1 + def download_user_radio(self, url_rndsongs): d = modificate_text(u'%s 的虾米推荐' % self.user_id) dir_ = os.path.join(os.getcwdu(), d) From 69d9116c058fd0bbd66a2450614b9ab50cbf4f61 Mon Sep 17 00:00:00 2001 From: PeterDing <dfhayst@gmail.com> Date: Thu, 1 Mar 2018 14:55:27 +0800 Subject: [PATCH 128/158] [README.md] [Update] --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 722852a..bb09903 100644 --- a/README.md +++ b/README.md @@ -240,6 +240,8 @@ pan.baidu.com.py 是一个百度网盘的命令行客户端。 **支持多帐号登录** +**现在只支持[用cookie登录](#cookie_login)** + **支持cookie登录** **支持加密上传**, 需要 shadowsocks @@ -507,6 +509,7 @@ bp login username password # 一直用 bp login 即可 ``` + #### cookie 登录: 1. 打开 chrome 隐身模式窗口 From 82397ffb1bdf15bf89ec2f86183304c41af361f3 Mon Sep 17 00:00:00 2001 From: PeterDing <dfhayst@gmail.com> Date: 2018年3月24日 11:04:35 +0800 Subject: [PATCH 129/158] [pan.baidu.com.py] support https links for `save` --- 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 0e80037..f38df6c 100755 --- a/pan.baidu.com.py +++ b/pan.baidu.com.py @@ -1224,7 +1224,7 @@ def _upload_slice(self, piece=0, slice=DefaultSliceSize): fl = cStringIO.StringIO(__slice_block) files = {'file': ('file', fl, '')} data = MultipartEncoder(files) - theaders = headers + theaders = dict(headers) theaders['Content-Type'] = data.content_type url = 'https://c.pcs.baidu.com/rest/2.0/pcs/file' r = ss.post(url, params=p, data=data, verify=VERIFY, headers=theaders) @@ -2362,7 +2362,7 @@ def _get_torrent_info(self, path): } url = 'http://pan.baidu.com/rest/2.0/services/cloud_dl' - r = ss.post(url, params=p) + r = ss.get(url, params=p) j = r.json() if j.get('error_code'): print s % (1, 91, ' !! Error at _get_torrent_info:'), j['error_msg'] @@ -3313,7 +3313,7 @@ def handle_command(comd, xxx): url = xxx[0] x.save_inbox_share(url, remotepath, infos=infos) else: - url = re.search(r'(http://.+?.baidu.com/.+?)(#|$)', xxx[0]).group(1) + url = re.search(r'(https?://.+?.baidu.com/.+?)(#|$)', xxx[0]).group(1) url = url.replace('wap/link', 'share/link') x._secret_or_not(url) x.save_share(url, remotepath, infos=infos) From b700a40fa50b66855c04755af3654e341d445b2b Mon Sep 17 00:00:00 2001 From: PeterDing <dfhayst@gmail.com> Date: 2018年4月19日 17:37:55 +0800 Subject: [PATCH 130/158] [xiami.py] [Update] there is a new xiami ablum page html, #133 --- xiami.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/xiami.py b/xiami.py index d5872df..b3bbd2f 100755 --- a/xiami.py +++ b/xiami.py @@ -458,13 +458,13 @@ def get_artist_id(url): elif '/u/' in url: self.user_id = re.search(r'/u/(\w+)', url).group(1) code = raw_input( - '>> m # 该用户歌曲库.\n' \ - '>> c # 最近在听\n' \ + '>> m # 该用户歌曲库.\n' + '>> c # 最近在听\n' '>> s # 分享的音乐\n' '>> r # 歌曲试听排行 - 一周\n' '>> rt # 歌曲试听排行 - 全部 \n' - '>> rm # 私人电台:来源于"收藏的歌曲","收藏的专辑",\ - "喜欢的艺人","收藏的精选集"\n' + '>> rm # 私人电台:来源于"收藏的歌曲","收藏的专辑",' + ' "喜欢的艺人","收藏的精选集"\n' '>> rc # 虾米猜:基于试听行为所建立的个性电台\n>> ') if code == 'm': #print(s % (2, 92, u'\n -- 正在分析用户歌曲库信息 ...')) @@ -558,11 +558,8 @@ def get_songs(self, album_id, song_id=None): t = re.sub(r'<.+?>(\r\n|)', u'\n', t) album_description = t - #t = re.search(r'href="(.+?)" id="albumCover"', html1).group(1) - t = re.search(r'id="albumCover".+?"(http://.+?)" ', html1).group(1) - #tt = t.rfind('.') - #t = '%s_4%s' % (t[:tt], t[tt:]) - t = t.replace('_2.', '_4.') + t = re.search(r'pic:\'//(pic\.xiami\.net.+?)\'', html).group(1) # issue133 + t = 'http://' + t album_pic_url = t songs = [] From c1e410db6913d3af961dc7bd3d05fc26499e056f Mon Sep 17 00:00:00 2001 From: PeterDing <dfhayst@gmail.com> Date: Thu, 3 May 2018 22:46:57 +0800 Subject: [PATCH 131/158] [xiami.py] xiami updates web page --- xiami.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xiami.py b/xiami.py index b3bbd2f..50872ae 100755 --- a/xiami.py +++ b/xiami.py @@ -558,7 +558,7 @@ def get_songs(self, album_id, song_id=None): t = re.sub(r'<.+?>(\r\n|)', u'\n', t) album_description = t - t = re.search(r'pic:\'//(pic\.xiami\.net.+?)\'', html).group(1) # issue133 + t = re.search(r'//(pic\.xiami\.net.+?)"', html).group(1) # issue133 t = 'http://' + t album_pic_url = t From 76b4032193010c74bd727077b40be1db690910f8 Mon Sep 17 00:00:00 2001 From: PeterDing <dfhayst@gmail.com> Date: 2018年5月10日 16:50:30 +0800 Subject: [PATCH 132/158] [xiami.py] request is handled by _request --- xiami.py | 163 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 88 insertions(+), 75 deletions(-) diff --git a/xiami.py b/xiami.py index 50872ae..6edf3f8 100755 --- a/xiami.py +++ b/xiami.py @@ -116,7 +116,7 @@ def z_index(song_infos): class xiami(object): def __init__(self): self.dir_ = os.getcwdu() - self.template_record = 'http://www.xiami.com/count/playrecord?sid=%s' + self.template_record = 'https://www.xiami.com/count/playrecord?sid={song_id}&ishq=1&t={time}&object_id={song_id}&object_name=default&start_point=120&_xiamitoken={token}' self.collect_id = '' self.album_id = '' @@ -150,7 +150,7 @@ def init(self): def check_login(self): #print s % (1, 97, '\n -- check_login') url = 'http://www.xiami.com/task/signin' - r = ss.get(url) + r = self._request(url) if r.content: #print s % (1, 92, ' -- check_login success\n') # self.save_cookies() @@ -159,12 +159,32 @@ 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): + for _ in range(retry): + try: + headers = headers or ss.headers + resp = ss.request(method, url, headers=headers, data=data, timeout=timeout) + except Exception, err: + continue + + if not resp.ok: + raise Exception("response is not ok, status_code = %s" % resp.status_code) + + # save cookies + self.save_cookies() + + return resp + raise err + # manually, add cookies # you must know how to get the cookie - def add_member_auth(self, member_auth): - member_auth = member_auth.rstrip(';') - self.save_cookies(member_auth) - ss.cookies.update({'member_auth': member_auth}) + def add_cookies(self, cookies): + _cookies = {} + for item in cookies.strip('; ').split('; '): + k, v = item.split('=', 1) + _cookies[k] = v + self.save_cookies(_cookies) + ss.cookies.update(_cookies) def login(self, email, password): print s % (1, 97, '\n -- login') @@ -189,18 +209,14 @@ def login(self, email, password): '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, headers=hds, data=data, cookies=cookies) + res = self._request(url, headers=hds, data=data) if ss.cookies.get('member_auth'): - self.save_cookies() return True else: if 'checkcode' not in res.content: @@ -263,7 +279,7 @@ def login_taobao(self, username, password): if err_msg == u'请输入验证码' or err_msg == u'验证码错误,请重新输入': captcha_url = 'http://pin.aliyun.com/get_img?' \ 'identity=passport.alipay.com&sessionID=%s' % data['cid'] - tr = ss.get(captcha_url, headers=theaders) + tr = self._request(captcha_url, headers=theaders) path = os.path.join(os.path.expanduser('~'), 'vcode.jpg') with open(path, 'w') as g: img = tr.content @@ -280,7 +296,7 @@ def login_taobao(self, username, password): url = 'http://www.xiami.com/accounts/back?st=%s' \ % j['content']['data']['st'] - ss.get(url, headers=theaders) + self._request(url, headers=theaders) self.save_cookies() return @@ -292,17 +308,16 @@ def get_validate(self, cn): url = re.search(r'src="(http.+checkcode.+?)"', cn).group(1) path = os.path.join(os.path.expanduser('~'), 'vcode.png') with open(path, 'w') as g: - data = ss.get(url).content + data = self._request(url).content g.write(data) print " ++ 验证码已经保存至", s % (2, 91, path) validate = raw_input(s % (2, 92, ' 请输入验证码: ')) return validate - def save_cookies(self, member_auth=None): - if not member_auth: - member_auth = ss.cookies.get_dict()['member_auth'] + def save_cookies(self, cookies=None): + if not cookies: + cookies = ss.cookies.get_dict() with open(cookie_file, 'w') as g: - cookies = { 'cookies': { 'member_auth': member_auth } } json.dump(cookies, g) def get_durl(self, id_): @@ -310,11 +325,11 @@ def get_durl(self, id_): try: if not args.low: url = 'http://www.xiami.com/song/gethqsong/sid/%s' - j = ss.get(url % id_).json() + j = self._request(url % id_).json() t = j['location'] else: url = 'http://www.xiami.com/song/playlist/id/%s' - cn = ss.get(url % id_).text + cn = self._request(url % id_).text t = re.search(r'location>(.+?)</location', cn).group(1) if not t: return None row = t[0] @@ -325,8 +340,13 @@ def get_durl(self, id_): print s % (1, 91, ' |-- Error, get_durl --'), e time.sleep(5) - def record(self, id_): - ss.get(self.template_record % id_) + # FIXME, this request alway returns 405 + def record(self, song_id, album_id): + return + # token = ss.cookies.get('_xiamitoken', '') + # t = int(time.time() * 1000) + # self._request(self.template_record.format( + # song_id=song_id, album_id=album_id, token=token, time=t)) def get_cover(self, info): if info['album_name'] == self.cover_id: @@ -336,7 +356,7 @@ def get_cover(self, info): while True: url = info['album_pic_url'] try: - self.cover_data = ss.get(url).content + self.cover_data = self._request(url).content if self.cover_data[:5] != '<?xml': return self.cover_data except Exception as e: @@ -371,11 +391,11 @@ def lyric_parser(data): return data url = 'http://www.xiami.com/song/playlist/id/%s' % info['song_id'] - xml = ss.get(url).content + xml = self._request(url).content t = re.search('<lyric>(http.+?)</lyric>', xml) if not t: return None lyric_url = t.group(1) - data = ss.get(lyric_url).content.replace('\r\n', '\n') + data = self._request(lyric_url).content.replace('\r\n', '\n') data = lyric_parser(data) if data: return data.decode('utf8', 'ignore') @@ -384,7 +404,7 @@ def lyric_parser(data): def get_disc_description(self, album_url, info): if not self.html: - self.html = ss.get(album_url).text + self.html = self._request(album_url).text t = re.findall(re_disc_description, self.html) t = dict([(a, modificate_text(parser.unescape(b))) \ for a, b in t]) @@ -430,7 +450,7 @@ def url_parser(self, urls): elif '/artist/' in url or 'i.xiami.com' in url: def get_artist_id(url): - html = ss.get(url).text + html = self._request(url).text artist_id = re.search(r'artist_id = \'(\w+)\'', html).group(1) return artist_id @@ -531,7 +551,7 @@ def get_artist_id(url): print(s % (2, 91, u' 请正确输入虾米网址.')) def get_songs(self, album_id, song_id=None): - html = ss.get(url_album % album_id).text + html = self._request(url_album % album_id).text html = html.split('<div id="wall"')[0] html1, html2 = html.split('<div id="album_acts') @@ -641,7 +661,7 @@ def get_songs(self, album_id, song_id=None): return songs def get_song(self, song_id): - html = ss.get(url_song % song_id).text + html = self._request(url_song % song_id).text html = html.split('<div id="wall"')[0] t = re.search(r'href="/album/(.+?)" title="', html) if t: @@ -690,7 +710,7 @@ def download_collect(self): 'p': page, 'limit': 50, } - infos = ss.get(url_collect, params=params).json() + infos = self._request(url_collect, params=params).json() for info in infos['result']['data']: song_ids.append(str(info['song_id'])) @@ -698,7 +718,7 @@ def download_collect(self): break page += 1 - html = ss.get('http://www.xiami.com/collect/%s' % self.collect_id).text + html = self._request('http://www.xiami.com/collect/%s' % self.collect_id).text html = html.split('<div id="wall"')[0] collect_name = re.search(r'<title>(.+?)<', html).group(1) d = collect_name @@ -720,7 +740,7 @@ def download_artist_albums(self): ii = 1 album_ids = [] while True: - html = ss.get( + html = self._request( url_artist_albums % (self.artist_id, str(ii))).text t = re.findall(r'/album/(\w+)"', html) if album_ids == t: break @@ -737,7 +757,7 @@ def download_artist_albums(self): ii += 1 def download_artist_top_20_songs(self): - html = ss.get(url_artist_top_song % self.artist_id).text + html = self._request(url_artist_top_song % self.artist_id).text song_ids = re.findall(r'/song/(.+?)" title', html) artist_name = re.search( r'<p><a href="/index.cgi/contrast/https://github.com/artist/\w+">(.+?)<', html).group(1) @@ -756,7 +776,7 @@ def download_artist_top_20_songs(self): n += 1 def download_artist_radio(self): - html = ss.get(url_artist_top_song % self.artist_id).text + html = self._request(url_artist_top_song % self.artist_id).text artist_name = re.search( r'<p><a href="/index.cgi/contrast/https://github.com/artist/\w+">(.+?)<', html).group(1) d = modificate_text(artist_name + u' - radio') @@ -767,7 +787,7 @@ def download_artist_radio(self): % self.artist_id n = 1 while True: - xml = ss.get(url_artist_radio).text + xml = self._request(url_artist_radio).text song_ids = re.findall(r'<song_id>(\d+)', xml) for i in song_ids: songs = self.get_song(i) @@ -783,7 +803,7 @@ def download_user_songs(self, url, desc): ii = 1 n = 1 while True: - html = ss.get(url % (self.user_id, str(ii))).text + html = self._request(url % (self.user_id, str(ii))).text song_ids = re.findall(r'/song/(.+?)"', html) if song_ids: for i in song_ids: @@ -802,7 +822,7 @@ def download_user_shares(self, url_shares): self.dir_ = modificate_file_name_for_wget(dir_) page = 1 while True: - html = ss.get(url_shares % page).text + html = self._request(url_shares % page).text shares = re.findall(r'play.*\(\'\d+\'\)', html) for share in shares: if 'album' in share: @@ -821,7 +841,7 @@ def download_ranking_songs(self, url, tp): page = 1 n = 1 while True: - html = ss.get(url + '/page/' + str(page)).text + html = self._request(url + '/page/' + str(page)).text song_ids = re.findall(r"play\('(\d+)'", html) if not song_ids: break @@ -839,7 +859,7 @@ def download_user_radio(self, url_rndsongs): self.dir_ = modificate_file_name_for_wget(dir_) n = 1 while True: - xml = ss.get(url_rndsongs % self.user_id).text + xml = self._request(url_rndsongs % self.user_id).text song_ids = re.findall(r'<song_id>(\d+)', xml) for i in song_ids: songs = self.get_song(i) @@ -849,14 +869,14 @@ def download_user_radio(self, url_rndsongs): n += 1 def download_chart(self, type_): - html = ss.get('http://www.xiami.com/chart/index/c/%s' \ + html = self._request('http://www.xiami.com/chart/index/c/%s' \ % self.chart_id).text title = re.search(r'<title>(.+?)', 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: 2018年5月10日 16:51:07 +0800 Subject: [PATCH 133/158] [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: 2018年6月29日 12:01:22 +0800 Subject: [PATCH 134/158] [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: 2018年7月24日 19:35:39 +0800 Subject: [PATCH 135/158] [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'(.+?) i: - song_info['song_played'] = song_played[i] - else: - song_info['song_played'] = 0 - - song_info['album_id'] = album_id - song_info['song_url'] = u'http://www.xiami.com/song/' \ - + song_ids[i] - song_info['track'] = tracks[i] - song_info['cd_serial'] = disc - song_info['year'] = year - song_info['album_pic_url'] = album_pic_url - 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 - song_info['disc_description'] = disc_description - t = '%s\n\n%s%s' % (song_info['song_url'], - disc_description + u'\n\n' \ - if disc_description else '', - album_description) - song_info['comment'] = t - - songs.append(song_info) + songs = self._api.album(album_id) cd_serial_auth = int(songs[-1]['cd_serial'])> 1 - for i in xrange(len(songs)): - z = songs[i]['z'] - file_name = songs[i]['track'].zfill(z) + '.' \ - + songs[i]['song_name'] \ - + ' - ' + songs[i]['artist_name'] + '.mp3' - if cd_serial_auth: - songs[i]['file_name'] = ''.join([ - '[Disc-', - songs[i]['cd_serial'], - ' # ' + songs[i]['disc_description'] \ - if songs[i]['disc_description'] else '', '] ', - file_name]) - else: - songs[i]['file_name'] = file_name - - t = [i for i in songs if i['song_id'] == song_id] \ - if song_id else songs - songs = t + for song in songs: + self.make_file_name(song, cd_serial_auth=cd_serial_auth) + songs = [i for i in songs if i['song_id'] == song_id] \ + if song_id else songs return songs def get_song(self, song_id): - html = self._request(url_song % song_id).text - html = html.split('
(?: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: 2018年7月25日 13:37:34 +0800 Subject: [PATCH 137/158] [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 138/158] [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: 2018年8月11日 21:59:57 +0800 Subject: [PATCH 139/158] [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 140/158] [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 141/158] [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: 2019年1月21日 18:06:33 +0800 Subject: [PATCH 142/158] [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: 2019年1月24日 12:37:40 +0800 Subject: [PATCH 143/158] 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: 2019年3月18日 17:32:24 +0800 Subject: [PATCH 144/158] [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: 2019年3月18日 18:24:35 +0800 Subject: [PATCH 145/158] [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: 2019年9月14日 12:41:13 +0800 Subject: [PATCH 146/158] [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: 2019年9月14日 14:05:53 +0800 Subject: [PATCH 147/158] [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: 2019年10月11日 22:01:56 +0800 Subject: [PATCH 148/158] [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: 2020年4月24日 13:50:25 +0800 Subject: [PATCH 149/158] [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: 2020年6月23日 10:50:09 +0800 Subject: [PATCH 150/158] 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: 2020年6月23日 10:50:25 +0800 Subject: [PATCH 151/158] 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: 2020年10月18日 19:57:50 +0800 Subject: [PATCH 152/158] 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: 2020年10月18日 20:01:01 +0800 Subject: [PATCH 153/158] 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: 2020年11月16日 16:21:30 +0800 Subject: [PATCH 154/158] 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)", # 摩托罗拉 G4 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 领世旗舰III + "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", # 摩托罗拉 E3 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: 2020年11月16日 16:22:04 +0800 Subject: [PATCH 155/158] 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: 2020年12月10日 18:52:36 +0800 Subject: [PATCH 156/158] 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: 2020年12月14日 14:58:39 +0800 Subject: [PATCH 157/158] 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: 2021年1月21日 16:12:50 +0800 Subject: [PATCH 158/158] 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. 依赖 ```

AltStyle によって変換されたページ (->オリジナル) /