├── MacOS_ver ├── .k ├── requirements.txt ├── Stop.py ├── Refreshdlspeed.py ├── Notifications.py ├── Login.py ├── Worker.py └── main.py ├── Windows_ver ├── .k ├── icon.png ├── requirements.txt ├── Stop.py ├── Refreshdlspeed.py ├── Notifications.py ├── Login.py ├── Worker.py └── main.py ├── mac_example.png ├── win_example.png ├── LICENSE ├── README.md └── .gitignore /MacOS_ver/.k: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Windows_ver/.k: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /mac_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/natsuz0ra/SankakuDownloader/HEAD/mac_example.png -------------------------------------------------------------------------------- /win_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/natsuz0ra/SankakuDownloader/HEAD/win_example.png -------------------------------------------------------------------------------- /MacOS_ver/requirements.txt: -------------------------------------------------------------------------------- 1 | PyQt5 2 | beautifulsoup4 3 | requests 4 | websocket-client 5 | lxml -------------------------------------------------------------------------------- /Windows_ver/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/natsuz0ra/SankakuDownloader/HEAD/Windows_ver/icon.png -------------------------------------------------------------------------------- /Windows_ver/requirements.txt: -------------------------------------------------------------------------------- 1 | PyQt5 2 | beautifulsoup4 3 | requests 4 | websocket-client 5 | lxml -------------------------------------------------------------------------------- /Windows_ver/Stop.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import requests,json,sys,os,time 3 | if hasattr(sys, 'frozen'): 4 | os.environ['PATH'] = sys._MEIPASS + ";" + os.environ['PATH'] 5 | from PyQt5.QtCore import * 6 | from subprocess import call,PIPE,STDOUT 7 | 8 | ##终止下载 9 | class Stop(QThread): 10 | log_append = pyqtSignal(str) 11 | log_moveCursor = pyqtSignal() 12 | enable_start_button = pyqtSignal() 13 | disable_stop_button = pyqtSignal() 14 | set_startbutton_text = pyqtSignal(str) 15 | stop_worker = pyqtSignal() 16 | 17 | def __init__(self,now_page): 18 | super(Stop, self).__init__(None) 19 | self.now_page = now_page 20 | 21 | def run(self): 22 | call('taskkill /f /im aria2c_sd.exe',shell=True,stdin=PIPE,stdout=PIPE,stderr=STDOUT) 23 | self.stop_worker.emit() 24 | self.set_startbutton_text.emit('开始') 25 | self.enable_start_button.emit() 26 | self.disable_stop_button.emit() 27 | self.log_append.emit('已停止下载,你可以从第%d页重新开始' % self.now_page) 28 | self.log_moveCursor.emit() 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 natsuzora 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MacOS_ver/Stop.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import requests,json,sys,os,time 3 | if hasattr(sys, 'frozen'): 4 | os.environ['PATH'] = sys._MEIPASS + ";" + os.environ['PATH'] 5 | from PyQt5.QtCore import * 6 | from subprocess import call,PIPE,STDOUT 7 | 8 | ##终止下载 9 | class Stop(QThread): 10 | log_append = pyqtSignal(str) 11 | log_moveCursor = pyqtSignal() 12 | enable_start_button = pyqtSignal() 13 | disable_stop_button = pyqtSignal() 14 | set_startbutton_text = pyqtSignal(str) 15 | stop_worker = pyqtSignal() 16 | 17 | def __init__(self,now_page): 18 | super(Stop, self).__init__(None) 19 | self.now_page = now_page 20 | 21 | def run(self): 22 | call("kill -9 $(ps -ef|grep aria2c_sd |awk '$0 !~/grep/ {print $2}' |tr -s '\n' ' ')",shell=True,stdin=PIPE,stdout=PIPE,stderr=STDOUT) 23 | self.stop_worker.emit() 24 | self.set_startbutton_text.emit('开始') 25 | self.enable_start_button.emit() 26 | self.disable_stop_button.emit() 27 | self.log_append.emit('已停止下载,你可以从第%d页重新开始' % self.now_page) 28 | self.log_moveCursor.emit() 29 | 30 | -------------------------------------------------------------------------------- /MacOS_ver/Refreshdlspeed.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import requests,json,sys,os,time 3 | if hasattr(sys, 'frozen'): 4 | os.environ['PATH'] = sys._MEIPASS + ";" + os.environ['PATH'] 5 | from PyQt5.QtCore import * 6 | 7 | ##刷新下载速度 8 | class Refreshdlspeed(QThread): 9 | set_speedlabel = pyqtSignal(str) 10 | 11 | def __init__(self): 12 | super(Refreshdlspeed, self).__init__(None) 13 | 14 | def run(self): 15 | def bytes_conversion(number): 16 | symbols = ('K','M','G','T','P','E','Z','Y') 17 | prefix = dict() 18 | for i,s in enumerate(symbols): 19 | prefix[s] = 1<<(i+1) *10 20 | for s in reversed(symbols): 21 | if int(number) >= prefix[s]: 22 | value = float(number) / prefix[s] 23 | return '%.2f%s' %(value,s) 24 | return "%sB" %number 25 | jsonreq = json.dumps({'jsonrpc':'2.0','id':'qwer','method':'aria2.getGlobalStat','params': ['token:123654']}) 26 | while True: 27 | try: 28 | c = requests.post('http://localhost:7865/jsonrpc', data=jsonreq) 29 | speed = int(eval(c.text)['result']['downloadSpeed']) 30 | self.set_speedlabel.emit(bytes_conversion(speed)+'b/s') 31 | time.sleep(1.2)##避免刷新太快 32 | except: 33 | speed = '0Bb/s' 34 | self.set_speedlabel.emit(speed) 35 | -------------------------------------------------------------------------------- /Windows_ver/Refreshdlspeed.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import requests,json,sys,os,time 3 | if hasattr(sys, 'frozen'): 4 | os.environ['PATH'] = sys._MEIPASS + ";" + os.environ['PATH'] 5 | from PyQt5.QtCore import * 6 | 7 | ##刷新下载速度 8 | class Refreshdlspeed(QThread): 9 | set_speedlabel = pyqtSignal(str) 10 | 11 | def __init__(self): 12 | super(Refreshdlspeed, self).__init__(None) 13 | 14 | def run(self): 15 | def bytes_conversion(number): 16 | symbols = ('K','M','G','T','P','E','Z','Y') 17 | prefix = dict() 18 | for i,s in enumerate(symbols): 19 | prefix[s] = 1<<(i+1) *10 20 | for s in reversed(symbols): 21 | if int(number) >= prefix[s]: 22 | value = float(number) / prefix[s] 23 | return '%.2f%s' %(value,s) 24 | return "%sB" %number 25 | jsonreq = json.dumps({'jsonrpc':'2.0','id':'qwer','method':'aria2.getGlobalStat','params': ['token:123654']}) 26 | while True: 27 | try: 28 | c = requests.post('http://localhost:7865/jsonrpc', data=jsonreq) 29 | speed = int(eval(c.text)['result']['downloadSpeed']) 30 | self.set_speedlabel.emit(bytes_conversion(speed)+'b/s') 31 | time.sleep(1.2)##避免刷新太快 32 | except: 33 | speed = '0Bb/s' 34 | self.set_speedlabel.emit(speed) 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SankakuDownloader v2.0 2 | 3 | 2020.9.8更新 测试目前可用 4 | 5 | - 本工具用于批量下载Sankaku图站的acg图片 6 | - 使用Python编写,PyQt5构建UI,更易于使用 7 | - Windows版本使用PyInstaller封装,不需要安装Python即可使用 8 | - 捆绑Aria2下载工具,支持断点续传,更稳定的下载 9 | - 支持登入账号以使用完整的标签功能 10 | - 支持账号信息保存 11 | - 支持设定下载开始页,快速跳过已下载的部分 12 | - 当下载因为各种原因停止后都会保存下载失败的url,方便用户使用其他工具重新下载 13 | 14 | 15 | - 使用前请仔细阅读下面的说明与注意事项! 16 | 17 | ## Windows版使用方法 18 |  19 | 20 | 从[这里](https://github.com/natsuz0ra/SankakuDownloader/releases "这里")下载打包好的版本即可,解压即可使用! 21 | 如果需要从py源码运行可以参考下文,将命令中的3去掉即可(根据所配置的环境来)。 22 | 23 | ## MacOS版使用方法 24 |  25 | 26 | 很遗憾,MacOS版目前还没有找到打包后就可以直接跑起来的方法,目前遇到无法启动aria2以及文件无法读取写入等问题,可能还需要一定的时间(也可能不会有,目前对MacOS软件的相关机制还完全不了解)。 27 | 下面会介绍Python源码直接使用的方法,可能会比较繁琐 :( 28 | 29 | - 安装git 30 | - 运行以下命令: 31 | 32 | ```shell 33 | git clone https://github.com/natsuz0ra/SankakuDownloader.git 34 | cd SankakuDownloader/MacOS_ver(此处根据你的系统选择) 35 | pip3 install -r requirements.txt 36 | ``` 37 | - 待需要的包下载完毕后,点击[这里](https://github.com/aria2/aria2/releases/tag/release-1.35.0 "这里")下载MacOS版本的Aria2; 38 | - 解压,将名为aria2的可执行文件更名为aria2_sd,并放到MacOS_ver文件夹中; 39 | - 在MacOS_ver文件夹下运行以下命令运行: 40 | 41 | ```shell 42 | python3 main.py 43 | ``` 44 | - 尽情下载吧! 45 | 46 | ## 需要注意的地方 47 | - 标签请在chan上设定好后从URL复制到标签框里,必须是要chan所支持的才行 48 | - 标签的数目以及条件过滤等功能不是无限制使用的,登入后限制小一点(会员限制更少) 49 | - 未登入使用会有50页的限制,目前登入后可以解决(不排除未来会加会员限制) 50 | - 在登入后会自动关闭账号的敏感内容过滤选项(beta有,chan不清楚),不确定对下载是否有影响 51 | - 一些被版权和谐的标签(图片)应该也能够下载,只要chan上能获取到数目 52 | - Sankaku的图片服务器本身似乎有下载速度限制,1m/s是正常现象 53 | - 软件有时可能会自动退出,也许是网络问题(概率不大) 54 | - 因崩溃自动退出时无法保存失败列表 55 | - 不要让其他软件占用7865端口,有能力的大佬也可以下源码来改着用 56 | - 什么?1.0?那个只是个很简陋的东西就不用在意了233,而且也随着api的变动而失效 57 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | -------------------------------------------------------------------------------- /Windows_ver/Notifications.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import requests,sys,os,json,websocket 3 | if hasattr(sys, 'frozen'): 4 | os.environ['PATH'] = sys._MEIPASS + ";" + os.environ['PATH'] 5 | from PyQt5.QtCore import * 6 | from subprocess import call,PIPE,STDOUT 7 | 8 | this_page_downloaded = 0 9 | 10 | ##监控下载消息 11 | class Notifications(QThread): 12 | log_append = pyqtSignal(str) 13 | log_moveCursor = pyqtSignal() 14 | set_process = pyqtSignal(str,int) 15 | add_failed_info = pyqtSignal(str) 16 | 17 | def __init__(self,this_page_count,count): 18 | super(Notifications, self).__init__(None) 19 | self.this_page_count = this_page_count 20 | self.count = count 21 | 22 | def run(self): 23 | def on_message(ws, message): 24 | method = eval(message)['method'] 25 | gid = eval(message)['params'][0]['gid'] 26 | jsonreq = json.dumps({'jsonrpc':'2.0','id':'qwer','method':'aria2.tellStatus','params':['token:123654',gid]}) 27 | 28 | ##下载成功或失败时打印log,失败后还会记录 29 | if method == 'aria2.onDownloadComplete': 30 | c = requests.post('http://localhost:7865/jsonrpc', data=jsonreq) 31 | file_name = eval(c.text)['result']['files'][0]['path'].split('/')[-1] 32 | self.log_append.emit('下载成功:%s' % file_name) 33 | self.log_moveCursor.emit() 34 | elif method == 'aria2.onDownloadError': 35 | c = requests.post('http://localhost:7865/jsonrpc', data=jsonreq) 36 | file_name = eval(c.text)['result']['files'][0]['uris'][0]['uri'].replace('\\','').split('/')[-1].split('?')[0] 37 | file_url = eval(c.text)['result']['files'][0]['uris'][0]['uri'].replace('\\','') 38 | self.add_failed_info.emit(file_url) 39 | self.log_append.emit('下载失败:%s' % file_name) 40 | self.log_moveCursor.emit() 41 | else: 42 | return 43 | global this_page_downloaded 44 | this_page_downloaded += 1 45 | self.set_process.emit('+1',self.count) 46 | 47 | if this_page_downloaded == self.this_page_count:#如果当页下载完毕则关闭aria2 48 | call('taskkill /f /im aria2c_sd.exe',shell=True,stdin=PIPE,stdout=PIPE,stderr=STDOUT) 49 | this_page_downloaded = 0 50 | websocket.enableTrace(True) 51 | ws = websocket.WebSocketApp("ws://127.0.0.1:7865/jsonrpc",on_message=on_message) 52 | ws.run_forever() 53 | 54 | def stop(self): 55 | global this_page_downloaded 56 | this_page_downloaded = 0 57 | -------------------------------------------------------------------------------- /MacOS_ver/Notifications.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import requests,sys,os,json,websocket 3 | if hasattr(sys, 'frozen'): 4 | os.environ['PATH'] = sys._MEIPASS + ";" + os.environ['PATH'] 5 | from PyQt5.QtCore import * 6 | from subprocess import call,PIPE,STDOUT 7 | 8 | this_page_downloaded = 0 9 | 10 | ##监控下载消息 11 | class Notifications(QThread): 12 | log_append = pyqtSignal(str) 13 | log_moveCursor = pyqtSignal() 14 | set_process = pyqtSignal(str,int) 15 | add_failed_info = pyqtSignal(str) 16 | 17 | def __init__(self,this_page_count,count): 18 | super(Notifications, self).__init__(None) 19 | self.this_page_count = this_page_count 20 | self.count = count 21 | 22 | def run(self): 23 | def on_message(ws, message): 24 | method = eval(message)['method'] 25 | gid = eval(message)['params'][0]['gid'] 26 | jsonreq = json.dumps({'jsonrpc':'2.0','id':'qwer','method':'aria2.tellStatus','params':['token:123654',gid]}) 27 | 28 | ##下载成功或失败时打印log,失败后还会记录 29 | if method == 'aria2.onDownloadComplete': 30 | c = requests.post('http://localhost:7865/jsonrpc', data=jsonreq) 31 | file_name = eval(c.text)['result']['files'][0]['path'].split('/')[-1] 32 | self.log_append.emit('下载成功:%s' % file_name) 33 | self.log_moveCursor.emit() 34 | elif method == 'aria2.onDownloadError': 35 | c = requests.post('http://localhost:7865/jsonrpc', data=jsonreq) 36 | file_name = eval(c.text)['result']['files'][0]['uris'][0]['uri'].replace('\\','').split('/')[-1].split('?')[0] 37 | file_url = eval(c.text)['result']['files'][0]['uris'][0]['uri'].replace('\\','') 38 | self.add_failed_info.emit(file_url) 39 | self.log_append.emit('下载失败:%s' % file_name) 40 | self.log_moveCursor.emit() 41 | else: 42 | return 43 | global this_page_downloaded 44 | this_page_downloaded += 1 45 | self.set_process.emit('+1',self.count) 46 | 47 | if this_page_downloaded == self.this_page_count:#如果当页下载完毕则关闭aria2 48 | call("kill -9 $(ps -ef|grep aria2c_sd |awk '$0 !~/grep/ {print $2}' |tr -s '\n' ' ')",shell=True,stdin=PIPE,stdout=PIPE,stderr=STDOUT) 49 | this_page_downloaded = 0 50 | websocket.enableTrace(True) 51 | ws = websocket.WebSocketApp("ws://127.0.0.1:7865/jsonrpc",on_message=on_message) 52 | ws.run_forever() 53 | 54 | def stop(self): 55 | global this_page_downloaded 56 | this_page_downloaded = 0 57 | -------------------------------------------------------------------------------- /MacOS_ver/Login.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import requests,sys,re,os,json,time 3 | if hasattr(sys, 'frozen'): 4 | os.environ['PATH'] = sys._MEIPASS + ";" + os.environ['PATH'] 5 | from PyQt5.QtCore import * 6 | 7 | requests.adapters.DEFAULT_RETRIES = 5 8 | headers = {'User-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/17.17134'} 9 | 10 | ##用户登录处理 11 | class Login(QThread): 12 | log_append = pyqtSignal(str) 13 | log_moveCursor = pyqtSignal() 14 | set_infolabel = pyqtSignal(str) 15 | enable_login_button = pyqtSignal() 16 | disable_login_button = pyqtSignal() 17 | set_usernameedit_disable = pyqtSignal() 18 | set_passwordedit_disable = pyqtSignal() 19 | info_message = pyqtSignal(str,str) 20 | set_token = pyqtSignal(str) 21 | set_cookies = pyqtSignal(dict) 22 | 23 | def __init__(self,username,password): 24 | super(Login, self).__init__(None) 25 | self.username = username 26 | self.password = password 27 | 28 | ##用户登录 29 | def run(self): 30 | time.sleep(0.1) ##睡眠0.1s同步数据 31 | 32 | self.set_infolabel.emit('登入中...') 33 | 34 | login_headers = {'User-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36','content-type':'application/json'} 35 | login_url = "https://capi-v2.sankakucomplex.com/auth/token" 36 | login_data = {'login':self.username,'password':self.password} 37 | try: 38 | login_response = requests.post(login_url,data=json.dumps(login_data),headers=login_headers) 39 | except: 40 | self.info_message.emit('登入','登入失败,请检查网络状况') 41 | self.enable_login_button.emit() 42 | self.set_infolabel.emit('登入失败') 43 | self.log_append.emit('登入信息:失败,可能是网络状况不佳') 44 | self.log_moveCursor.emit() 45 | return 46 | 47 | ##登录chan主站,用于获取图片数 48 | chan_login_data = {'user[name]':self.username,'user[password]':self.password} 49 | chan_login_url = 'https://chan.sankakucomplex.com/user/authenticate' 50 | chan_login_session = requests.session() 51 | try: 52 | chan_login_response = chan_login_session.post(chan_login_url,headers=headers,data=chan_login_data) 53 | except: 54 | self.info_message.emit('登入','登入失败,请检查网络状况') 55 | self.enable_login_button.emit() 56 | self.set_infolabel.emit('登入失败') 57 | self.log_append.emit('登入信息:失败,可能是网络状况不佳') 58 | self.log_moveCursor.emit() 59 | return 60 | 61 | if login_response.status_code == 200 and chan_login_response.status_code == 200: 62 | token = re.findall(r'"access_token":"(.*?)"',login_response.text)[0] 63 | self.set_token.emit(token) 64 | self.set_cookies.emit(requests.utils.dict_from_cookiejar(chan_login_session.cookies)) 65 | ##禁止更改用户信息和登录按钮 66 | self.disable_login_button.emit() 67 | self.set_usernameedit_disable.emit() 68 | self.set_passwordedit_disable.emit() 69 | 70 | ##成功提示 71 | self.info_message.emit('登入','登入成功') 72 | self.set_infolabel.emit('登入成功') 73 | self.log_append.emit('登入信息:登入成功') 74 | self.log_moveCursor.emit() 75 | 76 | ##关闭过滤器 77 | disable_filter_headers = {'User-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36','content-type':'application/json','authorization':'Bearer '+token} 78 | disable_filter_url = "https://capi-v2.sankakucomplex.com/users/724964" 79 | disable_filter_data = {'user':{'filter_content':'false'}} 80 | disable_filter_request = requests.put(disable_filter_url,data=json.dumps(disable_filter_data),headers=disable_filter_headers) 81 | 82 | ##保存账号信息 83 | with open('user.ini','w') as user: 84 | user.write(self.username+'\n') 85 | user.write(self.password) 86 | else: 87 | ##失败提示 88 | self.info_message.emit('登入','登入失败,请检查账号密码是否正确') 89 | self.enable_login_button.emit() 90 | self.set_infolabel.emit('登入失败') 91 | self.log_append.emit('登入信息:失败,可能是账号密码有误') 92 | self.log_moveCursor.emit() -------------------------------------------------------------------------------- /Windows_ver/Login.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import requests,sys,re,os,json,time 3 | if hasattr(sys, 'frozen'): 4 | os.environ['PATH'] = sys._MEIPASS + ";" + os.environ['PATH'] 5 | from PyQt5.QtCore import * 6 | 7 | requests.adapters.DEFAULT_RETRIES = 5 8 | headers = {'User-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/17.17134'} 9 | 10 | ##用户登录处理 11 | class Login(QThread): 12 | log_append = pyqtSignal(str) 13 | log_moveCursor = pyqtSignal() 14 | set_infolabel = pyqtSignal(str) 15 | enable_login_button = pyqtSignal() 16 | disable_login_button = pyqtSignal() 17 | set_usernameedit_disable = pyqtSignal() 18 | set_passwordedit_disable = pyqtSignal() 19 | info_message = pyqtSignal(str,str) 20 | set_token = pyqtSignal(str) 21 | set_cookies = pyqtSignal(dict) 22 | 23 | def __init__(self,username,password): 24 | super(Login, self).__init__(None) 25 | self.username = username 26 | self.password = password 27 | 28 | ##用户登录 29 | def run(self): 30 | time.sleep(0.1) ##睡眠0.1s同步数据 31 | 32 | self.set_infolabel.emit('登入中...') 33 | 34 | login_headers = {'User-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36','content-type':'application/json'} 35 | login_url = "https://capi-v2.sankakucomplex.com/auth/token" 36 | login_data = {'login':self.username,'password':self.password} 37 | try: 38 | login_response = requests.post(login_url,data=json.dumps(login_data),headers=login_headers) 39 | except: 40 | self.info_message.emit('登入','登入失败,请检查网络状况') 41 | self.enable_login_button.emit() 42 | self.set_infolabel.emit('登入失败') 43 | self.log_append.emit('登入信息:失败,可能是网络状况不佳') 44 | self.log_moveCursor.emit() 45 | return 46 | 47 | ##登录chan主站,用于获取图片数 48 | chan_login_data = {'user[name]':self.username,'user[password]':self.password} 49 | chan_login_url = 'https://chan.sankakucomplex.com/user/authenticate' 50 | chan_login_session = requests.session() 51 | try: 52 | chan_login_response = chan_login_session.post(chan_login_url,headers=headers,data=chan_login_data) 53 | except: 54 | self.info_message.emit('登入','登入失败,请检查网络状况') 55 | self.enable_login_button.emit() 56 | self.set_infolabel.emit('登入失败') 57 | self.log_append.emit('登入信息:失败,可能是网络状况不佳') 58 | self.log_moveCursor.emit() 59 | return 60 | 61 | if login_response.status_code == 200 and chan_login_response.status_code == 200: 62 | token = re.findall(r'"access_token":"(.*?)"',login_response.text)[0] 63 | self.set_token.emit(token) 64 | self.set_cookies.emit(requests.utils.dict_from_cookiejar(chan_login_session.cookies)) 65 | ##禁止更改用户信息和登录按钮 66 | self.disable_login_button.emit() 67 | self.set_usernameedit_disable.emit() 68 | self.set_passwordedit_disable.emit() 69 | 70 | ##成功提示 71 | self.info_message.emit('登入','登入成功') 72 | self.set_infolabel.emit('登入成功') 73 | self.log_append.emit('登入信息:登入成功') 74 | self.log_moveCursor.emit() 75 | 76 | ##关闭过滤器 77 | disable_filter_headers = {'User-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36','content-type':'application/json','authorization':'Bearer '+token} 78 | disable_filter_url = "https://capi-v2.sankakucomplex.com/users/724964" 79 | disable_filter_data = {'user':{'filter_content':'false'}} 80 | disable_filter_request = requests.put(disable_filter_url,data=json.dumps(disable_filter_data),headers=disable_filter_headers) 81 | 82 | ##保存账号信息 83 | with open('user.ini','w') as user: 84 | user.write(self.username+'\n') 85 | user.write(self.password) 86 | else: 87 | ##失败提示 88 | self.info_message.emit('登入','登入失败,请检查账号密码是否正确') 89 | self.enable_login_button.emit() 90 | self.set_infolabel.emit('登入失败') 91 | self.log_append.emit('登入信息:失败,可能是账号密码有误') 92 | self.log_moveCursor.emit() -------------------------------------------------------------------------------- /MacOS_ver/Worker.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import time,requests,sys,datetime 3 | import math,re,os 4 | if hasattr(sys, 'frozen'): 5 | os.environ['PATH'] = sys._MEIPASS + ";" + os.environ['PATH'] 6 | from PyQt5.QtCore import * 7 | from subprocess import Popen,PIPE,STDOUT 8 | from bs4 import BeautifulSoup as bs 9 | 10 | requests.adapters.DEFAULT_RETRIES = 5 11 | this_page_count = this_page_downloaded = 0 #当前页图片数和已下载数 12 | count = downloaded_count = 0 #总图片数和已下载数 13 | 14 | headers = {'User-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/17.17134'} 15 | 16 | class Worker(QThread): 17 | log_append = pyqtSignal(str) 18 | set_processbar = pyqtSignal(int) 19 | set_process = pyqtSignal(str,int) 20 | log_moveCursor = pyqtSignal() 21 | enable_start_button = pyqtSignal() 22 | disable_start_button = pyqtSignal() 23 | enable_stop_button = pyqtSignal() 24 | disable_stop_button = pyqtSignal() 25 | set_infolabel = pyqtSignal(str) 26 | set_speedlabel = pyqtSignal(str) 27 | set_startbutton_text = pyqtSignal(str) 28 | info_message = pyqtSignal(str,str) 29 | notifications = pyqtSignal(int,int) 30 | save_failed_list = pyqtSignal() 31 | 32 | def __init__(self,tags,path,start_page,token,chan_cookies): 33 | super(Worker, self).__init__(None) 34 | self.tags = tags 35 | self.path = path 36 | self.start_page = int(start_page) 37 | self.token = token 38 | self.chan_cookies = chan_cookies 39 | 40 | ##调用aria2下载 41 | def get_img(self,img_list): 42 | order = ('./aria2c_sd --dir=%s --input-file=url.txt --continue=true --enable-rpc=true --rpc-listen-port=7865 --rpc-secret=123654' % self.path) 43 | 44 | ##保存链接并调用aria2下载 45 | with open('url.txt','w') as url_list: 46 | for img_url in img_list: 47 | url_list.write(img_url+'\n') 48 | process = Popen(order,shell=True,stdin=PIPE,stdout=PIPE,stderr=STDOUT) 49 | time.sleep(0.1) 50 | self.notifications.emit(this_page_count,count) 51 | 52 | ##获取aria2的信息 53 | while process.poll() is None: 54 | process.stdout.readline() 55 | 56 | ##获取当前页所有的图片地址 57 | def get_url(self,page_url): 58 | ##如果登录成功,使用带token的headers 59 | if self.token != 'failed': 60 | ##获取图片地址 61 | login_headers = {'User-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/17.17134','authorization':'Bearer '+self.token} 62 | try: 63 | page = requests.get(page_url,headers=login_headers).text 64 | except: 65 | return ['error'] 66 | else: 67 | try: 68 | page = requests.get(page_url,headers=headers).text 69 | except: 70 | return ['error'] 71 | url_list = re.findall(r'"file_url":"(.*?)"',str(page)) 72 | return url_list 73 | 74 | ##获取tags下图片的数量 75 | def get_count(self): 76 | url_for_count = 'https://chan.sankakucomplex.com/?tags=' + self.tags 77 | if self.token != 'failed':##检查是否登录 78 | try: 79 | text = bs(requests.get(url_for_count,headers=headers,cookies=self.chan_cookies).text,'lxml') 80 | except: 81 | return -2 82 | else:##如果没有登录,那么就会有搜索限制 83 | try: 84 | text = bs(requests.get(url_for_count,headers=headers).text,'lxml') 85 | except: 86 | return -2 87 | 88 | ##获取总图片数 89 | if re.findall(r'\+',self.tags)==[] and re.findall(r'\+-',self.tags)==[]:#单tag时 90 | img_count = re.findall(r'">(.*?)',str(text.find('span',{'class':'tag-count'})))[0] 91 | return int(img_count.replace(',','')) 92 | else:#多tag或过滤条件时 93 | try: 94 | img_count = re.findall(r'Post Count: (.*?)"><',str(text.find('span',{'class':'tag-type-none'})))[0] 95 | except: 96 | return -1 97 | return int(img_count.replace(',','')) 98 | 99 | ##获取当前页的图片地址 100 | def one_page_process(self,i): 101 | img_list = self.get_url(url+str(i)) 102 | if img_list == ['error']: 103 | return 1 104 | global this_page_count 105 | this_page_count = len(img_list) 106 | self.log_append.emit('下载第%d页,%d张图片' % (i,this_page_count)) 107 | self.log_moveCursor.emit() 108 | if not os.path.exists(self.path): 109 | os.mkdir(self.path) 110 | #if len(img_list) > 5: 111 | # if len(img_list)%5 == 0: 112 | # for k in range(0,int(len(img_list)/5)): 113 | # self.get_img(img_list[k*5:k*5+5]) 114 | # else: 115 | # for k in range(0,math.ceil(len(img_list)/5)-1): 116 | # self.get_img(img_list[k*5:k*5+5]) 117 | # self.get_img(img_list[(math.ceil(len(img_list)/5)-1)*5:]) 118 | #else: 119 | # self.get_img(img_list) 120 | self.get_img(img_list) 121 | return 0 122 | 123 | ##开始 124 | def run(self): 125 | self.set_processbar.emit(0)#进度条初始化 126 | self.disable_start_button.emit() 127 | self.enable_stop_button.emit() 128 | 129 | ##开始页码不能为0 130 | if self.start_page == 0: 131 | self.info_message.emit('错误','开始页码错误') 132 | self.enable_start_button.emit() 133 | self.disable_stop_button.emit() 134 | return 135 | 136 | ##检查path和tags是否为空 137 | if self.path == '': 138 | self.info_message.emit('错误','请设定保存路径') 139 | self.enable_start_button.emit() 140 | self.disable_stop_button.emit() 141 | return 142 | if self.tags == '': 143 | self.info_message.emit('错误','请设定标签') 144 | self.enable_start_button.emit() 145 | self.disable_stop_button.emit() 146 | return 147 | 148 | start = datetime.datetime.now()##计时 149 | global url 150 | url = 'https://capi-v2.sankakucomplex.com/posts?limit=40&tags='+self.tags+'&page=' 151 | self.set_infolabel.emit('正在开始...') 152 | sys.setrecursionlimit(1000000) 153 | 154 | global count 155 | count = self.get_count() 156 | page_count = math.ceil(count/40) 157 | if count == -1 or count == 0:#如果不能获取正确页数则终止 158 | self.log_append.emit("错误:无法获取图片总数,可能是标签错误或者未登入") 159 | self.log_moveCursor.emit() 160 | self.enable_start_button.emit() 161 | self.disable_stop_button.emit() 162 | return 163 | elif count == -2: 164 | self.info_message.emit('错误',"网络错误,请检查网络状况") 165 | self.enable_start_button.emit() 166 | self.disable_stop_button.emit() 167 | return 168 | 169 | if self.start_page != 1 and self.start_page > page_count: #如果开始页数大于总页数则终止 170 | self.info_message.emit('错误','开始页码不能大于总页数') 171 | self.enable_start_button.emit() 172 | self.disable_stop_button.emit() 173 | return 174 | elif self.start_page != 1 and self.start_page <= page_count: #计算中途开始时的页数和图片数 175 | count -= ((self.start_page -1) * 40) 176 | page_count = (page_count - self.start_page + 1) 177 | 178 | self.log_append.emit('标签下找到%d张图片,共%d页' % (count,page_count)) 179 | self.log_moveCursor.emit() 180 | 181 | self.set_process.emit('0',count) 182 | 183 | ##开始获取链接并下载 184 | for i in range(self.start_page, (page_count+self.start_page)): 185 | self.set_startbutton_text.emit('(%d/%d)' % (i,page_count+self.start_page-1)) 186 | result = self.one_page_process(i) 187 | if result == 1: 188 | self.info_message.emit('错误',"网络错误,你可以从第%d页重新开始"%i) 189 | self.log_append.emit("网络错误,你可以从第%d页重新开始"%i) 190 | self.log_moveCursor.emit() 191 | self.save_failed_list.emit() 192 | self.set_startbutton_text.emit('开始') 193 | self.enable_start_button.emit() 194 | self.disable_stop_button.emit() 195 | return 196 | 197 | self.save_failed_list.emit() 198 | self.enable_start_button.emit() 199 | self.disable_stop_button.emit() 200 | self.set_startbutton_text.emit('开始') 201 | end = datetime.datetime.now() 202 | used_time = str(end-start).split('.', 2)[0] 203 | self.log_append.emit('用时:%s' % used_time) 204 | self.log_moveCursor.emit() 205 | -------------------------------------------------------------------------------- /Windows_ver/Worker.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import time,requests,sys,datetime 3 | import math,re,os 4 | if hasattr(sys, 'frozen'): 5 | os.environ['PATH'] = sys._MEIPASS + ";" + os.environ['PATH'] 6 | from PyQt5.QtCore import * 7 | from subprocess import Popen,PIPE,STDOUT 8 | from bs4 import BeautifulSoup as bs 9 | 10 | requests.adapters.DEFAULT_RETRIES = 5 11 | this_page_count = this_page_downloaded = 0 #当前页图片数和已下载数 12 | count = downloaded_count = 0 #总图片数和已下载数 13 | 14 | headers = {'User-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/17.17134'} 15 | 16 | class Worker(QThread): 17 | log_append = pyqtSignal(str) 18 | set_processbar = pyqtSignal(int) 19 | set_process = pyqtSignal(str,int) 20 | log_moveCursor = pyqtSignal() 21 | enable_start_button = pyqtSignal() 22 | disable_start_button = pyqtSignal() 23 | enable_stop_button = pyqtSignal() 24 | disable_stop_button = pyqtSignal() 25 | set_infolabel = pyqtSignal(str) 26 | set_speedlabel = pyqtSignal(str) 27 | set_startbutton_text = pyqtSignal(str) 28 | info_message = pyqtSignal(str,str) 29 | notifications = pyqtSignal(int,int) 30 | save_failed_list = pyqtSignal() 31 | 32 | def __init__(self,tags,path,start_page,token,chan_cookies): 33 | super(Worker, self).__init__(None) 34 | self.tags = tags 35 | self.path = path 36 | self.start_page = int(start_page) 37 | self.token = token 38 | self.chan_cookies = chan_cookies 39 | 40 | ##调用aria2下载 41 | def get_img(self,img_list): 42 | order = ('aria2c_sd --dir=%s --input-file=url.txt --continue=true --enable-rpc=true --rpc-listen-port=7865 --rpc-secret=123654' % self.path) 43 | 44 | ##保存链接并调用aria2下载 45 | with open('url.txt','w') as url_list: 46 | for img_url in img_list: 47 | url_list.write(img_url+'\n') 48 | process = Popen(order,shell=True,stdin=PIPE,stdout=PIPE,stderr=STDOUT) 49 | time.sleep(0.1) 50 | self.notifications.emit(this_page_count,count) 51 | 52 | ##获取aria2的信息 53 | while process.poll() is None: 54 | process.stdout.readline() 55 | 56 | ##获取当前页所有的图片地址 57 | def get_url(self,page_url): 58 | ##如果登录成功,使用带token的headers 59 | if self.token != 'failed': 60 | ##获取图片地址 61 | login_headers = {'User-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/17.17134','authorization':'Bearer '+self.token} 62 | try: 63 | page = requests.get(page_url,headers=login_headers).text 64 | except: 65 | return ['error'] 66 | else: 67 | try: 68 | page = requests.get(page_url,headers=headers).text 69 | except: 70 | return ['error'] 71 | url_list = re.findall(r'"file_url":"(.*?)"',str(page)) 72 | return url_list 73 | 74 | ##获取tags下图片的数量 75 | def get_count(self): 76 | url_for_count = 'https://chan.sankakucomplex.com/?tags=' + self.tags 77 | if self.token != 'failed':##检查是否登录 78 | try: 79 | text = bs(requests.get(url_for_count,headers=headers,cookies=self.chan_cookies).text,'lxml') 80 | except: 81 | return -2 82 | else:##如果没有登录,那么就会有搜索限制 83 | try: 84 | text = bs(requests.get(url_for_count,headers=headers).text,'lxml') 85 | except: 86 | return -2 87 | 88 | ##获取总图片数 89 | if re.findall(r'\+',self.tags)==[] and re.findall(r'\+-',self.tags)==[]:#单tag时 90 | img_count = re.findall(r'">(.*?)',str(text.find('span',{'class':'tag-count'})))[0] 91 | return int(img_count.replace(',','')) 92 | else:#多tag或过滤条件时 93 | try: 94 | img_count = re.findall(r'Post Count: (.*?)"><',str(text.find('span',{'class':'tag-type-none'})))[0] 95 | except: 96 | return -1 97 | return int(img_count.replace(',','')) 98 | 99 | ##获取当前页的图片地址 100 | def one_page_process(self,i): 101 | img_list = self.get_url(url+str(i)) 102 | if img_list == ['error']: 103 | return 1 104 | global this_page_count 105 | this_page_count = len(img_list) 106 | self.log_append.emit('下载第%d页,%d张图片' % (i,this_page_count)) 107 | self.log_moveCursor.emit() 108 | if not os.path.exists(self.path): 109 | os.mkdir(self.path) 110 | #if len(img_list) > 5: 111 | # if len(img_list)%5 == 0: 112 | # for k in range(0,int(len(img_list)/5)): 113 | # self.get_img(img_list[k*5:k*5+5]) 114 | # else: 115 | # for k in range(0,math.ceil(len(img_list)/5)-1): 116 | # self.get_img(img_list[k*5:k*5+5]) 117 | # self.get_img(img_list[(math.ceil(len(img_list)/5)-1)*5:]) 118 | #else: 119 | # self.get_img(img_list) 120 | self.get_img(img_list) 121 | return 0 122 | 123 | ##开始 124 | def run(self): 125 | self.set_processbar.emit(0)#进度条初始化 126 | self.disable_start_button.emit() 127 | self.enable_stop_button.emit() 128 | 129 | ##开始页码不能为0 130 | if self.start_page == 0: 131 | self.info_message.emit('错误','开始页码错误') 132 | self.enable_start_button.emit() 133 | self.disable_stop_button.emit() 134 | return 135 | 136 | ##检查path和tags是否为空 137 | if self.path == '': 138 | self.info_message.emit('错误','请设定保存路径') 139 | self.enable_start_button.emit() 140 | self.disable_stop_button.emit() 141 | return 142 | if self.tags == '': 143 | self.info_message.emit('错误','请设定标签') 144 | self.enable_start_button.emit() 145 | self.disable_stop_button.emit() 146 | return 147 | 148 | start = datetime.datetime.now()##计时 149 | global url 150 | url = 'https://capi-v2.sankakucomplex.com/posts?limit=40&tags='+self.tags+'&page=' 151 | self.set_infolabel.emit('正在开始...') 152 | sys.setrecursionlimit(1000000) 153 | 154 | global count 155 | count = self.get_count() 156 | page_count = math.ceil(count/40) 157 | if count == -1 or count == 0:#如果不能获取正确页数则终止 158 | self.log_append.emit("错误:无法获取图片总数,可能是标签错误或者未登入") 159 | self.log_moveCursor.emit() 160 | self.enable_start_button.emit() 161 | self.disable_stop_button.emit() 162 | return 163 | elif count == -2: 164 | self.info_message.emit('错误',"网络错误,请检查网络状况") 165 | self.enable_start_button.emit() 166 | self.disable_stop_button.emit() 167 | return 168 | 169 | if self.start_page != 1 and self.start_page > page_count: #如果开始页数大于总页数则终止 170 | self.info_message.emit('错误','开始页码不能大于总页数') 171 | self.enable_start_button.emit() 172 | self.disable_stop_button.emit() 173 | return 174 | elif self.start_page != 1 and self.start_page <= page_count: #计算中途开始时的页数和图片数 175 | count -= ((self.start_page -1) * 40) 176 | page_count = (page_count - self.start_page + 1) 177 | 178 | self.log_append.emit('标签下找到%d张图片,共%d页' % (count,page_count)) 179 | self.log_moveCursor.emit() 180 | 181 | self.set_process.emit('0',count) 182 | 183 | ##开始获取链接并下载 184 | for i in range(self.start_page, (page_count+self.start_page)): 185 | self.set_startbutton_text.emit('(%d/%d)' % (i,page_count+self.start_page-1)) 186 | result = self.one_page_process(i) 187 | if result == 1: 188 | self.info_message.emit('错误',"网络错误,你可以从第%d页重新开始"%i) 189 | self.log_append.emit("网络错误,你可以从第%d页重新开始"%i) 190 | self.log_moveCursor.emit() 191 | self.save_failed_list.emit() 192 | self.set_startbutton_text.emit('开始') 193 | self.enable_start_button.emit() 194 | self.disable_stop_button.emit() 195 | return 196 | 197 | self.save_failed_list.emit() 198 | self.enable_start_button.emit() 199 | self.disable_stop_button.emit() 200 | self.set_startbutton_text.emit('开始') 201 | end = datetime.datetime.now() 202 | used_time = str(end-start).split('.', 2)[0] 203 | self.log_append.emit('用时:%s' % used_time) 204 | self.log_moveCursor.emit() 205 | -------------------------------------------------------------------------------- /Windows_ver/main.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import sys,os,json 3 | if hasattr(sys, 'frozen'): 4 | os.environ['PATH'] = sys._MEIPASS + ";" + os.environ['PATH'] 5 | from PyQt5 import QtCore, QtGui, QtWidgets 6 | from PyQt5.QtWidgets import QApplication,QMessageBox,QFileDialog,QLineEdit 7 | from PyQt5.QtCore import * 8 | from subprocess import call,PIPE,STDOUT 9 | from Refreshdlspeed import Refreshdlspeed 10 | from Login import Login 11 | from Notifications import Notifications 12 | from Worker import Worker 13 | from Stop import Stop 14 | 15 | ##初始化全局变量 16 | token = 'failed' 17 | start_page = 1 18 | chan_cookies = None 19 | failed_list = [] 20 | headers = {'User-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/17.17134'} 21 | 22 | class Ui_SankakuDownloader(object): 23 | def setupUi(self, SankakuDownloader): 24 | SankakuDownloader.setObjectName("SankakuDownloader") 25 | SankakuDownloader.resize(521, 427) 26 | SankakuDownloader.setFixedSize(521, 427) 27 | self.centralwidget = QtWidgets.QWidget(SankakuDownloader) 28 | self.centralwidget.setObjectName("centralwidget") 29 | self.pushButton_start = QtWidgets.QPushButton(self.centralwidget) 30 | self.pushButton_start.setGeometry(QtCore.QRect(10, 190, 93, 28)) 31 | self.pushButton_start.setObjectName("pushButton_start") 32 | self.pushButton_stop = QtWidgets.QPushButton(self.centralwidget) 33 | self.pushButton_stop.setGeometry(QtCore.QRect(113, 190, 93, 28)) 34 | self.pushButton_stop.setObjectName("pushButton_start") 35 | self.pushButton_stop.setEnabled(False) 36 | self.logBrowser = QtWidgets.QTextBrowser(self.centralwidget) 37 | self.logBrowser.setGeometry(QtCore.QRect(10, 260, 501, 141)) 38 | self.logBrowser.setObjectName("logBrowser") 39 | self.label = QtWidgets.QLabel(self.centralwidget) 40 | self.label.setGeometry(QtCore.QRect(10, 70, 72, 15)) 41 | self.label.setObjectName("label") 42 | self.label_2 = QtWidgets.QLabel(self.centralwidget) 43 | self.label_2.setGeometry(QtCore.QRect(10, 130, 91, 16)) 44 | self.label_2.setObjectName("label_2") 45 | self.progressBar = QtWidgets.QProgressBar(self.centralwidget) 46 | self.progressBar.setGeometry(QtCore.QRect(10, 227, 511, 21)) 47 | self.progressBar.setProperty("value", 0) 48 | self.progressBar.setObjectName("progressBar") 49 | self.label_3 = QtWidgets.QLabel(self.centralwidget) 50 | self.label_3.setGeometry(QtCore.QRect(10, 10, 72, 15)) 51 | self.label_3.setObjectName("label_3") 52 | self.label_4 = QtWidgets.QLabel(self.centralwidget) 53 | self.label_4.setGeometry(QtCore.QRect(130, 10, 72, 15)) 54 | self.label_4.setObjectName("label_4") 55 | self.label_5 = QtWidgets.QLabel(self.centralwidget) 56 | self.label_5.setGeometry(QtCore.QRect(310, 70, 91, 16)) 57 | self.label_5.setObjectName("label_5") 58 | self.label_6 = QtWidgets.QLabel(self.centralwidget) 59 | self.label_6.setGeometry(QtCore.QRect(10, 402, 291, 21)) 60 | self.label_6.setObjectName("label_6") 61 | self.label_7 = QtWidgets.QLabel(self.centralwidget) 62 | self.label_7.setGeometry(QtCore.QRect(380, 402, 131, 21)) 63 | self.label_7.setFocusPolicy(QtCore.Qt.NoFocus) 64 | self.label_7.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) 65 | self.label_7.setObjectName("label_7") 66 | self.pushButton_login = QtWidgets.QPushButton(self.centralwidget) 67 | self.pushButton_login.setGeometry(QtCore.QRect(240, 31, 93, 28)) 68 | self.pushButton_login.setObjectName("pushButton_login") 69 | self.lineEdit_tags = QtWidgets.QLineEdit(self.centralwidget) 70 | self.lineEdit_tags.setGeometry(QtCore.QRect(10, 90, 291, 31)) 71 | self.lineEdit_tags.setObjectName("lineEdit_tags") 72 | self.lineEdit_savepath = QtWidgets.QLineEdit(self.centralwidget) 73 | self.lineEdit_savepath.setGeometry(QtCore.QRect(10, 150, 291, 31)) 74 | self.lineEdit_savepath.setObjectName("lineEdit_savepath") 75 | self.lineEdit_username = QtWidgets.QLineEdit(self.centralwidget) 76 | self.lineEdit_username.setGeometry(QtCore.QRect(10, 30, 101, 31)) 77 | self.lineEdit_username.setObjectName("lineEdit_username") 78 | self.lineEdit_password = QtWidgets.QLineEdit(self.centralwidget) 79 | self.lineEdit_password.setGeometry(QtCore.QRect(130, 30, 101, 31)) 80 | self.lineEdit_password.setObjectName("lineEdit_password") 81 | self.lineEdit_password.setEchoMode(QLineEdit.Password) 82 | self.lineEdit_startpage = QtWidgets.QLineEdit(self.centralwidget) 83 | self.lineEdit_startpage.setGeometry(QtCore.QRect(310, 90, 91, 31)) 84 | self.lineEdit_startpage.setObjectName("lineEdit_username") 85 | self.pushButton_selectpath = QtWidgets.QPushButton(self.centralwidget) 86 | self.pushButton_selectpath.setGeometry(QtCore.QRect(310, 150, 51, 31)) 87 | self.pushButton_selectpath.setObjectName("pushButton_selectpath") 88 | self.statusBar = QtWidgets.QStatusBar(SankakuDownloader) 89 | self.statusBar.setObjectName("statusBar") 90 | self.lineEdit_startpage.setValidator(QtGui.QIntValidator(1,2147483647)) 91 | 92 | if os.path.exists('user.ini'): 93 | with open('user.ini','r') as user: 94 | user_info = user.readlines() 95 | try: 96 | self.lineEdit_username.setText(user_info[0].split('\n')[0]) 97 | self.lineEdit_password.setText(user_info[1]) 98 | except: 99 | pass 100 | 101 | ##开始监控下载速度 102 | self.refreshdlspeed() 103 | 104 | ##按钮事件 105 | self.pushButton_selectpath.clicked.connect(self.set_path) 106 | self.pushButton_start.clicked.connect(self.start) 107 | self.pushButton_stop.clicked.connect(self.stop) 108 | self.pushButton_login.clicked.connect(self.login) 109 | app.aboutToQuit.connect(self.closeEvent) 110 | 111 | self.retranslateUi(SankakuDownloader) 112 | QtCore.QMetaObject.connectSlotsByName(SankakuDownloader) 113 | 114 | def retranslateUi(self, SankakuDownloader): 115 | _translate = QtCore.QCoreApplication.translate 116 | SankakuDownloader.setWindowTitle(_translate("SankakuDownloader", "SankakuDownloader v2.0")) 117 | SankakuDownloader.setWindowIcon(QtGui.QIcon('icon.png')) 118 | self.pushButton_start.setText("开始") 119 | self.pushButton_stop.setText("停止") 120 | self.logBrowser.setHtml(_translate("SankakuDownloader", "\n" 121 | "
\n" 124 | "