mirror of https://github.com/midoks/mdserver-web
parent
4f9f652c6b
commit
2caaad6d25
@ -1,23 +0,0 @@ |
|||||||
[db] |
|
||||||
DB_HOST = 127.0.0.1 |
|
||||||
DB_PORT = 3306 |
|
||||||
DB_USER = qbittorrent |
|
||||||
DB_PASS = qbittorrent |
|
||||||
DB_NAME = qbittorrent |
|
||||||
|
|
||||||
[qb] |
|
||||||
QB_HOST = 127.0.0.1 |
|
||||||
QB_PORT = 8080 |
|
||||||
QB_USER = admin |
|
||||||
QB_PWD = adminadmin |
|
||||||
|
|
||||||
|
|
||||||
[file] |
|
||||||
FILE_TO={$SERVER_PATH}/tmp |
|
||||||
FILE_TRANSFER_TO={$SERVER_PATH}/tmp |
|
||||||
FILE_OWN=www |
|
||||||
FILE_GROUP=www |
|
||||||
|
|
||||||
[task] |
|
||||||
TASK_RATE=4 |
|
||||||
TASK_COMPLETED_RATE=10 |
|
@ -1,29 +0,0 @@ |
|||||||
CREATE TABLE `pl_hash_list` ( |
|
||||||
`id` bigint(20) NOT NULL AUTO_INCREMENT, |
|
||||||
`name` text NOT NULL, |
|
||||||
`info_hash` varchar(40) NOT NULL, |
|
||||||
`length` bigint(20) NOT NULL, |
|
||||||
`create_time` datetime NOT NULL, |
|
||||||
PRIMARY KEY (`id`), |
|
||||||
UNIQUE KEY `info_hash` (`info_hash`), |
|
||||||
KEY `create_time` (`create_time`) |
|
||||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8; |
|
||||||
|
|
||||||
CREATE TABLE `pl_hash_file` ( |
|
||||||
`id` bigint(20) NOT NULL AUTO_INCREMENT, |
|
||||||
`pid` bigint(20) NOT NULL, |
|
||||||
`name` text NOT NULL, |
|
||||||
`m3u8` varchar(40) NOT NULL, |
|
||||||
`length` bigint(20) NOT NULL, |
|
||||||
`create_time` datetime NOT NULL, |
|
||||||
PRIMARY KEY (`id`), |
|
||||||
KEY `create_time` (`create_time`) |
|
||||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8; |
|
||||||
|
|
||||||
CREATE TABLE `pl_hash_queue` ( |
|
||||||
`id` bigint(20) NOT NULL AUTO_INCREMENT, |
|
||||||
`info_hash` varchar(40) NOT NULL, |
|
||||||
`create_time` datetime NOT NULL, |
|
||||||
PRIMARY KEY (`id`), |
|
||||||
KEY `create_time` (`create_time`) |
|
||||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8; |
|
Before Width: | Height: | Size: 3.7 KiB |
@ -1,22 +0,0 @@ |
|||||||
<div class="bt-form"> |
|
||||||
<div class="bt-w-main"> |
|
||||||
<div class="bt-w-menu"> |
|
||||||
<p class="bgw" onclick="pluginService('qbittorrent');">服务</p> |
|
||||||
<p onclick="pluginInitD('qbittorrent');">自启动</p> |
|
||||||
<p onclick="pluginConfig('qbittorrent', '','get_sql');" title="手动导入SQL">导入SQL</p> |
|
||||||
<p onclick="pluginConfig('qbittorrent');">配置</p> |
|
||||||
<p onclick="pluginLogs('qbittorrent','','get_run_Log', 25);">日志</p> |
|
||||||
<p onclick="qbList();">列表</p> |
|
||||||
|
|
||||||
</div> |
|
||||||
<div class="bt-w-con pd15"> |
|
||||||
<div class="soft-man-con"> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
|
|
||||||
</div> |
|
||||||
<script type="text/javascript"> |
|
||||||
pluginService('qbittorrent'); |
|
||||||
$.getScript( "/plugins/file?name=qbittorrent&f=js/qbittorrent.js"); |
|
||||||
</script> |
|
@ -1,333 +0,0 @@ |
|||||||
# coding: utf-8 |
|
||||||
|
|
||||||
import time |
|
||||||
import random |
|
||||||
import os |
|
||||||
import json |
|
||||||
import re |
|
||||||
import sys |
|
||||||
|
|
||||||
sys.path.append(os.getcwd() + "/class/core") |
|
||||||
import public |
|
||||||
|
|
||||||
reload(sys) |
|
||||||
sys.setdefaultencoding("utf8") |
|
||||||
|
|
||||||
sys.path.append('/usr/local/lib/python2.7/site-packages') |
|
||||||
|
|
||||||
|
|
||||||
app_debug = False |
|
||||||
if public.isAppleSystem(): |
|
||||||
app_debug = True |
|
||||||
|
|
||||||
|
|
||||||
def getPluginName(): |
|
||||||
return 'qbittorrent' |
|
||||||
|
|
||||||
|
|
||||||
def getPluginDir(): |
|
||||||
return public.getPluginDir() + '/' + getPluginName() |
|
||||||
|
|
||||||
|
|
||||||
def getServerDir(): |
|
||||||
return public.getServerDir() + '/' + getPluginName() |
|
||||||
|
|
||||||
|
|
||||||
def getInitDFile(): |
|
||||||
if app_debug: |
|
||||||
return '/tmp/' + getPluginName() |
|
||||||
return '/etc/init.d/' + getPluginName() |
|
||||||
|
|
||||||
|
|
||||||
def getArgs(): |
|
||||||
args = sys.argv[2:] |
|
||||||
tmp = {} |
|
||||||
args_len = len(args) |
|
||||||
|
|
||||||
if args_len == 1: |
|
||||||
t = args[0].strip('{').strip('}') |
|
||||||
t = t.split(':') |
|
||||||
tmp[t[0]] = t[1] |
|
||||||
elif args_len > 1: |
|
||||||
for i in range(len(args)): |
|
||||||
t = args[i].split(':') |
|
||||||
tmp[t[0]] = t[1] |
|
||||||
|
|
||||||
return tmp |
|
||||||
|
|
||||||
|
|
||||||
def checkArgs(data, ck=[]): |
|
||||||
for i in range(len(ck)): |
|
||||||
if not ck[i] in data: |
|
||||||
return (False, public.returnJson(False, '参数:(' + ck[i] + ')没有!')) |
|
||||||
return (True, public.returnJson(True, 'ok')) |
|
||||||
|
|
||||||
|
|
||||||
def getInitDTpl(): |
|
||||||
path = getPluginDir() + "/init.d/" + getPluginName() + ".tpl" |
|
||||||
return path |
|
||||||
|
|
||||||
|
|
||||||
def getSqlFile(): |
|
||||||
file = getPluginDir() + "/conf/qb.sql" |
|
||||||
return file |
|
||||||
|
|
||||||
|
|
||||||
def getConf(): |
|
||||||
file = getServerDir() + "/qb.conf" |
|
||||||
return file |
|
||||||
|
|
||||||
|
|
||||||
def getRunLog(): |
|
||||||
file = getServerDir() + "/logs.pl" |
|
||||||
return file |
|
||||||
|
|
||||||
|
|
||||||
def initDreplace(): |
|
||||||
|
|
||||||
ddir = getServerDir() + '/workers' |
|
||||||
if not os.path.exists(ddir): |
|
||||||
sdir = getPluginDir() + '/workers' |
|
||||||
public.execShell('cp -rf ' + sdir + ' ' + getServerDir()) |
|
||||||
|
|
||||||
cfg = getServerDir() + '/qb.conf' |
|
||||||
if not os.path.exists(cfg): |
|
||||||
cfg_tpl = getPluginDir() + '/conf/qb.conf' |
|
||||||
content = public.readFile(cfg_tpl) |
|
||||||
public.writeFile(cfg, content) |
|
||||||
|
|
||||||
file_tpl = getInitDTpl() |
|
||||||
service_path = os.path.dirname(os.getcwd()) |
|
||||||
|
|
||||||
initD_path = getServerDir() + '/init.d' |
|
||||||
if not os.path.exists(initD_path): |
|
||||||
os.mkdir(initD_path) |
|
||||||
file_bin = initD_path + '/' + getPluginName() |
|
||||||
|
|
||||||
# initd replace |
|
||||||
if not os.path.exists(file_bin): |
|
||||||
content = public.readFile(file_tpl) |
|
||||||
content = content.replace('{$SERVER_PATH}', service_path) |
|
||||||
public.writeFile(file_bin, content) |
|
||||||
public.execShell('chmod +x ' + file_bin) |
|
||||||
|
|
||||||
return file_bin |
|
||||||
|
|
||||||
|
|
||||||
def status(): |
|
||||||
data = public.execShell( |
|
||||||
"ps -ef|grep qbittorrent_worker | grep -v grep | awk '{print $2}'") |
|
||||||
if data[0] == '': |
|
||||||
return 'stop' |
|
||||||
return 'start' |
|
||||||
|
|
||||||
|
|
||||||
def start(): |
|
||||||
file = initDreplace() |
|
||||||
|
|
||||||
data = public.execShell(file + ' start') |
|
||||||
if data[1] == '': |
|
||||||
return 'ok' |
|
||||||
return data[1] |
|
||||||
|
|
||||||
|
|
||||||
def stop(): |
|
||||||
file = initDreplace() |
|
||||||
data = public.execShell(file + ' stop') |
|
||||||
if data[1] == '': |
|
||||||
return 'ok' |
|
||||||
return data[1] |
|
||||||
|
|
||||||
|
|
||||||
def restart(): |
|
||||||
file = initDreplace() |
|
||||||
data = public.execShell(file + ' restart') |
|
||||||
if data[1] == '': |
|
||||||
return 'ok' |
|
||||||
return data[1] |
|
||||||
|
|
||||||
|
|
||||||
def reload(): |
|
||||||
file = initDreplace() |
|
||||||
data = public.execShell(file + ' reload') |
|
||||||
if data[1] == '': |
|
||||||
return 'ok' |
|
||||||
return data[1] |
|
||||||
|
|
||||||
|
|
||||||
def initdStatus(): |
|
||||||
if not app_debug: |
|
||||||
if public.isAppleSystem(): |
|
||||||
return "Apple Computer does not support" |
|
||||||
|
|
||||||
initd_bin = getInitDFile() |
|
||||||
if os.path.exists(initd_bin): |
|
||||||
return 'ok' |
|
||||||
return 'fail' |
|
||||||
|
|
||||||
|
|
||||||
def initdInstall(): |
|
||||||
import shutil |
|
||||||
if not app_debug: |
|
||||||
if public.isAppleSystem(): |
|
||||||
return "Apple Computer does not support" |
|
||||||
|
|
||||||
mysql_bin = initDreplace() |
|
||||||
initd_bin = getInitDFile() |
|
||||||
shutil.copyfile(mysql_bin, initd_bin) |
|
||||||
public.execShell('chmod +x ' + initd_bin) |
|
||||||
return 'ok' |
|
||||||
|
|
||||||
|
|
||||||
def initdUinstall(): |
|
||||||
if not app_debug: |
|
||||||
if public.isAppleSystem(): |
|
||||||
return "Apple Computer does not support" |
|
||||||
initd_bin = getInitDFile() |
|
||||||
os.remove(initd_bin) |
|
||||||
return 'ok' |
|
||||||
|
|
||||||
|
|
||||||
def matchData(reg, content): |
|
||||||
tmp = re.search(reg, content).groups() |
|
||||||
return tmp[0] |
|
||||||
|
|
||||||
|
|
||||||
def getDbConfInfo(): |
|
||||||
cfg = getConf() |
|
||||||
content = public.readFile(cfg) |
|
||||||
data = {} |
|
||||||
data['DB_HOST'] = matchData("DB_HOST\s*=\s(.*)", content) |
|
||||||
data['DB_USER'] = matchData("DB_USER\s*=\s(.*)", content) |
|
||||||
data['DB_PORT'] = matchData("DB_PORT\s*=\s(.*)", content) |
|
||||||
data['DB_PASS'] = matchData("DB_PASS\s*=\s(.*)", content) |
|
||||||
data['DB_NAME'] = matchData("DB_NAME\s*=\s(.*)", content) |
|
||||||
return data |
|
||||||
|
|
||||||
|
|
||||||
def getQbConf(): |
|
||||||
cfg = getConf() |
|
||||||
content = public.readFile(cfg) |
|
||||||
data = {} |
|
||||||
data['QB_HOST'] = matchData("QB_HOST\s*=\s(.*)", content) |
|
||||||
data['QB_PORT'] = matchData("QB_PORT\s*=\s(.*)", content) |
|
||||||
data['QB_USER'] = matchData("QB_USER\s*=\s(.*)", content) |
|
||||||
data['QB_PWD'] = matchData("QB_PWD\s*=\s(.*)", content) |
|
||||||
return data |
|
||||||
|
|
||||||
|
|
||||||
def pMysqlDb(): |
|
||||||
data = getDbConfInfo() |
|
||||||
conn = mysql.mysql() |
|
||||||
conn.setHost(data['DB_HOST']) |
|
||||||
conn.setUser(data['DB_USER']) |
|
||||||
conn.setPwd(data['DB_PASS']) |
|
||||||
conn.setPort(int(data['DB_PORT'])) |
|
||||||
conn.setDb(data['DB_NAME']) |
|
||||||
return conn |
|
||||||
|
|
||||||
|
|
||||||
def pQbClient(): |
|
||||||
from qbittorrent import Client |
|
||||||
info = getQbConf() |
|
||||||
url = 'http://' + info['QB_HOST'] + ':' + info['QB_PORT'] + '/' |
|
||||||
qb = Client(url) |
|
||||||
qb.login(info['QB_USER'], info['QB_PWD']) |
|
||||||
return qb |
|
||||||
|
|
||||||
|
|
||||||
def getQbUrl(): |
|
||||||
info = getQbConf() |
|
||||||
url = 'http://' + info['QB_HOST'] + ':' + info['QB_PORT'] + '/' |
|
||||||
return public.returnJson(True, 'ok', url) |
|
||||||
|
|
||||||
|
|
||||||
def qbList(): |
|
||||||
args = getArgs() |
|
||||||
# data = checkArgs(args, ['type']) |
|
||||||
# if not data[0]: |
|
||||||
# return data[1] |
|
||||||
args_type = '' |
|
||||||
if 'type' in args: |
|
||||||
args_type = args['type'] |
|
||||||
|
|
||||||
f = ['downloading', 'completed'] |
|
||||||
tfilter = '' |
|
||||||
if args_type in f: |
|
||||||
tfilter = args['type'] |
|
||||||
try: |
|
||||||
qb = pQbClient() |
|
||||||
torrents = qb.torrents(filter=tfilter) |
|
||||||
data = {} |
|
||||||
data['type'] = tfilter |
|
||||||
data['torrents'] = torrents |
|
||||||
return public.returnJson(True, 'ok', data) |
|
||||||
except Exception as e: |
|
||||||
return public.returnJson(False, str(e)) |
|
||||||
|
|
||||||
|
|
||||||
def qbDel(): |
|
||||||
args = getArgs() |
|
||||||
data = checkArgs(args, ['hash']) |
|
||||||
if not data[0]: |
|
||||||
return data[1] |
|
||||||
qb = pQbClient() |
|
||||||
data = qb.delete(args['hash']) |
|
||||||
return public.returnJson(True, '操作成功!', data) |
|
||||||
|
|
||||||
|
|
||||||
def qbAdd(): |
|
||||||
args = getArgs() |
|
||||||
data = checkArgs(args, ['hash']) |
|
||||||
if not data[0]: |
|
||||||
return data[1] |
|
||||||
url = 'magnet:?xt=urn:btih:' + args['hash'] |
|
||||||
qb = pQbClient() |
|
||||||
data = qb.download_from_link(url) |
|
||||||
return public.returnJson(True, '操作成功!', data) |
|
||||||
|
|
||||||
|
|
||||||
def test(): |
|
||||||
qb = pQbClient() |
|
||||||
# magnet_link = "magnet:?xt=urn:btih:57a0ec92a61c60585f1b7a206a75798aa69285a5" |
|
||||||
# print qb.download_from_link(magnet_link) |
|
||||||
torrents = qb.torrents(filter='downloading') |
|
||||||
for torrent in torrents: |
|
||||||
print public.returnJson(False, torrent) |
|
||||||
|
|
||||||
if __name__ == "__main__": |
|
||||||
func = sys.argv[1] |
|
||||||
if func == 'status': |
|
||||||
print status() |
|
||||||
elif func == 'start': |
|
||||||
print start() |
|
||||||
elif func == 'stop': |
|
||||||
print stop() |
|
||||||
elif func == 'restart': |
|
||||||
print restart() |
|
||||||
elif func == 'reload': |
|
||||||
print reload() |
|
||||||
elif func == 'initd_status': |
|
||||||
print initdStatus() |
|
||||||
elif func == 'initd_install': |
|
||||||
print initdInstall() |
|
||||||
elif func == 'initd_uninstall': |
|
||||||
print initdUinstall() |
|
||||||
elif func == 'get_sql': |
|
||||||
print getSqlFile() |
|
||||||
elif func == 'conf': |
|
||||||
print getConf() |
|
||||||
elif func == 'get_run_Log': |
|
||||||
print getRunLog() |
|
||||||
elif func == 'qb_list': |
|
||||||
print qbList() |
|
||||||
elif func == 'qb_del': |
|
||||||
print qbDel() |
|
||||||
elif func == 'qb_add': |
|
||||||
print qbAdd() |
|
||||||
elif func == 'qb_url': |
|
||||||
print getQbUrl() |
|
||||||
elif func == 'test': |
|
||||||
print test() |
|
||||||
else: |
|
||||||
print 'error' |
|
@ -1,17 +0,0 @@ |
|||||||
{ |
|
||||||
"sort": 7, |
|
||||||
"ps": "一个新的轻量级BitTorrent客户端", |
|
||||||
"name": "qbittorrent", |
|
||||||
"title": "qBittorrent", |
|
||||||
"shell": "install.sh", |
|
||||||
"versions":["4.1.5"], |
|
||||||
"updates":["4.1.5"], |
|
||||||
"tip": "soft", |
|
||||||
"checks": "server/qbittorrent", |
|
||||||
"display": 1, |
|
||||||
"author": "Zend", |
|
||||||
"date": "2017-04-01", |
|
||||||
"home": "https://www.qbittorrent.org", |
|
||||||
"type": 0, |
|
||||||
"pid": "5" |
|
||||||
} |
|
@ -1,46 +0,0 @@ |
|||||||
#!/bin/sh |
|
||||||
# chkconfig: 2345 55 25 |
|
||||||
# description: qbittorrent Service |
|
||||||
|
|
||||||
### BEGIN INIT INFO |
|
||||||
# Provides: qbittorrent |
|
||||||
# Required-Start: $all |
|
||||||
# Required-Stop: $all |
|
||||||
# Default-Start: 2 3 4 5 |
|
||||||
# Default-Stop: 0 1 6 |
|
||||||
# Short-Description: starts qbittorrent |
|
||||||
# Description: starts the MDW-Web |
|
||||||
### END INIT INFO |
|
||||||
|
|
||||||
|
|
||||||
qb_start(){ |
|
||||||
cd {$SERVER_PATH}/qbittorrent/workers |
|
||||||
nohup python qbittorrent_worker.py > {$SERVER_PATH}/qbittorrent/logs.pl 2>&1 & |
|
||||||
echo "qbittorrent started" |
|
||||||
} |
|
||||||
|
|
||||||
qb_stop(){ |
|
||||||
echo "Stopping ..." |
|
||||||
#ps -ef | grep qbittorrent-nox-bin | grep -v grep | awk '{print $2}' | xargs kill -9 |
|
||||||
ps -ef | grep "qbittorrent_worker.py" | grep -v grep | awk '{print $2}' | xargs kill -9 |
|
||||||
echo "qbittorrent stopped" |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
case "$1" in |
|
||||||
start) |
|
||||||
qb_start |
|
||||||
;; |
|
||||||
stop) |
|
||||||
qb_stop |
|
||||||
;; |
|
||||||
restart|reload) |
|
||||||
qb_stop |
|
||||||
sleep 0.3 |
|
||||||
qb_start |
|
||||||
;; |
|
||||||
*) |
|
||||||
echo "Please use start or stop as first argument" |
|
||||||
;; |
|
||||||
esac |
|
||||||
|
|
@ -1,50 +0,0 @@ |
|||||||
#!/bin/bash |
|
||||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin |
|
||||||
export PATH |
|
||||||
|
|
||||||
curPath=`pwd` |
|
||||||
rootPath=$(dirname "$curPath") |
|
||||||
rootPath=$(dirname "$rootPath") |
|
||||||
serverPath=$(dirname "$rootPath") |
|
||||||
sysName=`uname` |
|
||||||
|
|
||||||
install_tmp=${rootPath}/tmp/bt_install.pl |
|
||||||
|
|
||||||
|
|
||||||
Install_qbittorrent() |
|
||||||
{ |
|
||||||
if [ $sysName == 'Darwin' ]; then |
|
||||||
echo 'apple system' |
|
||||||
else |
|
||||||
yum -y install qbittorrent-nox |
|
||||||
#安装Nux-Dextop源 |
|
||||||
sudo rpm --import http://li.nux.ro/download/nux/RPM-GPG-KEY-nux.ro |
|
||||||
sudo rpm -Uvh http://li.nux.ro/download/nux/dextop/el7/x86_64/nux-dextop-release-0-1.el7.nux.noarch.rpm |
|
||||||
yum install -y ffmpeg |
|
||||||
fi |
|
||||||
|
|
||||||
pip install python-qbittorrent |
|
||||||
|
|
||||||
echo '正在安装脚本文件...' > $install_tmp |
|
||||||
mkdir -p $serverPath/qbittorrent |
|
||||||
|
|
||||||
QB_DIR=${serverPath}/source/qbittorrent |
|
||||||
mkdir -p $QB_DIR |
|
||||||
|
|
||||||
echo '4.1.5' > $serverPath/qbittorrent/version.pl |
|
||||||
echo '安装完成' > $install_tmp |
|
||||||
} |
|
||||||
|
|
||||||
Uninstall_qbittorrent() |
|
||||||
{ |
|
||||||
rm -rf $serverPath/qbittorrent |
|
||||||
which yum && yum -y remove qbittorrent-nox |
|
||||||
echo "Uninstall_qbittorrent" > $install_tmp |
|
||||||
} |
|
||||||
|
|
||||||
action=$1 |
|
||||||
if [ "${1}" == 'install' ];then |
|
||||||
Install_qbittorrent |
|
||||||
else |
|
||||||
Uninstall_qbittorrent |
|
||||||
fi |
|
@ -1,186 +0,0 @@ |
|||||||
function qbPostMin(method, args, callback){ |
|
||||||
|
|
||||||
var req_data = {}; |
|
||||||
req_data['name'] = 'qbittorrent'; |
|
||||||
req_data['func'] = method; |
|
||||||
|
|
||||||
if (typeof(args) != 'undefined' && args!=''){ |
|
||||||
req_data['args'] = JSON.stringify(args); |
|
||||||
} |
|
||||||
|
|
||||||
$.post('/plugins/run', req_data, function(data) { |
|
||||||
if (!data.status){ |
|
||||||
layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']}); |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
if(typeof(callback) == 'function'){ |
|
||||||
callback(data); |
|
||||||
} |
|
||||||
},'json');
|
|
||||||
} |
|
||||||
|
|
||||||
function qbPost(method, args, callback){ |
|
||||||
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 }); |
|
||||||
qbPostMin(method,args,function(data){ |
|
||||||
layer.close(loadT); |
|
||||||
if(typeof(callback) == 'function'){ |
|
||||||
callback(data); |
|
||||||
}
|
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
function showHideHash(obj){ |
|
||||||
var a = "glyphicon-eye-open"; |
|
||||||
var b = "glyphicon-eye-close"; |
|
||||||
|
|
||||||
if($(obj).hasClass(a)){ |
|
||||||
$(obj).removeClass(a).addClass(b); |
|
||||||
$(obj).prev().text($(obj).prev().attr('data-pw')) |
|
||||||
} else{ |
|
||||||
$(obj).removeClass(b).addClass(a); |
|
||||||
$(obj).prev().text($(obj).attr('data-pw')); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
function copyText(password){ |
|
||||||
var clipboard = new ClipboardJS('#bt_copys'); |
|
||||||
clipboard.on('success', function (e) { |
|
||||||
layer.msg('复制成功',{icon:1,time:2000}); |
|
||||||
}); |
|
||||||
|
|
||||||
clipboard.on('error', function (e) { |
|
||||||
layer.msg('复制失败,浏览器不兼容!',{icon:2,time:2000}); |
|
||||||
}); |
|
||||||
$("#bt_copys").attr('data-clipboard-text',password); |
|
||||||
$("#bt_copys").click(); |
|
||||||
} |
|
||||||
|
|
||||||
function getLocalTime(nS) {
|
|
||||||
return new Date(parseInt(nS) * 1000).toLocaleString().replace(/:\d{1,2}$/,' ');
|
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function qbAdd(){ |
|
||||||
|
|
||||||
var loadOpen = layer.open({ |
|
||||||
type: 1, |
|
||||||
title: '添加资源', |
|
||||||
area: '400px', |
|
||||||
content:"<div class='bt-form pd20 pb70 c6'>\ |
|
||||||
<div class='version line'>\ |
|
||||||
<div><input class='bt-input-text mr5 outline_no' type='text' id='qb_hash' name='qb_hash' style='height: 28px; border-radius: 3px;width: 350px;' placeholder='hash'></div>\ |
|
||||||
</div>\ |
|
||||||
<div class='bt-form-submit-btn'>\ |
|
||||||
<button type='button' id='qb_close' class='btn btn-danger btn-sm btn-title'>关闭</button>\ |
|
||||||
<button type='button' id='qb_ok' class='btn btn-success btn-sm btn-title bi-btn'>确认</button>\ |
|
||||||
</div>\ |
|
||||||
</div>" |
|
||||||
}); |
|
||||||
|
|
||||||
$('#qb_close').click(function(){ |
|
||||||
layer.close(loadOpen); |
|
||||||
}); |
|
||||||
|
|
||||||
$('#qb_ok').click(function(){ |
|
||||||
var hash = $('#qb_hash').val(); |
|
||||||
qbPost('qb_add', {hash:hash}, function(data){ |
|
||||||
|
|
||||||
var rdata = $.parseJSON(data.data); |
|
||||||
if (rdata['status']){ |
|
||||||
showMsg(rdata.msg, function(){ |
|
||||||
qbList(); |
|
||||||
},{icon:1,time:2000,shade: [0.3, '#000']}); |
|
||||||
layer.close(loadOpen); |
|
||||||
} else { |
|
||||||
layer.msg(rdata.msg,{icon:2,time:2000,shade: [0.3, '#000']}); |
|
||||||
} |
|
||||||
}); |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
function qbDel(hash){ |
|
||||||
qbPost('qb_del', {hash:hash}, function(data){ |
|
||||||
var rdata = $.parseJSON(data.data); |
|
||||||
if (rdata['status']){ |
|
||||||
layer.msg(rdata.msg,{icon:1,time:2000,shade: [0.3, '#000']}); |
|
||||||
} else { |
|
||||||
layer.msg(rdata.msg,{icon:2,time:2000,shade: [0.3, '#000']}); |
|
||||||
} |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
function qbListFind(){ |
|
||||||
var qbs = $('#qb_selected').val(); |
|
||||||
if ( qbs == '0' ){ |
|
||||||
qbList(); |
|
||||||
} else { |
|
||||||
qbList(qbs); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
function openAdminUrl(){ |
|
||||||
qbPost('qb_url', '', function(data){ |
|
||||||
var rdata = $.parseJSON(data.data); |
|
||||||
window.open(rdata.data); |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
function qbList(search){ |
|
||||||
var _data = {}; |
|
||||||
_data['test'] ='yes'; |
|
||||||
if(typeof(search) != 'undefined'){ |
|
||||||
_data['type'] = search; |
|
||||||
} |
|
||||||
|
|
||||||
qbPost('qb_list', _data, function(data){ |
|
||||||
|
|
||||||
var rdata = $.parseJSON(data.data); |
|
||||||
if (!rdata['status']){ |
|
||||||
layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']}); |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
content = '<select id="qb_selected" class="bt-input-text mr20" style="width:30%;margin-bottom: 3px;">'+ |
|
||||||
'<option value="0">所有</option>' + |
|
||||||
'<option value="completed">已下载</option>' + |
|
||||||
'<option value="downloading">下载中</option>' + |
|
||||||
'</select>'; |
|
||||||
content += '<button class="btn btn-success btn-sm" onclick="qbListFind();">查找</button></div>'; |
|
||||||
|
|
||||||
content += '<div class="divtable" style="margin-top:5px;"><table class="table table-hover" width="100%" cellspacing="0" cellpadding="0" border="0">'; |
|
||||||
content += '<thead><tr>'; |
|
||||||
content += '<th>种子(hash)</th>'; |
|
||||||
content += '<th>添加时间</th>'; |
|
||||||
content += '<th>操作(<a class="btlink" onclick="qbAdd();">添加</a> | <a class="btlink" onclick="openAdminUrl();">管理</a>)</th>'; |
|
||||||
content += '</tr></thead>'; |
|
||||||
|
|
||||||
content += '<tbody>'; |
|
||||||
|
|
||||||
ulist = rdata.data.torrents; |
|
||||||
for (i in ulist){ |
|
||||||
content += '<tr><td>'+ |
|
||||||
'<span class="password" data-pw="'+ulist[i]['hash']+'">'+ulist[i]['hash'].substr(0,3)+'</span>' + |
|
||||||
'<span onclick="showHideHash(this)" data-pw="'+ulist[i]['hash'].substr(0,3)+'" class="glyphicon glyphicon-eye-open cursor pw-ico" style="margin-left:10px"></span>'+ |
|
||||||
'<span class="ico-copy cursor btcopy" style="margin-left:10px" title="复制种子hash" onclick="copyText(\''+ulist[i]['hash']+'\')"></span>'+ |
|
||||||
'</td>'+ |
|
||||||
'<td>'+getLocalTime(ulist[i]['added_on'])+'</td>'+ |
|
||||||
'<td><a class="btlink" onclick="qbDel(\''+ulist[i]['hash']+'\')">删除</a></td></tr>'; |
|
||||||
} |
|
||||||
|
|
||||||
content += '</tbody>'; |
|
||||||
content += '</table></div>'; |
|
||||||
|
|
||||||
$(".soft-man-con").html(content); |
|
||||||
|
|
||||||
var type = rdata.data.type; |
|
||||||
if (type == ''){ |
|
||||||
$("#qb_selected option[value='0']").attr("selected", true); |
|
||||||
} else { |
|
||||||
$("#qb_selected option[value='"+type+"']").attr("selected", true); |
|
||||||
} |
|
||||||
}); |
|
||||||
} |
|
@ -1,23 +0,0 @@ |
|||||||
[db] |
|
||||||
DB_HOST = 154.48.251.71 |
|
||||||
DB_PORT = 33061 |
|
||||||
DB_USER = qbittorrent |
|
||||||
DB_PASS = qbittorrent |
|
||||||
DB_NAME = qbittorrent |
|
||||||
|
|
||||||
[qb] |
|
||||||
QB_HOST = 127.0.0.1 |
|
||||||
QB_PORT = 8080 |
|
||||||
QB_USER = admin |
|
||||||
QB_PWD = adminadmin |
|
||||||
|
|
||||||
|
|
||||||
[file] |
|
||||||
FILE_TO={$SERVER_PATH}/tmp |
|
||||||
FILE_TRANSFER_TO={$SERVER_PATH}/tmp |
|
||||||
FILE_OWN=www |
|
||||||
FILE_GROUP=www |
|
||||||
|
|
||||||
[task] |
|
||||||
TASK_RATE=4 |
|
||||||
TASK_COMPLETED_RATE=10 |
|
@ -1,344 +0,0 @@ |
|||||||
#!/usr/bin/env python |
|
||||||
# encoding: utf-8 |
|
||||||
""" |
|
||||||
下载检测 |
|
||||||
""" |
|
||||||
|
|
||||||
import hashlib |
|
||||||
import os |
|
||||||
import time |
|
||||||
import datetime |
|
||||||
import traceback |
|
||||||
import sys |
|
||||||
import json |
|
||||||
import socket |
|
||||||
import threading |
|
||||||
from hashlib import sha1 |
|
||||||
from random import randint |
|
||||||
from struct import unpack |
|
||||||
from socket import inet_ntoa |
|
||||||
from threading import Timer, Thread |
|
||||||
from time import sleep |
|
||||||
|
|
||||||
reload(sys) |
|
||||||
sys.setdefaultencoding("utf8") |
|
||||||
|
|
||||||
sys.path.append('/usr/local/lib/python2.7/site-packages') |
|
||||||
|
|
||||||
# import pygeoip |
|
||||||
import MySQLdb as mdb |
|
||||||
|
|
||||||
|
|
||||||
from configparser import ConfigParser |
|
||||||
cp = ConfigParser() |
|
||||||
cp.read("../qb.conf") |
|
||||||
section_db = cp.sections()[0] |
|
||||||
DB_HOST = cp.get(section_db, "DB_HOST") |
|
||||||
DB_USER = cp.get(section_db, "DB_USER") |
|
||||||
DB_PORT = cp.getint(section_db, "DB_PORT") |
|
||||||
DB_PASS = cp.get(section_db, "DB_PASS") |
|
||||||
DB_NAME = cp.get(section_db, "DB_NAME") |
|
||||||
|
|
||||||
|
|
||||||
section_qb = cp.sections()[1] |
|
||||||
QB_HOST = cp.get(section_qb, "QB_HOST") |
|
||||||
QB_PORT = cp.get(section_qb, "QB_PORT") |
|
||||||
QB_USER = cp.get(section_qb, "QB_USER") |
|
||||||
QB_PWD = cp.get(section_qb, "QB_PWD") |
|
||||||
|
|
||||||
section_file = cp.sections()[2] |
|
||||||
FILE_TO = cp.get(section_file, "FILE_TO") |
|
||||||
FILE_TRANSFER_TO = cp.get(section_file, "FILE_TRANSFER_TO") |
|
||||||
FILE_OWN = cp.get(section_file, "FILE_OWN") |
|
||||||
FILE_GROUP = cp.get(section_file, "FILE_GROUP") |
|
||||||
|
|
||||||
section_task = cp.sections()[3] |
|
||||||
TASK_RATE = cp.getint(section_task, "TASK_RATE") |
|
||||||
TASK_COMPLETED_RATE = cp.getint(section_task, "TASK_COMPLETED_RATE") |
|
||||||
|
|
||||||
|
|
||||||
def formatTime(): |
|
||||||
return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) |
|
||||||
|
|
||||||
|
|
||||||
class downloadBT(Thread): |
|
||||||
|
|
||||||
def __init__(self): |
|
||||||
Thread.__init__(self) |
|
||||||
self.setDaemon(True) |
|
||||||
self.dbconn = mdb.connect( |
|
||||||
DB_HOST, DB_USER, DB_PASS, DB_NAME, port=DB_PORT, charset='utf8') |
|
||||||
self.dbconn.autocommit(False) |
|
||||||
self.dbcurr = self.dbconn.cursor() |
|
||||||
self.dbcurr.execute('SET NAMES utf8') |
|
||||||
self.qb = self.qb() |
|
||||||
|
|
||||||
_has_suffix = ['mp4', 'rmvb', 'flv', 'avi', 'mpg', 'mkv', 'wmv', 'avi'] |
|
||||||
has_suffix = [] |
|
||||||
for x in range(len(_has_suffix)): |
|
||||||
has_suffix.append('.' + _has_suffix[x]) |
|
||||||
has_suffix.append('.' + _has_suffix[x].upper()) |
|
||||||
self.has_suffix = has_suffix |
|
||||||
|
|
||||||
def query(self, sql): |
|
||||||
self.dbcurr.execute(sql) |
|
||||||
result = self.dbcurr.fetchall() |
|
||||||
data = map(list, result) |
|
||||||
return data |
|
||||||
|
|
||||||
def qb(self): |
|
||||||
from qbittorrent import Client |
|
||||||
url = 'http://' + QB_HOST + ':' + QB_PORT + '/' |
|
||||||
qb = Client(url) |
|
||||||
qb.login(QB_USER, QB_PWD) |
|
||||||
return qb |
|
||||||
|
|
||||||
def execShell(self, cmdstring, cwd=None, timeout=None, shell=True): |
|
||||||
import subprocess |
|
||||||
if shell: |
|
||||||
cmdstring_list = cmdstring |
|
||||||
else: |
|
||||||
cmdstring_list = shlex.split(cmdstring) |
|
||||||
if timeout: |
|
||||||
end_time = datetime.datetime.now() + datetime.timedelta(seconds=timeout) |
|
||||||
|
|
||||||
sub = subprocess.Popen(cmdstring_list, cwd=cwd, stdin=subprocess.PIPE, |
|
||||||
shell=shell, bufsize=4096, stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
|
||||||
|
|
||||||
while sub.poll() is None: |
|
||||||
time.sleep(0.1) |
|
||||||
if timeout: |
|
||||||
if end_time <= datetime.datetime.now(): |
|
||||||
raise Exception("Timeout:%s" % cmdstring) |
|
||||||
|
|
||||||
return sub.communicate() |
|
||||||
|
|
||||||
def md5(self, str): |
|
||||||
# 生成MD5 |
|
||||||
try: |
|
||||||
m = hashlib.md5() |
|
||||||
m.update(str) |
|
||||||
return m.hexdigest() |
|
||||||
except: |
|
||||||
return False |
|
||||||
|
|
||||||
def get_transfer_ts_file(self, to): |
|
||||||
return FILE_TRANSFER_TO + '/' + to + '.ts' |
|
||||||
|
|
||||||
def get_transfer_mp4_file(self, to): |
|
||||||
return FILE_TRANSFER_TO + '/' + to + '.mp4' |
|
||||||
|
|
||||||
def get_lock_file(self, to): |
|
||||||
return FILE_TRANSFER_TO + '/' + to + '.lock' |
|
||||||
|
|
||||||
def get_transfer_m3u5_dir(self, dirname): |
|
||||||
return FILE_TO + '/m3u8/' + dirname |
|
||||||
|
|
||||||
def fg_transfer_mp4_cmd(self, sfile, dfile): |
|
||||||
cmd = 'ffmpeg -y -i "' + sfile + \ |
|
||||||
'" -threads 1 -preset veryslow -crf 28 -c:v libx264 -strict -2 ' + dfile |
|
||||||
return cmd |
|
||||||
|
|
||||||
def fg_transfer_ts_cmd(self, file, to_file): |
|
||||||
cmd = 'ffmpeg -y -i ' + file + \ |
|
||||||
' -s 480x360 -vcodec copy -acodec copy -vbsf h264_mp4toannexb ' + to_file |
|
||||||
return cmd |
|
||||||
|
|
||||||
def fg_m3u8_cmd(self, ts_file, m3u8_file, to_file): |
|
||||||
cmd = 'ffmpeg -y -i ' + ts_file + ' -c copy -map 0 -f segment -segment_list ' + \ |
|
||||||
m3u8_file + ' -segment_time 3 ' + to_file |
|
||||||
return cmd |
|
||||||
|
|
||||||
def debug(self, msg): |
|
||||||
return formatTime() + ":" + msg |
|
||||||
|
|
||||||
def lock(self, sign): |
|
||||||
l = self.get_lock_file(sign) |
|
||||||
self.execShell('touch ' + l) |
|
||||||
|
|
||||||
def unlock(self, sign): |
|
||||||
l = self.get_lock_file(sign) |
|
||||||
self.execShell('rm -rf ' + l) |
|
||||||
|
|
||||||
def islock(self, sign): |
|
||||||
l = self.get_lock_file(sign) |
|
||||||
if os.path.exists(l): |
|
||||||
return True |
|
||||||
return False |
|
||||||
|
|
||||||
def ffmpeg(self, file=''): |
|
||||||
|
|
||||||
md5file = self.md5(file)[0:6] |
|
||||||
|
|
||||||
if not os.path.exists(file): |
|
||||||
print formatTime(), 'file not exists:', file |
|
||||||
return |
|
||||||
print self.debug('source file ' + file) |
|
||||||
|
|
||||||
mp4file = self.get_transfer_mp4_file(md5file) |
|
||||||
cmd_mp4 = self.fg_transfer_mp4_cmd(file, mp4file) |
|
||||||
print self.debug('cmd_mp4:' + cmd_mp4) |
|
||||||
|
|
||||||
if not os.path.exists(mp4file): |
|
||||||
os.system(cmd_mp4) |
|
||||||
else: |
|
||||||
print self.debug('mp4 exists:' + mp4file) |
|
||||||
|
|
||||||
if not os.path.exists(mp4file): |
|
||||||
print self.debug('mp4 not exists') |
|
||||||
return |
|
||||||
|
|
||||||
tsfile = self.get_transfer_ts_file(md5file) |
|
||||||
cmd_ts = self.fg_transfer_ts_cmd(mp4file, tsfile) |
|
||||||
print self.debug('cmd_ts:' + cmd_ts) |
|
||||||
if not os.path.exists(tsfile): |
|
||||||
os.system(cmd_ts) |
|
||||||
else: |
|
||||||
print self.debug('data_ts exists:' + mp4file) |
|
||||||
|
|
||||||
if not os.path.exists(tsfile): |
|
||||||
print self.debug('ts not exists') |
|
||||||
return |
|
||||||
|
|
||||||
m3u8_dir = self.get_transfer_m3u5_dir(md5file) |
|
||||||
if not os.path.exists(m3u8_dir): |
|
||||||
self.execShell('mkdir -p ' + m3u8_dir) |
|
||||||
|
|
||||||
m3u8_file = m3u8_dir + '/' + md5file + '.m3u8' |
|
||||||
tofile = m3u8_dir + '/%010d.ts' |
|
||||||
cmd_m3u8 = self.fg_m3u8_cmd(tsfile, m3u8_file, tofile) |
|
||||||
print self.debug('cmd_m3u8:' + cmd_m3u8) |
|
||||||
if not os.path.exists(m3u8_file): |
|
||||||
os.system(cmd_m3u8) |
|
||||||
self.execShell('chown -R ' + FILE_OWN + ':' + |
|
||||||
FILE_GROUP + ' ' + m3u8_dir) |
|
||||||
self.add_hash(md5file) |
|
||||||
else: |
|
||||||
self.add_hash(md5file) |
|
||||||
print self.debug('m3u8 exists:' + tofile) |
|
||||||
|
|
||||||
def add_hash_file(self): |
|
||||||
pass |
|
||||||
|
|
||||||
def add_hash(self, m3u8_name): |
|
||||||
print '-------------------------add_hash---start-----------------------' |
|
||||||
ct = formatTime() |
|
||||||
# print (self.sign_torrent) |
|
||||||
total_size = str(self.sign_torrent['total_size']) |
|
||||||
shash = self.sign_torrent['hash'] |
|
||||||
sname = self.sign_torrent['name'] |
|
||||||
|
|
||||||
info = self.query( |
|
||||||
"select id from pl_hash_list where info_hash='" + shash + "'") |
|
||||||
|
|
||||||
sname = mdb.escape_string(sname) |
|
||||||
if len(info) > 0: |
|
||||||
pid = str(info[0][0]) |
|
||||||
file_data = self.query( |
|
||||||
"select id from pl_hash_file where name='" + sname + "' and pid='" + pid + "'") |
|
||||||
if len(file_data) == 0: |
|
||||||
self.query("insert into pl_hash_file (`pid`,`name`,`m3u8`,`length`,`create_time`) values('" + |
|
||||||
pid + "','" + sname + "','" + m3u8_name + "','" + total_size + "','" + ct + "')") |
|
||||||
else: |
|
||||||
print shash, 'already is exists!' |
|
||||||
else: |
|
||||||
print 'insert into pl_hash_list' |
|
||||||
pid = self.dbcurr.execute("insert into pl_hash_list (`name`,`info_hash`,`length`,`create_time`) values('" + |
|
||||||
sname + "','" + shash + "','" + total_size + "','" + ct + "')") |
|
||||||
file_data = self.query( |
|
||||||
"select id from pl_hash_file where name='" + sname + "' and pid='" + pid + "'") |
|
||||||
if len(file_data) == 0: |
|
||||||
self.query("insert into pl_hash_file (`pid`,`name`,`m3u8`,`length`,`create_time`) values('" + |
|
||||||
pid + "','" + sname + "','" + m3u8_name + "','" + total_size + "','" + ct + "')") |
|
||||||
else: |
|
||||||
print shash, 'already is exists!' |
|
||||||
print '-------------------------add_hash---end--------------------------' |
|
||||||
|
|
||||||
def file_arr(self, path, filters=['.DS_Store']): |
|
||||||
file_list = [] |
|
||||||
flist = os.listdir(path) |
|
||||||
# print flist |
|
||||||
for i in range(len(flist)): |
|
||||||
file_path = os.path.join(path, flist[i]) |
|
||||||
if flist[i] in filters: |
|
||||||
continue |
|
||||||
if os.path.isdir(file_path): |
|
||||||
tmp = self.file_arr(file_path, filters) |
|
||||||
file_list.extend(tmp) |
|
||||||
else: |
|
||||||
file_list.append(file_path) |
|
||||||
return file_list |
|
||||||
|
|
||||||
def find_dir_video(self, path): |
|
||||||
flist = self.file_arr(path) |
|
||||||
video = [] |
|
||||||
for i in range(len(flist)): |
|
||||||
t = os.path.splitext(flist[i]) |
|
||||||
if t[1] in self.has_suffix: |
|
||||||
video.append(flist[i]) |
|
||||||
return video |
|
||||||
|
|
||||||
def video_do(self, path): |
|
||||||
if os.path.isfile(path): |
|
||||||
t = os.path.splitext(path) |
|
||||||
if t[1] in self.has_suffix: |
|
||||||
self.ffmpeg(path) |
|
||||||
else: |
|
||||||
vlist = self.find_dir_video(path) |
|
||||||
for i in range(len(vlist)): |
|
||||||
self.ffmpeg(vlist[i]) |
|
||||||
return '' |
|
||||||
|
|
||||||
def checkTask(self): |
|
||||||
while True: |
|
||||||
torrents = self.qb.torrents(filter='downloading') |
|
||||||
tlen = len(torrents) |
|
||||||
if tlen > 0: |
|
||||||
print "downloading torrents count:", tlen |
|
||||||
for torrent in torrents: |
|
||||||
print torrent['name'], ' task downloading!' |
|
||||||
else: |
|
||||||
print formatTime(), "no downloading task!" |
|
||||||
time.sleep(TASK_RATE) |
|
||||||
|
|
||||||
def completed(self): |
|
||||||
while True: |
|
||||||
|
|
||||||
torrents = self.qb.torrents(filter='completed') |
|
||||||
tlen = len(torrents) |
|
||||||
print "completed torrents count:", tlen |
|
||||||
if tlen > 0: |
|
||||||
for torrent in torrents: |
|
||||||
self.sign_torrent = torrent |
|
||||||
# print torrent |
|
||||||
path = torrent['save_path'] + torrent['name'] |
|
||||||
path = path.encode() |
|
||||||
try: |
|
||||||
self.video_do(path) |
|
||||||
except Exception as e: |
|
||||||
print formatTime(), str(e) |
|
||||||
|
|
||||||
print formatTime(), "done task!" |
|
||||||
else: |
|
||||||
print formatTime(), "no completed task!" |
|
||||||
time.sleep(TASK_COMPLETED_RATE) |
|
||||||
|
|
||||||
|
|
||||||
def test(): |
|
||||||
while True: |
|
||||||
print formatTime(), "no download task!", |
|
||||||
time.sleep(1) |
|
||||||
test() |
|
||||||
|
|
||||||
if __name__ == "__main__": |
|
||||||
|
|
||||||
dl = downloadBT() |
|
||||||
|
|
||||||
import threading |
|
||||||
task = threading.Thread(target=dl.checkTask) |
|
||||||
task.start() |
|
||||||
|
|
||||||
completed = threading.Thread(target=dl.completed) |
|
||||||
completed.start() |
|
||||||
|
|
||||||
# test() |
|
Loading…
Reference in new issue