mirror of https://github.com/midoks/mdserver-web
parent
35d75781f0
commit
801478ba30
@ -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. |
@ -0,0 +1,14 @@ |
|||||||
|
# mw-qbittorrent |
||||||
|
mdserver-web|qbittorrent管理 |
||||||
|
|
||||||
|
|
||||||
|
### 安装过程 |
||||||
|
|
||||||
|
``` |
||||||
|
* 先进行压缩 `cd mw-qbittorrent && zip qbittorrent.zip -r ./* ` |
||||||
|
* 在mdserver-web点击`添加插件` |
||||||
|
``` |
||||||
|
|
||||||
|
### 截图 |
||||||
|
|
||||||
|
[](/screenshot/ss1.jpg) |
@ -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 |
||||||
|
|
@ -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; |
After Width: | Height: | Size: 3.0 KiB |
@ -0,0 +1,23 @@ |
|||||||
|
<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="pluginConfig('qbittorrent', '','rsync_shell');" title="异步脚本">异步脚本</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> |
@ -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' |
@ -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" |
||||||
|
} |
@ -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 |
||||||
|
|
@ -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 |
@ -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:"<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); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
After Width: | Height: | Size: 12 KiB |
@ -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() |
@ -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' |
Loading…
Reference in new issue