├── .vscode ├── launch.json └── settings.json ├── README.md ├── __pycache__ ├── aboutme.cpython-38.pyc ├── d.cpython-38.pyc ├── event.cpython-38.pyc ├── fileManager.cpython-38.pyc ├── mTreeList.cpython-38.pyc ├── password.cpython-38.pyc ├── pica.cpython-38.pyc ├── post.cpython-38.pyc ├── set.cpython-38.pyc ├── setbox.cpython-38.pyc ├── start.cpython-38.pyc ├── test.cpython-38.pyc └── thread.cpython-38.pyc ├── aboutme.py ├── d.py ├── event.py ├── fileManager.py ├── icon ├── close.gif ├── delete.gif ├── download.gif ├── favicon.ico ├── filter.gif ├── image.gif ├── info.gif ├── refresh.gif └── temp.gif ├── mTreeList.py ├── password.py ├── pica.py ├── post.py ├── setbox.py ├── start.py ├── start.spec └── thread.py /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // 使用 IntelliSense 了解相关属性。 3 | // 悬停以查看现有属性的描述。 4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Python: 当前文件", 9 | "type": "python", 10 | "request": "launch", 11 | "program": "${file}", 12 | "console": "integratedTerminal" 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "python.pythonPath": "C:\\Users\\Muyoo\\AppData\\Local\\Programs\\Python\\Python38-32\\python.exe" 3 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 哔咔漫画收藏夹下载程序 2 | ```diff 3 | - 由于哔咔更新了API加密,此项目已不再有效 4 | ``` 5 | 哔咔APP的本子下载是乱序的,这个程序用来直接下载收藏夹里的文件。 6 | > 版本1.3更新,修复了因为特殊字符导致的部分漫画无法下载问题,和杀毒软件报错问题 7 | > 版本1.2更新,修复了token过期导致的无法登录,和40章后无法下载的问题 8 | > 版本1.1更新,增加了多选下载和暂停 9 | 10 | ### 下载地址: 11 | [点击下载 v1.2](https://www.muyoo.top/index.php/archives/61/) 12 | 13 | ### 哔咔API: 14 | [查看哔咔API](https://www.muyoo.top/index.php/archives/4/) 15 | 16 | 下载解压后,直接运行 17 | 18 | ![demo](https://www.muyoo.top/usr/uploads/2020/05/1076095479.png) 19 | -------------------------------------------------------------------------------- /__pycache__/aboutme.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/muyoou/picacomic_downloader/6d1e3728e0e0889d4ff4a1f4ce9e86f004e9e066/__pycache__/aboutme.cpython-38.pyc -------------------------------------------------------------------------------- /__pycache__/d.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/muyoou/picacomic_downloader/6d1e3728e0e0889d4ff4a1f4ce9e86f004e9e066/__pycache__/d.cpython-38.pyc -------------------------------------------------------------------------------- /__pycache__/event.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/muyoou/picacomic_downloader/6d1e3728e0e0889d4ff4a1f4ce9e86f004e9e066/__pycache__/event.cpython-38.pyc -------------------------------------------------------------------------------- /__pycache__/fileManager.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/muyoou/picacomic_downloader/6d1e3728e0e0889d4ff4a1f4ce9e86f004e9e066/__pycache__/fileManager.cpython-38.pyc -------------------------------------------------------------------------------- /__pycache__/mTreeList.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/muyoou/picacomic_downloader/6d1e3728e0e0889d4ff4a1f4ce9e86f004e9e066/__pycache__/mTreeList.cpython-38.pyc -------------------------------------------------------------------------------- /__pycache__/password.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/muyoou/picacomic_downloader/6d1e3728e0e0889d4ff4a1f4ce9e86f004e9e066/__pycache__/password.cpython-38.pyc -------------------------------------------------------------------------------- /__pycache__/pica.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/muyoou/picacomic_downloader/6d1e3728e0e0889d4ff4a1f4ce9e86f004e9e066/__pycache__/pica.cpython-38.pyc -------------------------------------------------------------------------------- /__pycache__/post.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/muyoou/picacomic_downloader/6d1e3728e0e0889d4ff4a1f4ce9e86f004e9e066/__pycache__/post.cpython-38.pyc -------------------------------------------------------------------------------- /__pycache__/set.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/muyoou/picacomic_downloader/6d1e3728e0e0889d4ff4a1f4ce9e86f004e9e066/__pycache__/set.cpython-38.pyc -------------------------------------------------------------------------------- /__pycache__/setbox.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/muyoou/picacomic_downloader/6d1e3728e0e0889d4ff4a1f4ce9e86f004e9e066/__pycache__/setbox.cpython-38.pyc -------------------------------------------------------------------------------- /__pycache__/start.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/muyoou/picacomic_downloader/6d1e3728e0e0889d4ff4a1f4ce9e86f004e9e066/__pycache__/start.cpython-38.pyc -------------------------------------------------------------------------------- /__pycache__/test.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/muyoou/picacomic_downloader/6d1e3728e0e0889d4ff4a1f4ce9e86f004e9e066/__pycache__/test.cpython-38.pyc -------------------------------------------------------------------------------- /__pycache__/thread.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/muyoou/picacomic_downloader/6d1e3728e0e0889d4ff4a1f4ce9e86f004e9e066/__pycache__/thread.cpython-38.pyc -------------------------------------------------------------------------------- /aboutme.py: -------------------------------------------------------------------------------- 1 | from tkinter import * # 导入 Tkinter 库 2 | from tkinter import ttk 3 | import tkinter.messagebox 4 | import webbrowser as web 5 | 6 | class about(): 7 | def __init__(self,root,event): 8 | self.root=root 9 | self.event=event 10 | self.event.aboutState=1 11 | x = root.winfo_x() 12 | y = root.winfo_y() 13 | t2 = Toplevel(self.root) 14 | t2.geometry("270x180+%d+%d"%(x+150,y+100)) 15 | t2.title("关于") 16 | t2.resizable(0,0) 17 | photo = PhotoImage(file=".\\icon\\temp.gif") 18 | imgLabel = Label(t2,image=photo) 19 | imgLabel.image=photo 20 | imgLabel.place(x=20,y=20,width=50,height=50) 21 | Label(t2,text='muyoo',font=('Microsoft YaHei UI',20),fg="RoyalBlue").place(x=80,y=13) 22 | Label(t2,text='一个摸鱼的伪技术宅',font=('Microsoft YaHei UI',10),fg="RoyalBlue").place(x=80,y=52) 23 | Label(t2,text="v 1.4 正式版").place(x=180,y=90) 24 | Button(t2,text="查看更多...",borderwidth=0,bg="RoyalBlue",fg="white",command=self.goto).place(x=0,y=120,relw=1,height=50) 25 | def goto(self): 26 | web.open("https://www.muyoo.top") -------------------------------------------------------------------------------- /d.py: -------------------------------------------------------------------------------- 1 | #-----配置信息----- 2 | 3 | #哔咔用户名 4 | Email='' 5 | #密码 6 | Password='' 7 | #是否使用代理 8 | useProxy=False 9 | #代理地址 10 | Proxy='' 11 | #app版本 12 | Version='2.2.1.3.3.4' 13 | #图片质量 14 | Image_quality='high' 15 | #编译版本 16 | Build_version='45' 17 | #密钥 18 | Key='C69BAF41DA5ABD1FFEDC6D2FEA56B' 19 | Key2="~d}$Q7$eIni=V)9\\RK/P.RM4;9[7|@/CA}b~OW!3?EV`:<>M7pddUBL5n|0/*Cn" 20 | #GET请求最多重连次数 21 | GetNum=3 22 | #POST请求最多重连次数 23 | PostNum=3 24 | #图片下载最多重连次数 25 | ImaConnNum=5 26 | #数据超时时间 27 | Timeout=20 28 | #图片超时时间 29 | imgTimeout=15 30 | 31 | #-----自动生成----- 32 | Token='' 33 | #已经下载的漫画 34 | Downloaded=[] 35 | #现在的页码数 36 | nowPage=1 37 | #所有的页数 38 | AllPage=0 39 | #正在下载的文件 40 | Downloading='' 41 | #正在下载漫画的总页数 42 | DownloadingPage=0 -------------------------------------------------------------------------------- /event.py: -------------------------------------------------------------------------------- 1 | import d 2 | import fileManager 3 | import setbox 4 | import thread 5 | import pica 6 | import aboutme 7 | import sys 8 | 9 | log=None 10 | root=None 11 | page=None 12 | tree=None 13 | mself=None 14 | mpica=None 15 | #一些状态标识量 16 | threadaState=0 17 | setboxState=0 18 | aboutState=0 19 | downloadThred=None 20 | #下载的两个下载状态组件列表 21 | DownloadStateList=() 22 | #下载是在暂停、取消还是在正常状态 23 | isStartDownload=1 24 | #正在下载中的那个漫画列组件 25 | downloadingList=None 26 | 27 | #获取pica类 28 | def getPica(): 29 | global mpica 30 | mpica=pica.pica(mself) 31 | 32 | #-----日志交互----- 33 | 34 | #打印信息到窗口日志 35 | def printl(text): 36 | log.insert('end',text+'\n') 37 | log.see('end') 38 | 39 | #-----列表控制----- 40 | def insertList(data): 41 | tree.insert_tv(data) 42 | #-----配置设置----- 43 | 44 | #将配置导入全局设置 45 | def setConfig(data): 46 | d.Email=data['user'] 47 | d.Password=data['password'] 48 | d.Proxy=data['proxy'] 49 | d.Image_quality=data['quality'] 50 | d.useProxy=data['useProxy'] 51 | 52 | #从配置文件中读取配置 53 | def getConfigByFile(): 54 | setConfig(fileManager.readConfig()) 55 | 56 | #打印所有配置到窗口日志 57 | def printConfig(): 58 | printl("----------------------") 59 | printl("用户名:"+d.Email) 60 | printl("图片质量:"+d.Image_quality) 61 | printl("代理设置:"+d.Proxy) 62 | printl("----------------------") 63 | 64 | #检查是否是初次启动,初始化配置文件 65 | def checkConfig(): 66 | if fileManager.isExist(".\\data\\config.json"): 67 | printl("加载配置文件") 68 | getConfigByFile() 69 | printConfig() 70 | huoqu(1) 71 | else: 72 | printl("初次配置") 73 | openMenu() 74 | printConfig() 75 | printl("加载日志") 76 | if fileManager.isExist('.\\data\\downloaded.json'): 77 | setDownloaded() 78 | else: 79 | fileManager.createJsonFile([],'downloaded.json') 80 | 81 | #-----下载事务----- 82 | 83 | #从下载记录中导入已下载的文件清单 84 | def setDownloaded(): 85 | d.Downloaded=fileManager.readDownloaded() 86 | 87 | #从全局变量中上传到下载记录文件 88 | def writeToDownFile(): 89 | fileManager.updataDownloaded(d.Downloaded) 90 | 91 | #添加已下载清单 92 | def addDownloadedList(input): 93 | d.Downloaded.append(input) 94 | writeToDownFile() 95 | 96 | #检查一个动漫id是否已经下载完成 97 | def isDownloaded(input): 98 | if input in d.Downloaded: 99 | return True 100 | else: return False 101 | 102 | #检查一个动漫是否在下载列表里 103 | def isInDownloadList(input): 104 | for item in mpica.dolwnloadList: 105 | print("id信息:"+item['_id']) 106 | if input==item['_id']: 107 | return True 108 | return False 109 | 110 | 111 | #获取现在正在下载的漫画id 112 | def getdowning(): 113 | return d.Downloading 114 | 115 | #-----页码交互----- 116 | 117 | #设置窗口中的页码部分 118 | def setPage(nowp,allp): 119 | d.nowPage=nowp 120 | d.AllPage=allp 121 | page.set("第%d页,共%d页"%(nowp,allp)) 122 | 123 | #获取当前的页码数 124 | def getNowPage(): 125 | return d.nowPage 126 | 127 | #获取收藏夹的总页码数 128 | def getAllPage(): 129 | return d.AllPage 130 | 131 | #更改当前页码数 132 | def setNowPage(nowp): 133 | d.nowPage=nowp 134 | page.set("第%d页,共%d页"%(nowp,d.AllPage)) 135 | 136 | #-----错误处理----- 137 | def checkError(input): 138 | if input==-1: 139 | printl("----------------\n连接超时!正在尝试重新登录...") 140 | reLogin() 141 | return False 142 | elif input==-2: 143 | printl("----------------\n连接错误!请检查你的网络,是否能连接到哔咔服务器") 144 | printl("建议使用VPN或者在设置中配置代理") 145 | return False 146 | elif input==-3: 147 | printl("----------------\n代理错误!请检查设置中的代理设置。如不需要代理,请保持空值") 148 | return False 149 | elif input==-4: 150 | print("-----------------\n发生未知错误!尚没有适配的错误处理") 151 | return False 152 | elif input==-5: 153 | print("-----------------\n多次重连失败,超过重连次数。请检查网络或更改重连次数") 154 | return False 155 | else: return True 156 | 157 | #重新登录 158 | def reLogin(): 159 | fileManager.removeToken() 160 | mpica.loginByWeb() 161 | 162 | #-----事件方法----- 163 | 164 | #打开下载文件夹 165 | def openfolder(): 166 | fileManager.openFile(".\\comic") 167 | 168 | 169 | #打开设置窗口 170 | def openMenu(): 171 | if setboxState==0: 172 | setbox.setbox(root,mself) 173 | 174 | #获取index页的漫画列表 175 | def huoqu(index=0): 176 | if threadaState==1: 177 | printl('请等待页面加载完成...') 178 | else: 179 | if index!=0: 180 | setNowPage(index) 181 | thread1=thread.myThread(tree,mpica,mself) 182 | thread1.start() 183 | 184 | #下一页 185 | def nextPage(): 186 | tem=getNowPage() 187 | if(tem1): 194 | huoqu(tem-1) 195 | 196 | #打开关于窗口 197 | def openAbout(): 198 | if aboutState==0: 199 | aboutme.about(root,mself) 200 | 201 | 202 | #启动下载线程 203 | def download(): 204 | global downloadThred 205 | thread1=thread.downThread(mpica,mself) 206 | thread1.start() 207 | downloadThred=thread1 208 | 209 | #暂停下载 210 | def PauseDownload(): 211 | global isStartDownload 212 | if isStartDownload == 1: 213 | isStartDownload=0 214 | printl("暂停下载中,等待当前图片加载完成") 215 | DownloadStateList[0].pack_forget() 216 | DownloadStateList[1].pack(side='right') 217 | refreshStatus(False) 218 | 219 | #继续下载 220 | def startDownload(): 221 | global isStartDownload 222 | if isStartDownload == 0: 223 | isStartDownload=1 224 | printl('下载继续') 225 | DownloadStateList[1].pack_forget() 226 | DownloadStateList[0].pack(side='right') 227 | refreshStatus(True) 228 | 229 | #初始化下载 230 | def fristStartDownload(): 231 | DownloadStateList[0].pack(side='right') 232 | refreshStatus(True) 233 | 234 | #下载完成 235 | def finishDownload(): 236 | DownloadStateList[0].pack_forget() 237 | refreshStatus(False) 238 | 239 | def refreshStatus(status): 240 | tmp=len(mpica.dolwnloadList) 241 | if status == True: 242 | DownloadStateList[2].set('下载中 有%d个漫画等待下载'%tmp) 243 | else: 244 | if tmp!=0: 245 | DownloadStateList[2].set('暂停中 有%d个漫画等待下载'%tmp) 246 | else: 247 | DownloadStateList[2].set('闲置中') 248 | 249 | #下载此页 250 | def downloadPage(): 251 | mpica.putNowPagePicToList() 252 | 253 | #取消下载选中 254 | def cancelSelected(): 255 | refreshCancer() 256 | mpica.cancelSectctInList(tree.getSelected()) 257 | refreshStatus(True if isStartDownload==1 else False) 258 | tree.cancelAll() 259 | refresh() 260 | 261 | #下载选中 262 | def downloadSelected(): 263 | refresh() 264 | mpica.putSelectPicToList(tree.getSelected()) 265 | refreshStatus(True if isStartDownload==1 else False) 266 | tree.cancelAll() 267 | refresh() 268 | 269 | #刷新列表下载状态 270 | def refresh(): 271 | global downloadingList 272 | for item in tree.getTree().get_children(): 273 | tem=tree.getTree().set(item,'id') 274 | if tem==getdowning(): 275 | downloadingList=item 276 | elif isDownloaded(tem): 277 | tree.getTree().set(item,'状态','已下载') 278 | elif isInDownloadList(tem): 279 | tree.getTree().set(item,'状态','等待下载') 280 | else: 281 | tree.getTree().set(item,'状态','未下载') 282 | 283 | #刷新获取信息提示 284 | def refreshBefore(): 285 | tree.getTree().set(downloadingList,'状态','获取信息中') 286 | 287 | #刷新取消中提示 288 | def refreshCancer(): 289 | tree.getTree().set(downloadingList,'状态','取消中') 290 | 291 | #刷新下载中的百分比 292 | def refreshRate(rate): 293 | tree.getTree().set(downloadingList,'状态','下载'+str(rate)+'%') 294 | 295 | def close(): 296 | downloadThred.stop() 297 | root.destroy() -------------------------------------------------------------------------------- /fileManager.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | 4 | def mkdir(path): 5 | isExists=os.path.exists(path) 6 | if not isExists: 7 | print(path) 8 | os.makedirs(path) 9 | return True 10 | else: 11 | return False 12 | 13 | def createJsonFile(input,name): 14 | mkdir(".\\data") 15 | with open('.\\data\\'+name,'w',encoding='utf-8') as f: 16 | json.dump(input,f,ensure_ascii=False) 17 | 18 | def openFile(input): 19 | os.startfile(input) 20 | 21 | def isExist(input): 22 | return os.path.exists(input) 23 | 24 | def readConfig(): 25 | with open('.\\data\\config.json', 'r',encoding='utf-8') as f: 26 | data = json.load(f) 27 | return data 28 | 29 | def creatTokenFile(input): 30 | with open('.\\data\\token.dat', 'w') as f: 31 | f.write(input) 32 | 33 | def readToken(): 34 | return open(".\\data\\token.dat", "r+").readline() 35 | 36 | def readDownloaded(): 37 | with open('.\\data\\downloaded.json', 'r',encoding='utf-8') as f: 38 | data = json.load(f) 39 | return data 40 | 41 | def updataDownloaded(input): 42 | with open('.\\data\\downloaded.json', 'w') as f: 43 | json.dump(input,f,ensure_ascii=False) 44 | 45 | def createNewFile(filename): 46 | fd = open(filename, mode="w", encoding="utf-8") 47 | fd.close() 48 | 49 | def saveImg(img,path): 50 | if not isExist(path): 51 | open(path, 'wb').write(img) 52 | 53 | def removeToken(): 54 | if isExist(".\\data\\token.dat"): 55 | os.unlink(".\\data\\token.dat") -------------------------------------------------------------------------------- /icon/close.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/muyoou/picacomic_downloader/6d1e3728e0e0889d4ff4a1f4ce9e86f004e9e066/icon/close.gif -------------------------------------------------------------------------------- /icon/delete.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/muyoou/picacomic_downloader/6d1e3728e0e0889d4ff4a1f4ce9e86f004e9e066/icon/delete.gif -------------------------------------------------------------------------------- /icon/download.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/muyoou/picacomic_downloader/6d1e3728e0e0889d4ff4a1f4ce9e86f004e9e066/icon/download.gif -------------------------------------------------------------------------------- /icon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/muyoou/picacomic_downloader/6d1e3728e0e0889d4ff4a1f4ce9e86f004e9e066/icon/favicon.ico -------------------------------------------------------------------------------- /icon/filter.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/muyoou/picacomic_downloader/6d1e3728e0e0889d4ff4a1f4ce9e86f004e9e066/icon/filter.gif -------------------------------------------------------------------------------- /icon/image.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/muyoou/picacomic_downloader/6d1e3728e0e0889d4ff4a1f4ce9e86f004e9e066/icon/image.gif -------------------------------------------------------------------------------- /icon/info.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/muyoou/picacomic_downloader/6d1e3728e0e0889d4ff4a1f4ce9e86f004e9e066/icon/info.gif -------------------------------------------------------------------------------- /icon/refresh.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/muyoou/picacomic_downloader/6d1e3728e0e0889d4ff4a1f4ce9e86f004e9e066/icon/refresh.gif -------------------------------------------------------------------------------- /icon/temp.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/muyoou/picacomic_downloader/6d1e3728e0e0889d4ff4a1f4ce9e86f004e9e066/icon/temp.gif -------------------------------------------------------------------------------- /mTreeList.py: -------------------------------------------------------------------------------- 1 | 2 | from tkinter import * 3 | from tkinter.ttk import * 4 | class My_Tk(): 5 | def __init__(self,mytk): 6 | self.tk=mytk 7 | self.orm={} 8 | #self.create_button() 9 | self.create_heading() 10 | self.create_tv() 11 | 12 | def create_button(self): 13 | Button(self.tk,text='增加数据',command=self.insert_tv).pack() 14 | 15 | def create_heading(self,): 16 | '''重新做一个treeview的头,不然滚动滚动条,看不到原先的头!!!''' 17 | heading_frame=Frame(self.tk) 18 | heading_frame.pack(fill=X) 19 | 20 | #填充用 21 | button_frame=Label(heading_frame,width=0.5) 22 | button_frame.pack(side=LEFT,) 23 | #全选按钮 24 | self.all_buttonvar = IntVar() 25 | self.all_button = Checkbutton(heading_frame, text='',variable=self.all_buttonvar, command=self.select_all) 26 | self.all_button.pack(side=LEFT) 27 | self.all_buttonvar.set(0) 28 | 29 | self.columns = ['id','名称', '作者', '点赞数', '页数', '章节数','状态'] 30 | self.widths = [0,260, 150, 65, 65, 65,65] 31 | 32 | #重建tree的头 33 | for i in range(len(self.columns)): 34 | Label(heading_frame,text=self.columns[i],width=int(self.widths[i]*0.156),anchor='center',relief=GROOVE).pack(side=LEFT) 35 | 36 | 37 | def create_tv(self): 38 | #放置 canvas、滚动条的frame 39 | canvas_frame=Frame(self.tk,width=400,height=400) 40 | canvas_frame.pack(fill=X) 41 | 42 | #只剩Canvas可以放置treeview和按钮,并且跟滚动条配合 43 | self.canvas=Canvas(canvas_frame,width=400,height=300,scrollregion=(0,0,500,300)) 44 | self.canvas.pack(side=LEFT,fill=BOTH,expand=1) 45 | #滚动条 46 | ysb = Scrollbar(canvas_frame, orient=VERTICAL, command=self.canvas.yview) 47 | self.canvas.configure(yscrollcommand=ysb.set) 48 | ysb.pack(side=RIGHT, fill=Y) 49 | #!!!!=======重点:鼠标滚轮滚动时,改变的页面是canvas 而不是treeview 50 | self.canvas.bind_all("",lambda event:self.canvas.yview_scroll(int(-1 * (event.delta / 120)), "units")) 51 | 52 | 53 | #想要滚动条起效,得在canvas创建一个windows(frame)!! 54 | tv_frame=Frame(self.canvas) 55 | self.tv_frame=self.canvas.create_window(0, 0, window=tv_frame, anchor='nw',width=780,height=400)#anchor该窗口在左上方 56 | 57 | #放置button的frame 58 | self.button_frame=Frame(tv_frame) 59 | self.button_frame.pack(side=LEFT, fill=Y) 60 | Label(self.button_frame,width=3).pack() #填充用 61 | 62 | 63 | #创建treeview 64 | self.tv = Treeview(tv_frame, height=10, columns=self.columns, show='headings')#height好像设定不了行数,实际由插入的行数决定 65 | self.tv.pack(expand=1, side=LEFT, fill=BOTH) 66 | #设定每一列的属性 67 | for i in range(len(self.columns)): 68 | self.tv.column(self.columns[i], width=self.widths[i], anchor='n', stretch=True) 69 | 70 | #设定treeview格式 71 | # import tkinter.font as tkFont 72 | # ft = tkFont.Font(family='Fixdsys', size=20, weight=tkFont.BOLD) 73 | self.tv.tag_configure('oddrow') #设定treeview里字体格式font=ft 74 | self.tv.tag_configure('select', background='LightPink')#当对应的按钮被打勾,那么对于的行背景颜色改变! 75 | self.rowheight=27 #很蛋疼,好像tkinter里只能用整数! 76 | Style().configure('Treeview', rowheight=self.rowheight) #设定每一行的高度 77 | 78 | # 设定选中的每一行字体颜色、背景颜色 (被选中时,没有变化) 79 | Style().map("Treeview", 80 | foreground=[ ('focus', 'black'), ], 81 | background=[ ('active', 'white')] 82 | ) 83 | self.tv.bind('<>', self.select_tree) #绑定tree选中时的回调函数 84 | 85 | def getTree(self): 86 | return self.tv 87 | 88 | def delALl(self): 89 | #清空tree、checkbutton 90 | items = self.tv.get_children() 91 | [self.tv.delete(item) for item in items] 92 | self.tv.update() 93 | for child in self.button_frame.winfo_children()[1:]: #第一个构件是label,所以忽略 94 | child.destroy() 95 | 96 | def insert_tv(self,data): 97 | #清空tree、checkbutton 98 | items = self.tv.get_children() 99 | [self.tv.delete(item) for item in items] 100 | self.tv.update() 101 | for child in self.button_frame.winfo_children()[1:]: #第一个构件是label,所以忽略 102 | child.destroy() 103 | #重设tree、button对应关系 104 | self.orm={} 105 | for item in data: 106 | tv_item=self.tv.insert('','end',values=(item.get('_id',''),item.get('title',''),item.get('author',''),item.get('likesCount',''),item.get('pagesCount',''),item.get('epsCount',''),'')) 107 | import tkinter 108 | ck_button = tkinter.Checkbutton(self.button_frame,variable=IntVar()) 109 | ck_button['command']=lambda item=tv_item:self.select_button(item) 110 | ck_button.pack() 111 | self.orm[tv_item]=[ck_button] 112 | #每次点击插入tree,先设定全选按钮不打勾,接着打勾并且调用其函数 113 | self.all_buttonvar.set(0) 114 | #self.all_button.invoke() 115 | 116 | #更新canvas的高度 117 | height = (len(self.tv.get_children()) + 1) * self.rowheight # treeview实际高度 118 | self.canvas.itemconfigure(self.tv_frame, height=height) #设定窗口tv_frame的高度 119 | self.tk.update() 120 | self.canvas.config(scrollregion=self.canvas.bbox("all"))#滚动指定的范围 121 | 122 | def select_all(self): 123 | '''全选按钮的回调函数 124 | 作用:所有多选按钮打勾、tree所有行都改变底色(被选中)''' 125 | for item,[button] in self.orm.items(): 126 | if self.all_buttonvar.get()==1: 127 | button.select() 128 | self.tv.item(item, tags='select') 129 | else: 130 | button.deselect() 131 | self.tv.item(item, tags='oddrow') 132 | 133 | def select_button(self,item): 134 | '''多选按钮的回调函数 135 | 作用:1.根据按钮的状态,改变对应item的底色(被选中) 136 | 2.根据所有按钮被选的情况,修改all_button的状态''' 137 | 138 | button=self.orm[item][0] 139 | button_value=button.getvar(button['variable']) 140 | if button_value=='1': 141 | self.tv.item(item,tags='select') 142 | else: 143 | self.tv.item(item, tags='oddrow') 144 | self.all_button_select()#根据所有按钮改变 全选按钮状态 145 | 146 | def select_tree(self,event): 147 | '''tree绑定的回调函数 148 | 作用:根据所点击的item改变 对应的按钮''' 149 | select_item=self.tv.focus() 150 | button = self.orm[select_item][0] 151 | button.invoke() #改变对应按钮的状态,而且调用其函数 152 | 153 | def all_button_select(self): 154 | '''根据所有按钮改变 全选按钮状态 155 | 循环所有按钮,当有一个按钮没有被打勾时,全选按钮取消打勾''' 156 | for [button] in self.orm.values(): 157 | button_value = button.getvar(button['variable']) 158 | if button_value=='0': 159 | self.all_buttonvar.set(0) 160 | break 161 | else: 162 | self.all_buttonvar.set(1) 163 | 164 | def cancelAll(self): 165 | for item,[button] in self.orm.items(): 166 | button.deselect() 167 | self.tv.item(item, tags='oddrow') 168 | 169 | def getSelected(self): 170 | tmp=1 171 | output=[] 172 | for [button] in self.orm.values(): 173 | button_value = button.getvar(button['variable']) 174 | if button_value!='0': 175 | output.append(tmp) 176 | tmp+=1 177 | return output 178 | -------------------------------------------------------------------------------- /password.py: -------------------------------------------------------------------------------- 1 | import hmac 2 | import hashlib 3 | import d 4 | 5 | def sha256(string): 6 | sha256 = hashlib.sha256() 7 | sha256.update(string.encode('utf-8')) 8 | res = sha256.hexdigest() 9 | return res 10 | 11 | def hmacsha256(key,string): 12 | signature = hmac.new( 13 | bytes(key.encode('utf-8')), 14 | msg=string.encode('utf-8'), 15 | digestmod=hashlib.sha256 16 | ).hexdigest() 17 | return signature 18 | 19 | def password(url,method,time,nonce): 20 | key=d.Key 21 | str=url+time+nonce+method+key 22 | str=str.lower() 23 | mi=d.Key2 24 | return hmacsha256(mi,str) 25 | 26 | def readtxt(url,content): 27 | with open(url,'a') as file_handle: 28 | print(content) 29 | file_handle.write(content) 30 | 31 | -------------------------------------------------------------------------------- /pica.py: -------------------------------------------------------------------------------- 1 | import d 2 | import json 3 | import post 4 | import fileManager 5 | import time 6 | 7 | class pica(): 8 | 9 | def __init__(S,event): 10 | #event类,在这里主要用于打印信息到窗口控制台 11 | S.event=event 12 | #mrequest类,用于发送请求 13 | S.mrp=post.mrequest() 14 | #记录曾获取到的所有的漫画信息 15 | S.allInfo = [] 16 | #记录当前收藏页里的所有漫画信息 17 | S.allComicInfo = None 18 | #记录当前漫画的详细信息 19 | S.comicInfo= None 20 | #记录当前漫画分页里的所有图片详细信息 21 | S.allCPageInfo = None 22 | #已废除 23 | S.cPageInfo = None 24 | #记录当前漫画的所有章节 25 | S.allEpsInfo = None 26 | #记录当前章节的信息,是allEpsInfo的子类 27 | S.epsInfo = None 28 | #当前浏览的页数 29 | S.pageNum = -1 30 | #当前页中浏览的漫画是第几个 31 | S.index = 0 32 | #当前的章数 33 | S.epsID = 0 34 | #当前的图片分页数 35 | S.temID = 0 36 | #当前漫画的保存路径 37 | S.saveRootPath = None 38 | #当前图片的保存路径 39 | S.savePath = None 40 | #下载列表 41 | S.dolwnloadList=[] 42 | #当前的下载百分比 43 | S.dolwnloadRate=0 44 | S.event.printl("初始化完成") 45 | 46 | #用于尝试使用token和密码登录 47 | def login(self): 48 | self.event.printl("验证登录") 49 | if self.loginByFile()!=0: 50 | self.loginByWeb() 51 | 52 | #使用保存的token文件登录 53 | def loginByFile(self): 54 | if fileManager.isExist(".\\data\\token.dat"): 55 | self.mytoken=fileManager.readToken() 56 | #return self.testConn() 57 | return 0 58 | else: return 1 59 | 60 | #使用密码登录,并保存token 61 | def loginByWeb(self): 62 | self.event.printl('尝试使用密码登录中...') 63 | print("密码") 64 | print(d.Password) 65 | output=self.sendPost("auth/sign-in",{"email":d.Email,"password":d.Password},"POST").json() 66 | try: 67 | if output['message'] == 'invalid email or password': 68 | self.event.printl("-------------\n用户名或密码错误!请在设置中更改") 69 | return 1 70 | else: 71 | self.event.printl("登录成功!") 72 | self.mytoken=str(output['data']['token']) 73 | fileManager.creatTokenFile(self.mytoken) 74 | self.event.printl("token已保存") 75 | return 0 76 | except TypeError: 77 | self.login() 78 | 79 | #用来检测input值是否为空,是则返回other值 80 | def isNone(self,input,other): 81 | if(not input):return other 82 | else : return input 83 | 84 | #用来检测漫画名中是否有非法字符,是则替换 85 | def haveIllegalChar(self,input): 86 | return input.replace("\\", "").replace("/","").replace(":", "").replace("*", "").replace("?", "").replace("\"","").replace("<","").replace(">","").replace("|","").replace(" ","") 87 | 88 | #获取这一页的收藏夹里的所有漫画信息,并导出到allComicInfo 89 | def getPage(self,index=None): 90 | index=self.isNone(index,self.index) 91 | tmp=self.sendPost("users/favourite?s=dd&page="+str(index),None,"GET",self.mytoken).json()['data']['comics'] 92 | if self.pageNum==-1: 93 | self.pageNum=int(tmp['pages']) 94 | self.allComicInfo = tmp['docs'] 95 | for item in self.allComicInfo: 96 | if self.event.isDownloaded(item['_id']): 97 | item['download']=True 98 | else: 99 | item['download']=False 100 | self.event.setPage(index,self.pageNum) 101 | self.event.printl("第%d页加载完成"%(index)) 102 | 103 | #获取一个漫画的章节列表 104 | #可以通过comicid获取,也可以直接使用当前的漫画号 105 | def getComicEps(self,comicid=None): 106 | comicid=self.isNone(comicid,self.comicInfo['_id']) 107 | firstEps=self.sendPost("comics/"+str(comicid)+"/eps?page=1",None,"GET",self.mytoken) 108 | if not self.event.checkError(firstEps): 109 | self.event.printl("将在5秒后重试") 110 | time.sleep(5) 111 | self.getComicEps(comicid) 112 | else: firstEps=firstEps.json()['data']['eps'] 113 | epsNum=firstEps["total"] 114 | firstEps=firstEps['docs'] 115 | pageItem=2 116 | while True: 117 | if epsNum>40: 118 | firstEps+=self.sendPost("comics/"+str(comicid)+"/eps?page="+str(pageItem),None,"GET",self.mytoken).json()['data']['eps']['docs'] 119 | pageItem+=1 120 | epsNum-=40 121 | else:break 122 | return firstEps 123 | 124 | #获取comicid所示漫画的epsid章节的temppage分页中的所有图片信息 125 | def getCPage(self,temppage=None,comicid=None,epsid=None): 126 | comicid=self.isNone(comicid,self.comicInfo['_id']) 127 | epsid=self.isNone(epsid,self.epsID) 128 | temppage=self.isNone(temppage,self.temID) 129 | return self.sendPost("comics/"+str(comicid)+"/order/"+str(epsid)+"/pages?page="+str(temppage),None,"GET",self.mytoken).json()['data']['pages'] 130 | 131 | #通过图片信息下载图片 132 | def getPic(self,picture,savepath): 133 | if self.event.isStartDownload==0: 134 | self.event.printl('下载已暂停') 135 | while True: 136 | time.sleep(1) 137 | if self.event.isStartDownload==1: 138 | break 139 | elif self.event.isStartDownload==2: 140 | return -1 141 | if not fileManager.isExist(savepath): 142 | tmp = self.mrp.sendPost(str(picture['fileServer'])+"/static/"+str(picture['path']),savepath,"img",self.mytoken) 143 | if not self.event.checkError(tmp): 144 | self.event.printl("此张图片已被搁置,可以在失败记录中重新下载") 145 | else: 146 | return 1 147 | 148 | #获取一张图片的保存路径及文件名 149 | def getPicSavePath(self,root,id,info): 150 | return root+"/"+str(id)+"_"+str(info['originalName']) 151 | 152 | #直接下载一个分页中的所有图片 153 | def get40Pic(self): 154 | picnum=(self.temID-1)*40+1 155 | for picture in self.allCPageInfo['docs']: 156 | self.event.printl('- '+str(picnum)+'/'+str(self.allCPageInfo['total'])+' -- '+str(picture['media']['originalName'])) 157 | if self.getPic(picture['media'],self.getPicSavePath(self.saveRootPath,picnum,picture['media']))==-1: 158 | return -1 159 | self.dolwnloadRate=int(picnum/d.DownloadingPage*100) 160 | self.event.refreshRate(self.dolwnloadRate) 161 | picnum+=1 162 | 163 | #直接下载一个章节中的所有图片 164 | def getEpsPic(self): 165 | self.saveRootPath=self.getComicSavePath() 166 | fileManager.mkdir(self.saveRootPath) 167 | self.temID=1 168 | while True: 169 | self.allCPageInfo=self.getCPage() 170 | if self.get40Pic()==-1: 171 | return -1 172 | if int(self.allCPageInfo['pages'])==self.temID:break 173 | else : self.temID+=1 174 | 175 | #获取这个漫画的保存路径 176 | def getComicSavePath(self,comic=None,eps=None,id=None): 177 | comic=self.isNone(comic,self.comicInfo) 178 | eps=self.isNone(eps,self.epsInfo) 179 | id=self.isNone(id,self.index) 180 | return "./comic/"+self.haveIllegalChar(str(comic['title']))+"/"+self.haveIllegalChar(str(eps['title'])) 181 | 182 | #直接下载一个漫画中的所有图片 183 | def getComicPic(self): 184 | self.event.printl("开始下载:"+self.comicInfo['title']) 185 | for self.epsInfo in self.allEpsInfo: 186 | self.epsID=self.epsInfo["order"] 187 | if self.getEpsPic()==-1: 188 | return -1 189 | self.event.addDownloadedList(self.comicInfo['_id']) 190 | self.event.printl("此漫画下载完成!") 191 | 192 | #将当前页的漫画添加到下载列表 193 | def putNowPagePicToList(self): 194 | for item in self.allComicInfo: 195 | if not self.event.isDownloaded(item['_id']) and not item in self.dolwnloadList: 196 | self.dolwnloadList.append(item) 197 | 198 | #将选中的添加到下载列表 199 | def putSelectPicToList(self,data): 200 | for item in data: 201 | selectComic=self.allComicInfo[item-1] 202 | if not self.event.isDownloaded(selectComic['_id']) and not selectComic in self.dolwnloadList and not selectComic == self.comicInfo: 203 | self.dolwnloadList.append(selectComic) 204 | else: 205 | self.event.printl('已存在或者正在下载:'+selectComic['title']) 206 | self.event.printl('已经添加到下载列表!') 207 | 208 | #将选中的漫画取消下载 209 | def cancelSectctInList(self,data): 210 | tmp=0 211 | for item in data: 212 | selectComic=self.allComicInfo[item-1] 213 | if selectComic in self.dolwnloadList: 214 | if selectComic == self.comicInfo: 215 | self.event.printl("取消下载中漫画...请等待当前下载的图片加载完成") 216 | self.event.isStartDownload=2 217 | else: 218 | tmp+=1 219 | self.dolwnloadList.remove(selectComic) 220 | self.event.printl("取消了%d个待下载漫画"%tmp) 221 | 222 | #下载列表中的第一个漫画 223 | def downloadFirstComic(self): 224 | self.comicInfo=self.dolwnloadList[0] 225 | self.event.fristStartDownload() 226 | #初始化类中的下载数据 227 | d.Downloading=self.comicInfo['_id'] 228 | d.DownloadingPage=self.comicInfo['pagesCount'] 229 | self.dolwnloadingNum=0 230 | self.dolwnloadRate=0 231 | self.event.refresh() 232 | self.event.refreshBefore() 233 | self.allEpsInfo=self.getComicEps() 234 | if self.getComicPic()==-1: 235 | d.Downloading='' 236 | self.dolwnloadList.pop(0) 237 | self.event.isStartDownload=1 238 | self.event.printl("下载中漫画已取消") 239 | self.event.refresh() 240 | return -1 241 | d.Downloading='' 242 | self.dolwnloadList.pop(0) 243 | self.event.finishDownload() 244 | self.event.refresh() 245 | 246 | #带有错误处理的重复请求方法 247 | def sendPost(self, nurl, payload, mothed, auth=""): 248 | while True: 249 | tmp=self.mrp.sendPost(nurl, payload, mothed, auth) 250 | if not self.event.checkError(tmp): 251 | self.event.printl("将在5秒后重试") 252 | time.sleep(5) 253 | else: 254 | return tmp -------------------------------------------------------------------------------- /post.py: -------------------------------------------------------------------------------- 1 | import time 2 | import uuid 3 | import password 4 | import requests 5 | import urllib3 6 | import json 7 | import d 8 | urllib3.disable_warnings() 9 | 10 | 11 | class mrequest(): 12 | def __init__(self): 13 | if d.useProxy: 14 | self.proxies = { 15 | "http": d.Proxy, 16 | "https": d.Proxy, 17 | } 18 | else: 19 | self.proxies = None 20 | 21 | def sendPost(self, nurl, payload, mothed, auth=""): 22 | timestr = str(int(time.time())) 23 | url = "https://picaapi.picacomic.com/"+nurl 24 | nonce = str(uuid.uuid1()).replace("-", "") 25 | sign = password.password(nurl, mothed, timestr, nonce) 26 | headers = { 27 | "api-key": "C69BAF41DA5ABD1FFEDC6D2FEA56B", 28 | "accept": "application/vnd.picacomic.com.v1+json", 29 | "app-channel": "1", 30 | "time": timestr, 31 | "nonce": nonce, 32 | "signature": sign, 33 | "app-version": d.Version, 34 | "app-uuid": "cb69a7aa-b9a8-3320-8cf1-74347e9ee970", 35 | "image-quality": d.Image_quality, 36 | "app-platform": "android", 37 | "app-build-version": d.Build_version, 38 | "Content-Type": "application/json; charset=UTF-8", 39 | "User-Agent": "okhttp/3.8.1", 40 | } 41 | if auth != "": 42 | headers.update({"authorization": auth}) 43 | if mothed == "POST": 44 | getnum = 0 45 | while True: 46 | try: 47 | self.__init__() 48 | print(self.proxies) 49 | if d.useProxy: 50 | r = requests.post(url, headers=headers, data=json.dumps( 51 | payload), verify=False, proxies=self.proxies, timeout=d.Timeout) 52 | else: 53 | r = requests.post(url, headers=headers, data=json.dumps( 54 | payload), verify=False, timeout=d.Timeout) 55 | except requests.exceptions.ReadTimeout: 56 | print("读写超时") 57 | getnum += 1 58 | if getnum > d.ImaConnNum: 59 | return -5 60 | continue 61 | except requests.exceptions.Timeout: 62 | getnum+=1 63 | if getnum>d.PostNum: 64 | return -5 65 | print("连接超时") 66 | continue 67 | except requests.exceptions.ProxyError as e: 68 | print("代理错误") 69 | return -3 70 | except requests.exceptions.ConnectionError: 71 | print("连接错误") 72 | return -2 73 | except BaseException as e: 74 | print("未知错误") 75 | print(e) 76 | return -4 77 | if r.status_code == 200: 78 | print('GET请求成功') 79 | break 80 | else: 81 | print('GET请求失败') 82 | print('尝试重新连接中。。。') 83 | time.sleep(3) 84 | getnum += 1 85 | if getnum > d.GetNum: 86 | return -1 87 | elif mothed == "GET": 88 | getnum = 0 89 | headers.pop("Content-Type") 90 | while True: 91 | try: 92 | self.__init__() 93 | print(self.proxies) 94 | r = requests.get( 95 | url, headers=headers, verify=False, proxies=self.proxies, timeout=d.Timeout) 96 | except requests.exceptions.ReadTimeout: 97 | print("读写超时") 98 | getnum += 1 99 | if getnum > d.ImaConnNum: 100 | return -5 101 | continue 102 | except requests.exceptions.Timeout: 103 | getnum+=1 104 | if getnum>d.PostNum: 105 | return -5 106 | print("连接超时") 107 | continue 108 | except requests.exceptions.ProxyError as e: 109 | print(e) 110 | print("代理错误") 111 | return -3 112 | except requests.exceptions.ConnectionError: 113 | print("连接错误") 114 | return -2 115 | except: 116 | print("未知错误") 117 | return -4 118 | if r.status_code == 200: 119 | print('GET请求成功') 120 | break 121 | else: 122 | print('GET请求失败') 123 | print('尝试重新连接中。。。') 124 | time.sleep(3) 125 | getnum += 1 126 | if getnum > d.GetNum: 127 | return -1 128 | else: 129 | getnum = 0 130 | headers.pop("Content-Type") 131 | while True: 132 | try: 133 | self.__init__() 134 | r = requests.get(nurl, headers=headers, verify=False,stream=True, proxies=self.proxies) 135 | 136 | except requests.exceptions.Timeout: 137 | print("连接超时") 138 | getnum += 1 139 | if getnum > d.ImaConnNum: 140 | return -5 141 | time.sleep(1) 142 | continue 143 | except requests.exceptions.ConnectionError: 144 | print("连接错误") 145 | getnum += 1 146 | if getnum > d.ImaConnNum: 147 | return -2 148 | time.sleep(1) 149 | continue 150 | except requests.exceptions.RequestException as e: 151 | print(e) 152 | print("读写超时") 153 | getnum += 1 154 | if getnum > d.ImaConnNum: 155 | return -5 156 | time.sleep(1) 157 | continue 158 | except: 159 | print("未知错误") 160 | continue 161 | if r.status_code == 200: 162 | open(payload, 'wb').write(r.content) 163 | break 164 | else: 165 | time.sleep(1) 166 | print('图片加载错误') 167 | getnum += 1 168 | if getnum > d.ImaConnNum: 169 | return -1 170 | return r 171 | -------------------------------------------------------------------------------- /setbox.py: -------------------------------------------------------------------------------- 1 | from tkinter import * # 导入 Tkinter 库 2 | from tkinter import ttk 3 | import tkinter.messagebox 4 | import d 5 | import fileManager 6 | import thread 7 | 8 | class setbox (): 9 | def __init__(self,root,event): 10 | self.event=event 11 | self.root=root 12 | self.event.setboxState=1 13 | screenWidth=root.winfo_screenwidth() 14 | screenHeight=root.winfo_screenheight() 15 | x=(screenWidth-270)/2 16 | y=(screenHeight-230)/2 17 | t2 = Toplevel(self.root) 18 | t2.geometry("270x230+%d+%d"%(x,y)) 19 | t2.title("设置") 20 | t2.transient(self.root) 21 | t2.resizable(0,0) 22 | Label(t2, text='哔咔账号', width=8,height=1).place(x=10,y=10) 23 | Label(t2, text='密码', width=5, height=1).place(x=10,y=50) 24 | Label(t2, text='下载图片质量', width=10, height=1).place(x=10,y=90) 25 | ttk.Separator(t2,orient='horizontal').place(x=10,y=150,width=240) 26 | self.var = StringVar() 27 | self.var.set(d.Image_quality) 28 | self.useProxy = BooleanVar() 29 | self.useProxy.set(d.useProxy) 30 | self.e1=Entry(t2) 31 | self.e3=Entry(t2, show='*') 32 | Radiobutton(t2, text='原画', variable=self.var, value='original').place(x=30,y=120) 33 | Radiobutton(t2, text='高', variable=self.var, value='high').place(x=90,y=120) 34 | Radiobutton(t2, text='中等', variable=self.var, value='medium').place(x=140,y=120) 35 | Radiobutton(t2, text='低', variable=self.var, value='low').place(x=200,y=120) 36 | Checkbutton(t2, text='使用https代理', variable=self.useProxy,command=self.usedProxy).place(x=10,y=160) 37 | self.e2=Entry(t2) 38 | self.e1.insert(0, d.Email) 39 | self.e3.insert(0,d.Password) 40 | self.e2.insert(0,d.Proxy) 41 | self.e1.place(x=80,y=10) 42 | self.e2.place(x=10,y=190) 43 | self.e3.place(x=80,y=50) 44 | Button(t2, text='确定', width=10, height=1, command=self.minput).place(x=170,y=180) 45 | t2.protocol('WM_DELETE_WINDOW', self.closeWindow) 46 | self.usedProxy() 47 | self.t2=t2 48 | 49 | def closeWindow(self): 50 | self.event.setboxState=0 51 | self.t2.destroy() 52 | 53 | def usedProxy(self): 54 | if self.useProxy.get() == False: 55 | self.e2.config(state=DISABLED) 56 | else: 57 | self.e2.config(state=NORMAL) 58 | 59 | def minput(self): 60 | data={'user':self.e1.get(),'password':self.e3.get(),'useProxy':self.useProxy.get(),'proxy':self.e2.get(),'quality':self.var.get()} 61 | if(data['user'] == '' or data['password'] == ''): 62 | tkinter.messagebox.showinfo(title='提示', message='请填写哔咔的用户名或密码') 63 | else: 64 | fileManager.mkdir(".\\comic") 65 | fileManager.createJsonFile(data,'config.json') 66 | d.Email=data['user'] 67 | d.useProxy=data['useProxy'] 68 | d.Password=data['password'] 69 | d.Proxy=data['proxy'] 70 | d.Image_quality=data['quality'] 71 | self.event.setboxState=0 72 | self.event.huoqu(1) 73 | self.t2.destroy() -------------------------------------------------------------------------------- /start.py: -------------------------------------------------------------------------------- 1 | #from tkinter import Button,Label,Frame,StringVar,Scrollbar,Text,Tk,IntVar 2 | from tkinter import * 3 | from tkinter import ttk 4 | import sys 5 | import event 6 | import mTreeList 7 | 8 | root = Tk() 9 | root.iconbitmap(".\\icon\\favicon.ico") 10 | root.resizable(0,0) 11 | root.title("哔咔收藏夹下载") 12 | screenWidth=root.winfo_screenwidth() 13 | screenHeight=root.winfo_screenheight() 14 | x=(screenWidth-800)/2 15 | y=(screenHeight-560)/2 16 | root.geometry("800x560+%d+%d"%(x,y)) 17 | root.protocol("WM_DELETE_WINDOW",event.close) 18 | 19 | toolBar = Frame(root) 20 | toolBar.pack(fill=X) 21 | downloadIcon=PhotoImage(file=".\\icon\\download.gif") 22 | refreshIcon=PhotoImage(file=".\\icon\\refresh.gif") 23 | cancerIcon=PhotoImage(file=".\\icon\\close.gif") 24 | infoIcon=PhotoImage(file=".\\icon\\info.gif") 25 | filterIcon=PhotoImage(file=".\\icon\\filter.gif") 26 | imageIcon=PhotoImage(file=".\\icon\\image.gif") 27 | delIcon=PhotoImage(file=".\\icon\\delete.gif") 28 | Button(toolBar,text="刷新",borderwidth=0,activeforeground="LightCoral",padx=5,height=40,command=event.huoqu,image=refreshIcon,compound=LEFT).pack(side=LEFT) 29 | Button(toolBar,text="已下载",borderwidth=0,activeforeground="LightCoral",padx=9,height=10,command=event.openfolder,image=imageIcon,compound=LEFT).pack(side=RIGHT) 30 | Button(toolBar,text="设置",borderwidth=0,activeforeground="LightCoral",padx=5,height=10,command=event.openMenu,image=filterIcon,compound=LEFT).pack(side=RIGHT) 31 | Button(toolBar,text="下载",borderwidth=0,activeforeground="LightCoral",padx=5,height=10,command=event.downloadSelected,image=downloadIcon,compound=LEFT).pack(side=LEFT) 32 | Button(toolBar,text="取消下载",borderwidth=0,activeforeground="LightCoral",padx=5,height=10,command=event.cancelSelected,image=cancerIcon,compound=LEFT).pack(side=LEFT) 33 | #Button(toolBar,text="删除",borderwidth=0,activeforeground="LightCoral",padx=5,height=10,command=event.downloadSelected,image=delIcon,compound=LEFT).pack(side=LEFT) 34 | Button(toolBar,text="关于",borderwidth=0,activeforeground="LightCoral",padx=10,height=10,command=event.openAbout,image=infoIcon,compound=LEFT).pack(side=RIGHT) 35 | tree_date=mTreeList.My_Tk(root) 36 | pageSetBar=Frame(root) 37 | pageSetBar.pack(fill=X) 38 | Button(pageSetBar,text="上一页",borderwidth=0,activeforeground="SkyBlue",padx=10,height=2,command=event.previousPage).pack(side=LEFT) 39 | PageT=StringVar() 40 | PageT.set("第 页,共 页") 41 | PageL=Label(pageSetBar,textvariable=PageT,padx=10) 42 | PageL.pack(side=LEFT) 43 | Button(pageSetBar,text="下一页",borderwidth=0,activeforeground="SkyBlue",padx=10,height=2,command=event.nextPage).pack(side=LEFT) 44 | StopBtton=Button(pageSetBar,text="暂停下载",borderwidth=0,activeforeground="SkyBlue",padx=10,height=2,command=event.PauseDownload) 45 | StartButton=Button(pageSetBar,text="继续下载",borderwidth=0,activeforeground="SkyBlue",padx=10,height=2,command=event.startDownload) 46 | downloadNum=StringVar() 47 | downloadNum.set("选中漫画后,点击[下载]开始下载") 48 | Label(pageSetBar,textvariable=downloadNum,padx=100,fg='Gray').pack(side=LEFT) 49 | DownloadStateList=(StopBtton,StartButton,downloadNum) 50 | 51 | pageBar=Frame(root) 52 | pageBar.place(relwidth=1,height=160,relx=1,rely=1,anchor="se") 53 | logT=Text(pageBar,bg="black",fg="white") 54 | logT.place(relwidth=1,height=150,relx=1,rely=1,anchor="se") 55 | 56 | event.tree=tree_date 57 | event.DownloadStateList=DownloadStateList 58 | event.log=logT 59 | event.root=root 60 | event.page=PageT 61 | event.mself=event 62 | 63 | event.printl("下载程序初始化") 64 | event.printl("v 1.0.0 BY MUYOO") 65 | event.getPica() 66 | event.checkConfig() 67 | event.printl("配置完成") 68 | 69 | event.download() 70 | 71 | root.mainloop() -------------------------------------------------------------------------------- /start.spec: -------------------------------------------------------------------------------- 1 | # -*- mode: python ; coding: utf-8 -*- 2 | 3 | block_cipher = None 4 | 5 | 6 | a = Analysis(['start.py'], 7 | pathex=['C:\\Users\\Muyoo\\Documents\\pythonFile\\pica'], 8 | binaries=[], 9 | datas=[], 10 | hiddenimports=[], 11 | hookspath=[], 12 | runtime_hooks=[], 13 | excludes=[], 14 | win_no_prefer_redirects=False, 15 | win_private_assemblies=False, 16 | cipher=block_cipher, 17 | noarchive=False) 18 | pyz = PYZ(a.pure, a.zipped_data, 19 | cipher=block_cipher) 20 | exe = EXE(pyz, 21 | a.scripts, 22 | [], 23 | exclude_binaries=True, 24 | name='start', 25 | debug=False, 26 | bootloader_ignore_signals=False, 27 | strip=False, 28 | upx=True, 29 | console=False , icon='icon\\favicon.ico') 30 | coll = COLLECT(exe, 31 | a.binaries, 32 | a.zipfiles, 33 | a.datas, 34 | strip=False, 35 | upx=True, 36 | upx_exclude=[], 37 | name='start') 38 | -------------------------------------------------------------------------------- /thread.py: -------------------------------------------------------------------------------- 1 | import threading 2 | import time 3 | 4 | class myThread (threading.Thread): 5 | def __init__(self,tree_date,mypica,event): 6 | threading.Thread.__init__(self) 7 | self.tree_date=tree_date 8 | self.mpica=mypica 9 | self.page=event.getNowPage() 10 | self.event=event 11 | 12 | def run(self): 13 | self.event.threadaState=1 14 | if self.mpica.login() == 1: 15 | input("error") 16 | ''' 17 | delList=self.tree_date.get_children() 18 | for item in delList: 19 | self.tree_date.delete(item) 20 | ''' 21 | #tmp=0 22 | #tmp2=1 23 | self.event.printl("获取收藏夹信息中...") 24 | #while True: 25 | self.mpica.getPage(self.page) 26 | #self.mpica.allInfo.extend(self.mpica.allComicInfo) 27 | self.event.insertList(self.mpica.allComicInfo) 28 | ''' 29 | for item in self.mpica.allComicInfo: 30 | self.tree_date.insert('',tmp,values=(item.get('_id',''),item.get('title',''),item.get('author',''),item.get('likesCount',''),item.get('pagesCount',''),item.get('epsCount',''),'')) 31 | tmp+=1 32 | ''' 33 | #if tmp2==self.mpica.pageNum:break 34 | #else:tmp2+=1 35 | # break 36 | self.event.refresh() 37 | self.event.threadaState=0 38 | self.event.printl("收藏夹加载完成!") 39 | 40 | class downThread (threading.Thread): 41 | def __init__(self,mypica,event): 42 | threading.Thread.__init__(self) 43 | self.mpica=mypica 44 | self.event=event 45 | self._stop_event = threading.Event() 46 | def run(self): 47 | while True: 48 | time.sleep(1) 49 | if self.event.isStartDownload==1: 50 | if len(self.mpica.dolwnloadList)!=0: 51 | self.mpica.downloadFirstComic() 52 | if self.stopped(): 53 | break 54 | 55 | def stop(self): 56 | self._stop_event.set() 57 | 58 | def stopped(self): 59 | return self._stop_event.is_set() 60 | 61 | ''' 62 | class refreshThread(threading.Thread): 63 | def __init__(self,tree,event,downing='',downed=[]): 64 | threading.Thread.__init__(self) 65 | self.tree_date=tree 66 | self.event=event 67 | self.downing=downing 68 | self.downed=downed 69 | def run(self): 70 | ''' --------------------------------------------------------------------------------