diff --git a/.gitignore b/.gitignore
index 59324bd41..b846f4b9f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -121,6 +121,5 @@ data/close.pl
ssl/input.pl
data/datadir.pl
data/502Task.pl
-plugins/qbittorrent/
data/default.pl
data/backup.pl
diff --git a/plugins/qbittorrent/LICENSE b/plugins/qbittorrent/LICENSE
new file mode 100644
index 000000000..b89fade39
--- /dev/null
+++ b/plugins/qbittorrent/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2019 Mr Chen
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/plugins/qbittorrent/README.md b/plugins/qbittorrent/README.md
new file mode 100644
index 000000000..6ae4b8a7b
--- /dev/null
+++ b/plugins/qbittorrent/README.md
@@ -0,0 +1,14 @@
+# mw-qbittorrent
+mdserver-web|qbittorrent管理
+
+
+### 安装过程
+
+```
+* 先进行压缩 `cd mw-qbittorrent && zip qbittorrent.zip -r ./* `
+* 在mdserver-web点击`添加插件`
+```
+
+### 截图
+
+[](/screenshot/ss1.jpg)
\ No newline at end of file
diff --git a/plugins/qbittorrent/conf/qb.conf b/plugins/qbittorrent/conf/qb.conf
new file mode 100755
index 000000000..765541ada
--- /dev/null
+++ b/plugins/qbittorrent/conf/qb.conf
@@ -0,0 +1,35 @@
+[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
+FILE_ENC_SWITCH=0
+FILE_API_URL=http://v.demo.com/api/key/id/{$KEY}
+FILE_ASYNC_SWITCH=0
+
+[task]
+TASK_SIZE_LIMIT=1
+TASK_RATE=4
+TASK_COMPLETED_RATE=10
+TASK_DEBUG=0
+
+[setting]
+QUEUE_SWITCH = 1
+MAX_ACTIVE_UPLOADS = 1
+MAX_ACTIVE_TORRENTS = 10
+MAX_ACTIVE_DOWNLOADS = 10
+
diff --git a/plugins/qbittorrent/conf/qb.sql b/plugins/qbittorrent/conf/qb.sql
new file mode 100755
index 000000000..08baac3a7
--- /dev/null
+++ b/plugins/qbittorrent/conf/qb.sql
@@ -0,0 +1,34 @@
+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,
+ `status` char(10) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT 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,
+ `key` char(40) NULL DEFAULT '',
+ `length` bigint(20) NULL DEFAULT 0,
+ `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` char(40) NOT NULL,
+ `length` bigint(20) NOT NULL DEFAULT 0,
+ `created_at` datetime NOT NULL,
+ `updated_at` datetime NOT NULL,
+ PRIMARY KEY (`id`),
+ INDEX `length`(`length`) USING BTREE,
+ KEY `created_at` (`created_at`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
\ No newline at end of file
diff --git a/plugins/qbittorrent/ico.png b/plugins/qbittorrent/ico.png
new file mode 100755
index 000000000..99808f644
Binary files /dev/null and b/plugins/qbittorrent/ico.png differ
diff --git a/plugins/qbittorrent/index.html b/plugins/qbittorrent/index.html
new file mode 100755
index 000000000..45229c8d3
--- /dev/null
+++ b/plugins/qbittorrent/index.html
@@ -0,0 +1,23 @@
+
+
\ No newline at end of file
diff --git a/plugins/qbittorrent/index.py b/plugins/qbittorrent/index.py
new file mode 100755
index 000000000..c7b6abbe3
--- /dev/null
+++ b/plugins/qbittorrent/index.py
@@ -0,0 +1,357 @@
+# 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 getRsyncShell():
+ file = getServerDir() + "/workers/rsync.sh"
+ return file
+
+
+def getConf():
+ file = getServerDir() + "/qb.conf"
+ return file
+
+
+def getRunLog():
+ file = getServerDir() + "/logs.pl"
+ return file
+
+
+def contentReplace(content):
+ service_path = public.getServerDir()
+ content = content.replace('{$SERVER_PATH}', service_path)
+ return content
+
+
+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)
+ content = contentReplace(content)
+ 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 = contentReplace(content)
+ 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():
+
+ cmd = "ps -ef | grep qbittorrent-nox |grep -v grep |awk '{print $2}'"
+ ret = public.execShell(cmd)
+ if ret[0] == '':
+ public.execShell('qbittorrent-nox -d')
+
+ file = initDreplace()
+
+ data = public.execShell(file + ' start')
+ if data[1] == '':
+ return 'ok'
+ return data[1]
+
+
+def stop():
+ file = initDreplace()
+ data = public.execShell(file + ' stop')
+ # cmd = "ps -ef | grep qbittorrent-nox |grep -v grep |awk '{print $2}' | xargs kill"
+ # public.execShell(cmd)
+ 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)
+ public.execShell('chkconfig --add ' + getPluginName())
+ return 'ok'
+
+
+def initdUinstall():
+ if not app_debug:
+ if public.isAppleSystem():
+ return "Apple Computer does not support"
+ initd_bin = getInitDFile()
+ os.remove(initd_bin)
+ public.execShell('chkconfig --del ' + getPluginName())
+ 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 == 'rsync_shell':
+ print getRsyncShell()
+ 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'
diff --git a/plugins/qbittorrent/info.json b/plugins/qbittorrent/info.json
new file mode 100755
index 000000000..c77d22c60
--- /dev/null
+++ b/plugins/qbittorrent/info.json
@@ -0,0 +1,18 @@
+{
+ "sort": 7,
+ "ps": "一个新的轻量级BitTorrent客户端",
+ "name": "qbittorrent",
+ "title": "qBittorrent",
+ "shell": "install.sh",
+ "versions":["4.1.5"],
+ "updates":["4.1.5"],
+ "tip": "soft",
+ "checks": "server/qbittorrent",
+ "path": "server/qbittorrent",
+ "display": 1,
+ "author": "Zend",
+ "date": "2017-04-01",
+ "home": "https://www.qbittorrent.org",
+ "type": 0,
+ "pid": "5"
+}
\ No newline at end of file
diff --git a/plugins/qbittorrent/init.d/qbittorrent.tpl b/plugins/qbittorrent/init.d/qbittorrent.tpl
new file mode 100755
index 000000000..a6fb06707
--- /dev/null
+++ b/plugins/qbittorrent/init.d/qbittorrent.tpl
@@ -0,0 +1,46 @@
+#!/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
+
diff --git a/plugins/qbittorrent/install.sh b/plugins/qbittorrent/install.sh
new file mode 100755
index 000000000..f72dfa7b6
--- /dev/null
+++ b/plugins/qbittorrent/install.sh
@@ -0,0 +1,74 @@
+#!/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
+
+#https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz
+
+Install_mac_ffmpeg()
+{
+ if [ ! -f $serverPath/source/ffmpeg-20180702-3c4af57-macos64-static.zip ];then
+ wget -O $serverPath/source/ffmpeg-20180702-3c4af57-macos64-static.zip https://ffmpeg.zeranoe.com/builds/macos64/static/ffmpeg-20180702-3c4af57-macos64-static.zip
+ fi
+
+ if [ ! -d $serverPath/lib/ffmpeg ];then
+ cd $serverPath/source && tar -xvf $serverPath/source/ffmpeg-20180702-3c4af57-macos64-static.zip
+ mv ffmpeg-20180702-3c4af57-macos64-static $serverPath/lib/ffmpeg
+ fi
+}
+
+Install_linux_ffmpeg()
+{
+ if [ ! -f $serverPath/source/ffmpeg-release-amd64-static.tar.xz ];then
+ wget -O $serverPath/source/ffmpeg-release-amd64-static.tar.xz https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz
+ fi
+
+ if [ ! -d $serverPath/lib/ffmpeg ];then
+ cd $serverPath/source && tar -xvf $serverPath/source/ffmpeg-release-amd64-static.tar.xz
+ mv ffmpeg-4.1.2-amd64-static $serverPath/lib/ffmpeg
+ fi
+}
+
+Install_qbittorrent()
+{
+ if [ $sysName == 'Darwin' ]; then
+ Install_mac_ffmpeg
+ else
+ yum -y install qbittorrent-nox
+ #qbittorrent-nox -d
+
+ Install_linux_ffmpeg
+ fi
+
+ pip install python-qbittorrent==0.2
+
+ 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
diff --git a/plugins/qbittorrent/js/qbittorrent.js b/plugins/qbittorrent/js/qbittorrent.js
new file mode 100755
index 000000000..e00249172
--- /dev/null
+++ b/plugins/qbittorrent/js/qbittorrent.js
@@ -0,0 +1,186 @@
+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:""
+ });
+
+ $('#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 = ''+
+ '所有 ' +
+ '已下载 ' +
+ '下载中 ' +
+ ' ';
+ content += '查找 ';
+
+ content += '';
+ content += '';
+ content += '种子(hash) ';
+ content += '添加时间 ';
+ content += '操作(添加 | 管理 ) ';
+ content += ' ';
+
+ content += '';
+
+ ulist = rdata.data.torrents;
+ for (i in ulist){
+ content += ''+
+ ''+ulist[i]['hash'].substr(0,3)+' ' +
+ ' '+
+ ' '+
+ ' '+
+ ''+getLocalTime(ulist[i]['added_on'])+' '+
+ '删除 ';
+ }
+
+ content += ' ';
+ content += '
';
+
+ $(".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);
+ }
+ });
+}
diff --git a/plugins/qbittorrent/screenshot/ss1.jpg b/plugins/qbittorrent/screenshot/ss1.jpg
new file mode 100644
index 000000000..807c51660
Binary files /dev/null and b/plugins/qbittorrent/screenshot/ss1.jpg differ
diff --git a/plugins/qbittorrent/workers/qbittorrent_worker.py b/plugins/qbittorrent/workers/qbittorrent_worker.py
new file mode 100755
index 000000000..d44e688f6
--- /dev/null
+++ b/plugins/qbittorrent/workers/qbittorrent_worker.py
@@ -0,0 +1,719 @@
+#!/usr/bin/env python
+# encoding: utf-8
+"""
+下载检测
+"""
+
+'''
+pl_hash_list 表字段 status说明
+
+0 视频资源 - 正常
+1 不含有视频资源
+2 3小时下载未反应
+4 3个月未下载完成
+'''
+
+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')
+
+
+def formatTime():
+ return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
+
+
+def getRunDir():
+ return os.getcwd()
+
+
+def getRootDir():
+ return os.path.dirname(os.path.dirname(getRunDir()))
+
+
+def toSize(size):
+ # 字节单位转换
+ d = ('b', 'KB', 'MB', 'GB', 'TB')
+ s = d[0]
+ for b in d:
+ if size < 1024:
+ return str(round(size, 2)) + ' ' + b
+ size = float(size) / 1024.0
+ s = b
+ return str(round(size, 2)) + ' ' + b
+
+# 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")
+FILE_ENC_SWITCH = cp.get(section_file, "FILE_ENC_SWITCH")
+FILE_API_URL = cp.get(section_file, "FILE_API_URL")
+FILE_ASYNC_SWITCH = cp.get(section_file, "FILE_ASYNC_SWITCH")
+
+
+section_task = cp.sections()[3]
+TASK_SIZE_LIMIT = cp.get(section_task, "TASK_SIZE_LIMIT")
+TASK_RATE = cp.getint(section_task, "TASK_RATE")
+TASK_COMPLETED_RATE = cp.getint(section_task, "TASK_COMPLETED_RATE")
+TASK_DEBUG = cp.getint(section_task, "TASK_DEBUG")
+
+section_setting = cp.sections()[4]
+QUEUE_SWITCH = cp.get(section_setting, "QUEUE_SWITCH")
+MAX_ACTIVE_UPLOADS = cp.getint(section_setting, "MAX_ACTIVE_UPLOADS")
+MAX_ACTIVE_TORRENTS = cp.getint(section_setting, "MAX_ACTIVE_TORRENTS")
+MAX_ACTIVE_DOWNLOADS = cp.getint(section_setting, "MAX_ACTIVE_DOWNLOADS")
+
+rooDir = getRootDir()
+ffmpeg_cmd = rooDir + "/lib/ffmpeg/ffmpeg"
+if not os.path.exists(ffmpeg_cmd):
+ ffmpeg_cmd = rooDir + "/lib/ffmpeg/bin/ffmpeg"
+
+
+class downloadBT(Thread):
+
+ __db_err = None
+
+ def __init__(self):
+ Thread.__init__(self)
+ self.setDaemon(True)
+ self.qb = self.qb()
+
+ self.qb.set_preferences(max_active_uploads=MAX_ACTIVE_UPLOADS)
+ self.qb.set_preferences(max_active_torrents=MAX_ACTIVE_TORRENTS)
+ self.qb.set_preferences(max_active_downloads=MAX_ACTIVE_DOWNLOADS)
+
+ _has_suffix = ['mp4', 'rmvb', 'flv', 'avi',
+ 'mpg', 'mkv', 'wmv', 'avi', 'rm']
+ 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 __conn(self):
+ try:
+ 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')
+ return True
+ except Exception as e:
+ self.__db_err = e
+ return False
+
+ def __close(self):
+ self.dbcurr.close()
+ self.dbconn.close()
+
+ def query(self, sql):
+ # 执行SQL语句返回数据集
+ if not self.__conn():
+ return self.__db_err
+ try:
+ self.dbcurr.execute(sql)
+ result = self.dbcurr.fetchall()
+ # print result
+ # 将元组转换成列表
+ data = map(list, result)
+ self.__close()
+ return data
+ except Exception, ex:
+ return ex
+
+ def execute(self, sql):
+ if not self.__conn():
+ return self.__db_err
+ try:
+ result = self.dbcurr.execute(sql)
+ self.dbconn.commit()
+ self.__close()
+ return result
+ except Exception, ex:
+ return ex
+
+ 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 readFile(self, filename):
+ # 读文件内容
+ try:
+ fp = open(filename, 'r')
+ fBody = fp.read()
+ fp.close()
+ return fBody
+ 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_transfer_m3u5_dir(self, dirname, fname):
+ return FILE_TO + '/m3u8/' + dirname + '/' + fname
+
+ def get_transfer_hash_dir(self, dirname):
+ return FILE_TO + '/m3u8/' + dirname
+
+ def fg_transfer_mp4_cmd(self, sfile, dfile):
+ cmd = ffmpeg_cmd + ' -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_cmd + ' -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_cmd + ' -y -i ' + ts_file + ' -c copy -map 0 -f segment -segment_list ' + \
+ m3u8_file + ' -segment_time 3 ' + to_file
+ return cmd
+
+ def fg_m3u8enc_cmd(self, ts_file, m3u8_file, to_file, enc_dir):
+ cmd = ffmpeg_cmd + ' -y -i ' + ts_file + ' -threads 1 -strict -2 -hls_time 3 -hls_key_info_file ' + \
+ enc_dir + '/enc.keyinfo.txt -hls_playlist_type vod -hls_segment_filename ' + \
+ to_file + ' ' + m3u8_file
+ return cmd
+
+ def debug(self, msg):
+ return formatTime() + ":" + msg
+
+ def get_lock_file(self, to):
+ return '/tmp/mdw_qb_' + to + '.lock'
+
+ 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_file_sync(self):
+ if FILE_ASYNC_SWITCH == '1':
+ runDir = getRunDir()
+ sign = 'sync'
+
+ print 'file_sync... start'
+ if self.islock(sign):
+ print self.debug('sync doing,already lock it!!!')
+ else:
+ self.lock(sign)
+
+ r = self.execShell('sh -x ' + runDir + '/rsync.sh')
+ print self.debug('file_sync:' + r[0])
+ print self.debug('file_sync_error:' + r[1])
+ self.unlock(sign)
+ print 'file_sync... end'
+
+ def ffmpeg_del_file(self, mp4, ts, m3u8_dir):
+ print self.debug('delete middle file ... start' + mp4)
+ self.execShell('rm -rf ' + mp4)
+ self.execShell('rm -rf ' + ts)
+
+ if os.path.exists(m3u8_dir):
+ self.execShell('rm -rf ' + m3u8_dir)
+
+ print self.debug('delete middle file ... end' + ts)
+
+ def ffmpeg_del_hfile(self, shash_dir):
+ print self.debug('delete middle hash dir ... start' + shash_dir)
+ if os.path.exists(shash_dir):
+ self.execShell('rm -rf ' + shash_dir)
+
+ print self.debug('delete middle hash dir ... end' + shash_dir)
+
+ def ffmpeg(self, file=''):
+ if not os.path.exists(FILE_TRANSFER_TO):
+ self.execShell('mkdir -p ' + FILE_TRANSFER_TO)
+
+ fname = os.path.basename(file)
+ shash = self.sign_torrent['hash']
+ md5file = self.md5(file)
+ 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)
+ if not os.path.exists(mp4file):
+ print self.debug('cmd_mp4:' + cmd_mp4)
+ 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)
+ if not os.path.exists(tsfile):
+ print self.debug('cmd_ts:' + cmd_ts)
+ 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
+
+ md5Fname = self.md5(fname)
+ m3u8_dir = self.get_transfer_m3u5_dir(shash, md5Fname)
+ if not os.path.exists(m3u8_dir):
+ self.execShell('mkdir -p ' + m3u8_dir)
+
+ m3u8_file = m3u8_dir + '/index.m3u8'
+ tofile = m3u8_dir + '/%010d.ts'
+ print self.debug('tofile:' + tofile)
+ # 加密m3u8
+ if FILE_ENC_SWITCH != '0':
+ enc_dir = '/tmp/qb_m3u8'
+ cmd = self.fg_m3u8enc_cmd(tsfile, m3u8_file, tofile, enc_dir)
+ if os.path.exists(m3u8_file):
+ print self.debug('cmd_m3u8_enc exists:' + m3u8_file)
+ print self.debug('cmd_m3u8_enc:' + cmd)
+ self.ffmpeg_file_sync()
+ self.ffmpeg_del_file(mp4file, tsfile, m3u8_dir)
+ return
+
+ self.execShell('mkdir -p ' + enc_dir)
+ self.execShell('openssl rand -base64 16 > ' +
+ enc_dir + '/enc.key')
+ self.execShell('rm -rf ' + enc_dir + '/enc.keyinfo.txt')
+
+ try:
+ fid = self.add_hash(fname, md5Fname)
+ except Exception as e:
+ print 'add_hash_enc:' + str(e)
+ return
+ fid = self.add_hash(fname, md5Fname)
+ key = self.readFile(enc_dir + '/enc.key').strip()
+ self.set_hashfile_key(fid, key)
+
+ # FILE_API_URL
+ url = FILE_API_URL.replace('{$KEY}', fid)
+ enc_url = 'echo ' + url + ' >> ' + enc_dir + '/enc.keyinfo.txt'
+ self.execShell(enc_url)
+ enc_path = 'echo ' + enc_dir + '/enc.key >> ' + enc_dir + '/enc.keyinfo.txt'
+ self.execShell(enc_path)
+ enc_iv = 'openssl rand -hex 16 >> ' + enc_dir + '/enc.keyinfo.txt'
+ self.execShell(enc_iv)
+
+ os.system(cmd)
+ else:
+
+ if os.path.exists(m3u8_file):
+ print self.debug('m3u8 exists:' + tofile)
+ self.ffmpeg_file_sync()
+ self.ffmpeg_del_file(mp4file, tsfile, m3u8_dir)
+ return
+
+ cmd_m3u8 = self.fg_m3u8_cmd(tsfile, m3u8_file, tofile)
+ print self.debug('cmd_m3u8:' + cmd_m3u8)
+ os.system(cmd_m3u8)
+
+ try:
+ self.add_hash(fname, md5Fname)
+ except Exception as e:
+ print 'add_hash', str(e)
+
+ self.execShell('chown -R ' + FILE_OWN + ':' +
+ FILE_GROUP + ' ' + m3u8_dir)
+ self.execShell('chmod -R 755 ' + m3u8_dir)
+
+ self.ffmpeg_file_sync()
+ self.ffmpeg_del_file(mp4file, tsfile, m3u8_dir)
+
+ def get_bt_size(self, torrent):
+ total_size = '0'
+ if 'size' in torrent:
+ total_size = str(torrent['size'])
+
+ if 'total_size' in torrent:
+ total_size = str(torrent['total_size'])
+ return total_size
+
+ def get_hashlist_id(self):
+ ct = formatTime()
+
+ total_size = self.get_bt_size(self.sign_torrent)
+
+ shash = self.sign_torrent['hash']
+ sname = self.sign_torrent['name']
+ sname = mdb.escape_string(sname)
+
+ info = self.query(
+ "select id from pl_hash_list where info_hash='" + shash + "'")
+ if len(info) > 0:
+ pid = str(info[0][0])
+ else:
+ print 'insert into pl_hash_list data'
+ pid = self.execute("insert into pl_hash_list (`name`,`info_hash`,`length`,`create_time`) values('" +
+ sname + "','" + shash + "','" + total_size + "','" + ct + "')")
+ return pid
+
+ def set_hashlist_status(self, torrent, status):
+ ct = formatTime()
+
+ shash = torrent['hash']
+
+ info = self.query(
+ "select id from pl_hash_list where info_hash='" + shash + "'")
+ if len(info) > 0:
+ print 'set_hashlist_status update'
+ usql = "update pl_hash_list set `status`='" + \
+ str(status) + "' where info_hash='" + shash + "'"
+ self.execute(usql)
+ else:
+ print 'set_hashlist_status insert'
+ total_size = self.get_bt_size(torrent)
+ sname = torrent['name']
+ sname = mdb.escape_string(sname)
+ return self.execute("insert into pl_hash_list (`name`,`info_hash`,`length`,`status`,`create_time`) values('" +
+ sname + "','" + shash + "','" + total_size + "','" + str(status) + "','" + ct + "')")
+
+ def get_hashfile_id(self, fname, m3u8_name, pid):
+ ct = formatTime()
+
+ info = self.query(
+ "select id from pl_hash_file where name='" + fname + "' and pid='" + pid + "'")
+ if len(info) == 0:
+ print 'insert into pl_hash_file data !'
+ fid = self.execute("insert into pl_hash_file (`pid`,`name`,`m3u8`,`create_time`) values('" +
+ pid + "','" + fname + "','" + m3u8_name + "','" + ct + "')")
+ else:
+ print fname, ':', m3u8_name, 'already is exists!'
+ fid = str(info[0][0])
+ return fid
+
+ def set_hashfile_key(self, fid, key):
+ self.execute("update pl_hash_file set `key`='" +
+ mdb.escape_string(key) + "' where id=" + fid)
+
+ def add_queue(self, shash, size):
+ ct = formatTime()
+
+ info = self.query(
+ "select id from pl_hash_queue where info_hash='" + shash + "'")
+ if len(info) == 0:
+ sql = "insert into pl_hash_queue (`info_hash`,`length`,`created_at`,`updated_at`) values('" + \
+ shash + "','" + str(size) + "','" + ct + "','" + ct + "')"
+ return self.execute(sql)
+ else:
+ print 'queue:', shash, 'already is exists!'
+
+ def add_hash(self, fname, m3u8_name):
+ print '-------------------------add_hash---start-----------------------'
+
+ pid = self.get_hashlist_id()
+ fid = self.get_hashfile_id(fname, m3u8_name, pid)
+
+ print '-------------------------add_hash---end--------------------------'
+
+ return fid
+
+ def file_arr(self, path, filters=['.DS_Store']):
+ file_list = []
+ flist = os.listdir(path)
+
+ for i in range(len(flist)):
+ # 下载缓存文件过滤
+ if flist[i] == '.unwanted':
+ continue
+
+ 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)):
+ if self.is_video(flist[i]):
+ video.append(flist[i])
+ return video
+
+ def video_do(self, path):
+ if os.path.isfile(path):
+ if self.is_video(path):
+ self.ffmpeg(path)
+ else:
+ vlist = self.find_dir_video(path)
+ for v in vlist:
+ self.ffmpeg(v)
+ return ''
+
+ def is_video(self, path):
+ t = os.path.splitext(path)
+ if t[1] in self.has_suffix:
+ return True
+ return False
+
+ def non_download(self, torrent):
+ flist = self.qb.get_torrent_files(torrent['hash'])
+ is_video = False
+ for pos in range(len(flist)):
+ file = torrent['save_path'] + flist[pos]['name']
+ if not self.is_video(file):
+ self.qb.set_file_priority(torrent['hash'], pos, 0)
+ else:
+ is_video = True
+
+ # is video
+ if not is_video:
+ self.set_status(torrent, 1)
+
+ def set_status(self, torrent, status):
+ self.set_hashlist_status(torrent, status)
+ if TASK_DEBUG == 0 and status != 0:
+ self.qb.delete_permanently(torrent['hash'])
+
+ def is_downloading(self, torrent):
+ if torrent['name'] == torrent['hash']:
+ return True
+ else:
+ return False
+
+ def is_nondownload_overtime(self, torrent, sec):
+ ct = time.time()
+ use_time = int(ct) - int(torrent['added_on'])
+
+ flist = self.qb.get_torrent_files(torrent['hash'])
+ # print flist
+ flist_len = len(flist)
+ # 没有获取种子信息
+ # print 'ddd:',flist_len,use_time,sec
+ if flist_len == 0 and use_time > sec:
+ self.set_status(torrent, 2)
+ return True
+
+ is_video_download = False
+
+ # 获取了种子信息,但是没有下载
+ for pos in range(len(flist)):
+ file = torrent['save_path'] + flist[pos]['name']
+ if self.is_video(file):
+ if flist[pos]['progress'] != '0':
+ is_video_download = True
+
+ if not is_video_download and use_time > sec:
+ self.set_status(torrent, 3)
+ return True
+ return False
+
+ def is_downloading_overtime(self, torrent, sec):
+ ct = time.time()
+ use_time = int(ct) - int(torrent['added_on'])
+ if use_time > sec:
+ self.set_status(torrent, 4)
+ return True
+ return False
+
+ def is_downloading_overlimit(self, torrent):
+ sz = self.get_bt_size(torrent)
+ ct = formatTime()
+ size_limit = float(TASK_SIZE_LIMIT) * 1024 * 1024 * 1024
+ size_limit = int(size_limit)
+ print 'is_downloading_overlimit:', toSize(sz), toSize(size_limit)
+ if int(sz) > int(size_limit):
+ print 'overlimit sz:' + sz
+ self.add_queue(torrent['hash'], str(sz))
+ self.qb.delete_permanently(torrent['hash'])
+
+ def check_task(self):
+ while True:
+ torrents = self.qb.torrents(filter='downloading')
+ tlen = len(torrents)
+ if tlen > 0:
+ print "downloading torrents count:", tlen
+ for torrent in torrents:
+ if self.is_nondownload_overtime(torrent, 5 * 60):
+ pass
+ elif self.is_downloading_overtime(torrent, 7 * 24 * 60 * 60):
+ pass
+ elif self.is_downloading(torrent):
+ pass
+ elif self.is_downloading_overlimit(torrent):
+ pass
+ else:
+ self.non_download(torrent)
+ print torrent['name'], ' task downloading!'
+ else:
+ print self.debug("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
+ path = torrent['save_path'] + torrent['name']
+ path = path.encode()
+ try:
+ self.video_do(path)
+
+ hash_dir = self.get_transfer_hash_dir(torrent['hash'])
+ self.ffmpeg_del_hfile(hash_dir)
+ if TASK_DEBUG == 0:
+ self.qb.delete_permanently(torrent['hash'])
+ except Exception as e:
+ print formatTime(), str(e)
+ print self.debug("done task!")
+ else:
+ print self.debug("no completed task!")
+ time.sleep(TASK_COMPLETED_RATE)
+
+ def add_hash_task(self, shash):
+ url = 'magnet:?xt=urn:btih:' + shash
+ self.qb.download_from_link(url)
+ print self.debug('queue add_hash_task is ok ... ')
+
+ def queue(self):
+ while True:
+ if QUEUE_SWITCH == '1':
+ print self.debug("------------ do queue task start! ---------------")
+
+ setting = self.qb.preferences()
+ torrents = self.qb.torrents()
+ tlen = len(torrents)
+ # print tlen, setting['max_active_torrents']
+ add = int(setting['max_active_torrents']) - tlen
+
+ if add == 0:
+ print self.debug('the download queue is full ... ')
+ else:
+ size_limit = float(TASK_SIZE_LIMIT) * 1024 * 1024 * 1024
+ size_limit = int(size_limit)
+ size_sql_where = ''
+ size_sql = ''
+ if size_limit != 0:
+ size_sql = ',`length` desc '
+ size_sql_where = 'where `length`<=' + str(size_limit)
+
+ sql = "select * from pl_hash_queue " + size_sql_where + \
+ " order by created_at desc " + \
+ size_sql + " limit " + str(add)
+ info = self.query(sql)
+ info_len = len(info)
+
+ if info_len == 0:
+ print self.debug('queue data is empty ... ')
+ else:
+ for x in range(info_len):
+ self.add_hash_task(info[x][1])
+ self.execute(
+ 'delete from pl_hash_queue where id=' + str(info[x][0]))
+ print self.debug("------------ do queue task end ! ---------------")
+ time.sleep(TASK_RATE)
+
+
+def test():
+ while True:
+ print self.debug("no download task!")
+ time.sleep(1)
+ test()
+
+if __name__ == "__main__":
+
+ dl = downloadBT()
+
+ import threading
+ check_task = threading.Thread(target=dl.check_task)
+ check_task.start()
+
+ completed = threading.Thread(target=dl.completed)
+ completed.start()
+
+ queue = threading.Thread(target=dl.queue)
+ queue.start()
diff --git a/plugins/qbittorrent/workers/rsync.sh b/plugins/qbittorrent/workers/rsync.sh
new file mode 100755
index 000000000..311108f21
--- /dev/null
+++ b/plugins/qbittorrent/workers/rsync.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+echo 'hello world'