一、下载界面

1、主界面下载显示

MainWindow首先对aria进行初始化,启动aria2。启动方法在download.py中。

# start aria2
        start_aria = StartAria2Thread()
        self.threadPool.append(start_aria)
        self.threadPool[0].start()
        self.threadPool[0].ARIA2RESPONDSIGNAL.connect(self.startAriaMessage)

然后定义添加下载链接界面,AddLinkWindow可以有多个,都存放在self.addlinkwindows_list中。

调用的时候有三个参数(self, self.callBack, self.persepolis_setting),下载添加界面通过回调函数传递参数给主界面的callBack函数。callBack获取下载信息后,添加到线程池中。

new_download = DownloadLink(gid, self)
            self.threadPool.append(new_download)
            self.threadPool[len(self.threadPool) - 1].start()
            self.threadPool[len(self.threadPool) -
                            1].ARIA2NOTRESPOND.connect(self.aria2NotRespond)

注意主界面的addLinkSpiderCallBack函数,该函数调用顺序为:

1、下载添加界面获取下载链接改变(linkLineChanged)信息

2、下载添加界面开启线程AddLinkSpiderThread尝试获取链接文件大小,通过parent将该线程添加到主界面线程池中。并将AddLinkSpiderThread的信号连接到主线程的addLinkSpiderCallBack函数,同时将下载添加界面的指针child添加到槽函数的参数中,这样主界面可以通过child访问下载添加界面。

self.parent.threadPool[len(self.parent.threadPool) - 1].ADDLINKSPIDERSIGNAL.connect(
                partial(self.parent.addLinkSpiderCallBack, child=self))

3、AddLinkSpiderThread线程将结果ADDLINKSPIDERSIGNAL信号发送给主界面addLinkSpiderCallBack函数,注意这里发射的时候,只有dict参数,连接的时候有两个参数。

self.ADDLINKSPIDERSIGNAL.emit(spider_dict)

4、主界面addLinkSpiderCallBack函数通过child调用下载添加界面,设置文件名称和大小的显示。

这样就是下载链接界面新增线程到主界面,然后主界面线程执行完成后控制子界面更新,为什么不是下载链接添加界面自己开启一个线程获取文件大小,然然后根据获取结果自己改变下载链接界面呢?

mainwindow.py:

class DownloadLink(QThread):ARIA2NOTRESPOND = pyqtSignal()def __init__(self, gid, parent):QThread.__init__(self)self.gid = gidself.parent = parentdef run(self):# add gid of download to the active gids in temp_db# or update data base , if it was existed beforetry:self.parent.temp_db.insertInSingleTable(self.gid)except:# release lockself.parent.temp_db.lock = Falsedictionary = {'gid': self.gid, 'status': 'active'}self.parent.temp_db.updateSingleTable(dictionary)# if request is not successful then persepolis is checking rpc# connection with download.aria2Version() functionanswer = download.downloadAria(self.gid, self.parent)if answer == False:version_answer = download.aria2Version()if version_answer == 'did not respond':self.ARIA2NOTRESPOND.emit()class MainWindow(MainWindow_Ui):def __init__(self, start_in_tray, persepolis_main, persepolis_setting):super().__init__(persepolis_setting)self.persepolis_setting = persepolis_settingself.persepolis_main = persepolis_main# list of threadsself.threadPool = []# start aria2start_aria = StartAria2Thread()self.threadPool.append(start_aria)self.threadPool[0].start()self.threadPool[0].ARIA2RESPONDSIGNAL.connect(self.startAriaMessage)def addLinkButtonPressed(self, button=None):addlinkwindow = AddLinkWindow(self, self.callBack, self.persepolis_setting)self.addlinkwindows_list.append(addlinkwindow)self.addlinkwindows_list[len(self.addlinkwindows_list) - 1].show()# callback of AddLinkWindowdef callBack(self, add_link_dictionary, download_later, category):# write information in data_baseself.persepolis_db.insertInDownloadTable([dict])self.persepolis_db.insertInAddLinkTable([add_link_dictionary])# if user didn't press download_later_pushButton in add_link window# then create new qthread for new download!if not(download_later):new_download = DownloadLink(gid, self)self.threadPool.append(new_download)self.threadPool[len(self.threadPool) - 1].start()self.threadPool[len(self.threadPool) -1].ARIA2NOTRESPOND.connect(self.aria2NotRespond)# open progress window for download.self.progressBarOpen(gid)# notify user# check that download scheduled or notif not(add_link_dictionary['start_time']):message = QCoreApplication.translate("mainwindow_src_ui_tr", "Download Starts")else:new_spider = SpiderThread(add_link_dictionary, self)self.threadPool.append(new_spider)self.threadPool[len(self.threadPool) - 1].start()self.threadPool[len(self.threadPool) - 1].SPIDERSIGNAL.connect(self.spiderUpdate)message = QCoreApplication.translate("mainwindow_src_ui_tr", "Download Scheduled")notifySend(message, '', 10000, 'no', parent=self)# see addlink.py filedef addLinkSpiderCallBack(self, spider_dict, child):# get file_name and file_sizefile_name = spider_dict['file_name']file_size = spider_dict['file_size']if file_size:file_size = 'Size: ' + str(file_size)child.size_label.setText(file_size)if file_name and not(child.change_name_checkBox.isChecked()):child.change_name_lineEdit.setText(file_name)child.change_name_checkBox.setChecked(True)

2、下载添加界面

下载添加界面AddLinkWindow将第一个参数self初始化为parent,后续通过该参数对主界面进行访问,第二个参数为回调函数,用于传递参数给主界面,第三个参数将系统设置传递给下载添加界面。

在下载链接改变时,将AddLinkSpiderThread加入到主界面的threadPool中,并将ADDLINKSPIDERSIGNAL连接到主界面的addLinkSpiderCallBack。

new_spider = AddLinkSpiderThread(dict)

self.parent.threadPool.append(new_spider)

self.parent.threadPool[len(self.parent.threadPool) - 1].ADDLINKSPIDERSIGNAL.connect(
                partial(self.parent.addLinkSpiderCallBack, child=self))

AddLinkSpiderThread通过spider.addLinkSpider获取到文件大小和名称信息,发送给主界面的addLinkSpiderCallBack函数,注意这里发射的时候,只有dict参数,连接的时候有两个参数。

self.ADDLINKSPIDERSIGNAL.emit(spider_dict)

在按下确定按钮后,通过callback回调函数调用传递参数给主界面。

addlink.py:

class AddLinkSpiderThread(QThread):ADDLINKSPIDERSIGNAL = pyqtSignal(dict)def __init__(self, add_link_dictionary):QThread.__init__(self)self.add_link_dictionary = add_link_dictionarydef run(self):try:# get file name and file sizefile_name, file_size = spider.addLinkSpider(self.add_link_dictionary)spider_dict = {'file_size': file_size, 'file_name': file_name}# emit resultsself.ADDLINKSPIDERSIGNAL.emit(spider_dict)class AddLinkWindow(AddLinkWindow_Ui):def __init__(self, parent, callback, persepolis_setting, plugin_add_link_dictionary={}):super().__init__(persepolis_setting)self.callback = callbackself.plugin_add_link_dictionary = plugin_add_link_dictionaryself.persepolis_setting = persepolis_settingself.parent = parent    self.link_lineEdit.textChanged.connect(self.linkLineChanged)self.ok_pushButton.clicked.connect(partial(self.okButtonPressed, download_later=False))self.download_later_pushButton.clicked.connect(partial(self.okButtonPressed, download_later=True))# enable when link_lineEdit is not empty and find size of file.def linkLineChanged(self, lineEdit):if str(self.link_lineEdit.text()) == '':self.ok_pushButton.setEnabled(False)self.download_later_pushButton.setEnabled(False)else:  # find file sizedict = {'link': str(self.link_lineEdit.text())}# spider is finding file sizenew_spider = AddLinkSpiderThread(dict)self.parent.threadPool.append(new_spider)self.parent.threadPool[len(self.parent.threadPool) - 1].start()self.parent.threadPool[len(self.parent.threadPool) - 1].ADDLINKSPIDERSIGNAL.connect(partial(self.parent.addLinkSpiderCallBack, child=self))self.ok_pushButton.setEnabled(True)self.download_later_pushButton.setEnabled(True)def okButtonPressed(self, button, download_later):# user submitted information by pressing ok_pushButton, so get information# from AddLinkWindow and return them to the mainwindow with callback!# save information in a dictionary(add_link_dictionary).self.add_link_dictionary = {'referer': referer, 'header': header, 'user_agent': user_agent, 'load_cookies': load_cookies,'out': out, 'start_time': start_time, 'end_time': end_time, 'link': link, 'ip': ip,'port': port, 'proxy_user': proxy_user, 'proxy_passwd': proxy_passwd,'download_user': download_user, 'download_passwd': download_passwd,'connections': connections, 'limit_value': limit, 'download_path': download_path}# get category of downloadcategory = str(self.add_queue_comboBox.currentText())del self.plugin_add_link_dictionary# return information to mainwindowself.callback(self.add_link_dictionary, download_later, category)# close windowself.close()

3、总结

1、线程间传递参数可以通过回调函数传递,也可以通过信号和槽传递。

2、主从线程之间,主线程将self传递给从线程,从线程可以对主线程的函数进行调用。从线程也可以将self传递给主线程,由主线程对从线程进行函数调用

二、下载文件

启动aria2的服务是通过subprocess.Popen启动的,每个选项的意义在aria2接口文档都有介绍。

subprocess 模块允许你生成新的进程,连接它们的输入、输出、错误管道,并且获取它们的返回码。此模块打算代替一些老旧的模块与功能os.system, os.popen*, os.spawn.

https://docs.python.org/zh-cn/3/library/subprocess.html#subprocess.Popen.communicate

https://blog.csdn.net/qq_34355232/article/details/87709418

subprocess.Popen([aria2d, '--no-conf', '--enable-rpc', '--rpc-listen-port=' + str(port), '--rpc-max-request-size=2M', '--rpc-listen-all', '--quiet=true'], stderr=subprocess.PIPE, stdout=subprocess.PIPE, stdin=subprocess.PIPE, shell=False, creationflags=NO_WINDOW)

添加下载链接是通过XML-RPC远程调用完成的:

server = xmlrpc.client.ServerProxy(server_uri, allow_none=True)

aria2的RPC接口介绍如下,支持JSON-RPC和XML-RPC。

https://aria2.github.io/manual/en/html/aria2c.html#rpc-interface

python的XML-RPC库介绍文档很多,找了两个如下:

https://www.jianshu.com/p/9987913cf734

https://developer.51cto.com/art/201906/597963.htm

GID:aria2通过GID索引管理每个下载,GID为64位二进制数。RPC访问时,表示为长度16个字符的十六进制字符串。通常aria2为每个下载链接产生衣蛾GID,用户也可以通过GID选项指定。

通过XML-RPC访问aria2

aria2.addUri([secret, ]uris[, options[, position]])¶

添加下载的链接,URIS是下载链接数组,option,positon是一个整数,表示插在下载队列的位置,0表示第一个。如果没有提供position参数或者position比队列的长度长,则添加的下载在下载队列的最后。该方法返回新注册下载的GID。

aria2.tellStatus([secret, ]gid[, keys])

该方法返回指定下载GID的进展,keys是一个字符串数组,指定了需要查询哪些项目。如果keys为空或者省略,则包含所有的项目。常用的项目有gid、status、totalLength、completedLength、downloadSpeeduploadSpeednumSeeders、connections、dir、files。

aria2.tellActive([secret][, keys])

该方法查询激活下载的状态,查询的项目与aria2.tellStatus类似。

aria2.removeDownloadResult([secret, ]gid)

根据GID从存储中移除下载完成/下载错误/删除的下载,如果成功返回OK

aria2.remove([secret, ]gid)

根据GID删除下载,如果下载正在进行先停止该下载。该下载链接的状态变为removed状态。返回删除状态的GID。

aria2.pause([secret, ]gid)

暂停指定GID的下载链接,下载链接的状态变为paused。如果下载是激活的,则该下载链接放置在等待队列的最前面。要想将状态变为waiting,需要用aria2.unpause方法。

download.py

# get port from persepolis_setting
port = int(persepolis_setting.value('settings/rpc-port'))# get aria2_path
aria2_path = persepolis_setting.value('settings/aria2_path')# xml rpc
SERVER_URI_FORMAT = 'http://{}:{:d}/rpc'
server_uri = SERVER_URI_FORMAT.format(host, port)
server = xmlrpc.client.ServerProxy(server_uri, allow_none=True)# start aria2 with RPCdef startAria():# in Windowselif os_type == OS.WINDOWS:if aria2_path == "" or aria2_path == None or os.path.isfile(str(aria2_path)) == False:cwd = sys.argv[0]current_directory = os.path.dirname(cwd)aria2d = os.path.join(current_directory, "aria2c.exe")  # aria2c.exe pathelse:aria2d = aria2_path# NO_WINDOW option avoids opening additional CMD window in MS Windows.NO_WINDOW = 0x08000000if not os.path.exists(aria2d):logger.sendToLog("Aria2 does not exist in the current path!", "ERROR")return None# aria2 command in windowssubprocess.Popen([aria2d, '--no-conf', '--enable-rpc', '--rpc-listen-port=' + str(port),'--rpc-max-request-size=2M', '--rpc-listen-all', '--quiet=true'],stderr=subprocess.PIPE,stdout=subprocess.PIPE,stdin=subprocess.PIPE,shell=False,creationflags=NO_WINDOW)time.sleep(2)# check that starting is successful or not!answer = aria2Version()# return resultreturn answer# check aria2 release version . Persepolis uses this function to
# check that aria2 RPC connection is available or not.def aria2Version():try:answer = server.aria2.getVersion()except:# write ERROR messages in terminal and loglogger.sendToLog("Aria2 didn't respond!", "ERROR")answer = "did not respond"return answerdef downloadAria(gid, parent):# add_link_dictionary is a dictionary that contains user download request# information.# get information from data_baseadd_link_dictionary = parent.persepolis_db.searchGidInAddLinkTable(gid)answer = server.aria2.addUri([link], aria_dict)

三、数据库

使用sqlite3数据库教程:https://docs.python.org/zh-cn/3/library/sqlite3.html

有三个数据库TempDB在内存中,放置实时数据,PluginsDB放置浏览器插件传来的新链接数据,PersepolisDB是主要的数据库,存放下载信息。

TempDB有两个表,single_db_table存放下载中的GID,queue_db_table存放下载队列的GID信息。

PersepolisDB有四个表:

category_db_table存放类型信息,包括'All Downloads'、'Single Downloads'和'Scheduled Downloads'类型。

download_db_table存放主界面显示的下载状态表。

addlink_db_table存放下载添加界面添加的下载链接。

video_finder_db_table存放下载添加界面添加下载的信息。

# This class manages TempDB
# TempDB contains gid of active downloads in every session.
class TempDB():def __init__(self):# temp_db saves in RAM# temp_db_connectionself.temp_db_connection = sqlite3.connect(':memory:', check_same_thread=False)def createTables(self):# lock data baseself.lockCursor()self.temp_db_cursor.execute("""CREATE TABLE IF NOT EXISTS single_db_table(self.temp_db_cursor.execute("""CREATE TABLE IF NOT EXISTS queue_db_table(# persepolis main data base contains downloads information
# This class is managing persepolis.db
class PersepolisDB():def __init__(self):# persepolis.db file pathpersepolis_db_path = os.path.join(config_folder, 'persepolis.db')# persepolis_db_connectionself.persepolis_db_connection = sqlite3.connect(persepolis_db_path, check_same_thread=False)# queues_list contains name of categories and category settingsdef createTables(self):# lock data baseself.lockCursor()# Create category_db_table and add 'All Downloads' and 'Single Downloads' to itself.persepolis_db_cursor.execute("""CREATE TABLE IF NOT EXISTS category_db_table(# download table contains download table download items informationself.persepolis_db_cursor.execute("""CREATE TABLE IF NOT EXISTS download_db_table(# addlink_db_table contains addlink window download informationself.persepolis_db_cursor.execute("""CREATE TABLE IF NOT EXISTS addlink_db_table(# video_finder_db_table contains addlink window download informationself.persepolis_db_cursor.execute("""CREATE TABLE IF NOT EXISTS video_finder_db_table(

sqlite3 模块支持两种占位符:问号(qmark风格)和命名占位符(命名风格)。

# This is the qmark style:

cur.execute("insert into people values (?, ?)", (who, age))

# And this is the named style:

cur.execute("select * from people where name_last=:who and age=:age", {"who": who, "age": age})

coalesce函数返回其参数中第一个非空表达式的值,也即提供了参数则用新参数,未提供新参数则用原值。

self.temp_db_cursor.execute("""UPDATE single_db_table SET shutdown = coalesce(:shutdown, shutdown),status = coalesce(:status, status)WHERE gid = :gid""", dict)

MainWindow在初始化时创建CheckDownloadInfoThread线程,轮询每一个下载中的链接,并将结果返回给主界面的checkDownloadInfo函数进行下载状态更新。

        # CheckDownloadInfoThreadcheck_download_info = CheckDownloadInfoThread(self)self.threadPool.append(check_download_info)self.threadPool[1].start()self.threadPool[1].DOWNLOAD_INFO_SIGNAL.connect(self.checkDownloadInfo)self.threadPool[1].RECONNECTARIASIGNAL.connect(self.reconnectAria)

开源代码学习之persepolis【二】相关推荐

  1. 开源代码学习之persepolis【一】

    https://github.com/persepolisdm/persepolis https://github.com/aria2/aria2 Persepolis是一款以aria2为基础打造的下 ...

  2. github 开源 代码 学习 集合(转载)

    一个支持多种item类型的recycleView依赖注入库 1.通过注解的方式方便的把ViewHolder注入到recycleView中. 2.去除findViewByID等冗余操作. 3.去除编写a ...

  3. anaconda+python+pycharm代码学习——自动化办公(二)——安装mysql数据库and使用Navicat 连接 mysql

    安装mysql数据库一定注意 就安装5.版本的 别安装8. 我就是因为安装了8. 才一直安装不上 5.版本的超级好安装 这里参考这篇 非常好!!! 但是成功之后cmd输入mysql 却报错 mysql ...

  4. x264代码学习笔记(二):x264_encoder_encode函数

    encode()函数中循环调用encode_frame()函数进行逐帧编码: 调用x264_encoder_encode()函数完成一帧编码: 将编码后的码流载入码流文件中. static int e ...

  5. 开源代码分析技巧之三——老外如是说

    开源代码分析技巧之三--老外如是说 继续从深入分析开源代码说起,当然源码分析没有太多捷径可走.笔者只是探讨下,如何分析会更好些.特通过Samba技术邮件群组,向老外提问"如何更好的分析Sam ...

  6. 开源代码分析技巧之四——国外技术社区提问

    开源代码分析技巧之四--国外技术社区提问 在分析源码的时候,我们或多或少都会遇到过技术瓶颈.如果不突破这个瓶颈,接下来的研究就无法继续进行.并且不止对自己是瓶颈.对团队人员.技术顾问.资深人士都没有很 ...

  7. 杰奇reader.php源码,杰奇CMS reader.php开源代码

    杰奇的reader.php开源代码,可以进行二次开发!测试可用! define('JIEQI_MODULE_NAME', 'article'); require_once('../../global. ...

  8. 开源代码的使用 二次开发

    开源开发,就我的理解,有三种. 1.当作底层基础,使用.例如大家使用mysql就算.有人会认为我说错了.但我认为,开发不代表就是要同一个语言,甚至修改代码.例如我们使用动态库,原先的动态库是什么写的并 ...

  9. CSDN日报19035——流浪地球 春节十二响程序开源代码

    游戏开发 | [流浪地球]春节十二响程序开源代码 作者:刺客五六柒 前几天看完流浪地球,被李长条的春节十二响惊到了,这几天看了下别的博主写的开源伪代码(借鉴了框架),试着用CMD实现了模拟的行星发动机 ...

最新文章

  1. AI推理与Compiler
  2. 【MM模块】Batch 批次管理 2
  3. 深入理解计算机系统(2.6)------整数的运算
  4. Numpy 排序 -- sort()、argsort()
  5. php对称算法_php里简单的对称加密算法
  6. JavaScript 变量、函数与原型链
  7. 29 | 案例篇:Redis响应严重延迟,如何解决?
  8. mysql的单个数据库物理迁移出现ERROR 1146 (42S02): Table 'xx' doesn't exist [问题点数:100分]...
  9. 团队项目电梯会议视频
  10. 微信开发 根据openid 获取用户基本信息
  11. ALIN10129-自查方案
  12. 调试,是一件有挑战的事情
  13. 人生这场牌,怎么打才是最优解?
  14. 【三】Jmeter:测试片段
  15. 服务器阵列卡装系统蓝屏,电脑开raid做系统蓝屏-电脑开机就蓝屏怎么解决?
  16. 云栖大会day2总结 上午
  17. 专升本第七部分 计算机网络基础与网页设计
  18. 何为功能平价?特斯拉「抛弃」多传感融合,背后有哪些门道
  19. 在苹果 M1 上运行 Linux 虚拟机变得容易了
  20. 信号偏移成为边界地区呼吸的痛

热门文章

  1. vue.js3D立方体旋转播放特效
  2. 为自己加油!--ThoughtWorks面试后感
  3. SpringCloud熔断机制大概什么意思
  4. 应届生落户上海申请及办理流程
  5. 淘宝电商需求文档的书写
  6. VC运行库安装错误0x80240017解决过程
  7. Word操作之插入图片自动更改大小
  8. web前端期末大作业——基于HTML+CSS+JavaScript实现中国茶文化(30页)
  9. 过滤对象属性值为空的属性
  10. 警务系统三维可视化管理与情报研判综合分析平台