|
|
|
#-*- coding=utf-8 -*-
|
|
|
|
import requests
|
|
|
|
import json
|
|
|
|
import subprocess
|
|
|
|
import os
|
|
|
|
import time
|
|
|
|
import base64
|
|
|
|
import sys
|
|
|
|
if sys.version_info[0]==2:
|
|
|
|
import xmlrpclib
|
|
|
|
else:
|
|
|
|
import xmlrpc.client as xmlrpclib
|
|
|
|
from self_config import *
|
|
|
|
|
|
|
|
|
|
|
|
class PyAria2(object):
|
|
|
|
|
|
|
|
def __init__(self,host,port,secret,scheme,session=None):
|
|
|
|
'''
|
|
|
|
PyAria2 constructor.
|
|
|
|
host: string, aria2 rpc host, default is 'localhost'
|
|
|
|
port: integer, aria2 rpc port, default is 6800
|
|
|
|
session: string, aria2 rpc session saving.
|
|
|
|
'''
|
|
|
|
if not isAria2Installed():
|
|
|
|
raise Exception('aria2 is not installed, please install it before.')
|
|
|
|
|
|
|
|
if not isAria2rpcRunning():
|
|
|
|
cmd = 'aria2c' \
|
|
|
|
' --enable-rpc' \
|
|
|
|
' --rpc-listen-port {}' \
|
|
|
|
' --continue' \
|
|
|
|
' --max-concurrent-downloads=20' \
|
|
|
|
' --max-connection-per-server=10' \
|
|
|
|
' --rpc-max-request-size=1024M'.format(port)
|
|
|
|
|
|
|
|
if not session is None:
|
|
|
|
cmd += ' --input-file=%s' \
|
|
|
|
' --save-session-interval=60' \
|
|
|
|
' --save-session=%s' % (session, session)
|
|
|
|
|
|
|
|
subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
|
|
|
|
|
|
|
|
count = 0
|
|
|
|
while True:
|
|
|
|
if isAria2rpcRunning():
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
count += 1
|
|
|
|
time.sleep(3)
|
|
|
|
if count == 5:
|
|
|
|
raise Exception('aria2 RPC server started failure.')
|
|
|
|
print('aria2 RPC server is started.')
|
|
|
|
else:
|
|
|
|
print('aria2 RPC server is already running.')
|
|
|
|
self.server_uri = scheme+'://{}:{}/jsonrpc'.format(host, port)
|
|
|
|
self.secret = secret
|
|
|
|
self.server = xmlrpclib.ServerProxy(self.server_uri, allow_none=True)
|
|
|
|
|
|
|
|
def sendJsonRPC(self, data):
|
|
|
|
r = requests.post(self.server_uri, data=data)
|
|
|
|
return r.text
|
|
|
|
|
|
|
|
def getRPCBody(self, method, params=None):
|
|
|
|
'''Create RPC body'''
|
|
|
|
uid = '1'
|
|
|
|
params = params if params else []
|
|
|
|
if self.secret is not None and self.secret!='':
|
|
|
|
params.insert(0, 'token:{}'.format(self.secret))
|
|
|
|
j = json.dumps([{
|
|
|
|
'jsonrpc': '2.0',
|
|
|
|
'id': uid,
|
|
|
|
'method': method,
|
|
|
|
'params': params,
|
|
|
|
}])
|
|
|
|
return j
|
|
|
|
|
|
|
|
def addUri(self, uris, options=None, position=None):
|
|
|
|
'''
|
|
|
|
This method adds new HTTP(S)/FTP/BitTorrent Magnet URI.
|
|
|
|
uris: list, list of URIs
|
|
|
|
options: dict, additional options
|
|
|
|
position: integer, position in download queue
|
|
|
|
return: This method returns GID of registered download.
|
|
|
|
'''
|
|
|
|
params = [[uris]]
|
|
|
|
if options:
|
|
|
|
params.append(options)
|
|
|
|
#params.append(position)
|
|
|
|
return self.sendJsonRPC(data=self.getRPCBody('aria2.addUri', params))
|
|
|
|
|
|
|
|
def addTorrent(self, torrent, uris=None, options=None, position=None):
|
|
|
|
'''
|
|
|
|
This method adds BitTorrent download by uploading ".torrent" file.
|
|
|
|
torrent: string, torrent file path
|
|
|
|
uris: list, list of webseed URIs
|
|
|
|
options: dict, additional options
|
|
|
|
position: integer, position in download queue
|
|
|
|
return: This method returns GID of registered download.
|
|
|
|
'''
|
|
|
|
return self.server.aria2.addTorrent(xmlrpclib.Binary(open(torrent, 'rb').read()), uris, options, position)
|
|
|
|
|
|
|
|
def addMetalink(self, metalink, options=None, position=None):
|
|
|
|
'''
|
|
|
|
This method adds Metalink download by uploading ".metalink" file.
|
|
|
|
metalink: string, metalink file path
|
|
|
|
options: dict, additional options
|
|
|
|
position: integer, position in download queue
|
|
|
|
return: This method returns list of GID of registered download.
|
|
|
|
'''
|
|
|
|
return self.server.aria2.addMetalink(xmlrpclib.Binary(open(metalink, 'rb').read()), options, position)
|
|
|
|
|
|
|
|
def remove(self, gid):
|
|
|
|
'''
|
|
|
|
This method removes the download denoted by gid.
|
|
|
|
gid: string, GID.
|
|
|
|
return: This method returns GID of removed download.
|
|
|
|
'''
|
|
|
|
params = [gid]
|
|
|
|
return self.sendJsonRPC(data=self.getRPCBody('aria2.remove', params))
|
|
|
|
|
|
|
|
|
|
|
|
def forceRemove(self, gid):
|
|
|
|
'''
|
|
|
|
This method removes the download denoted by gid.
|
|
|
|
gid: string, GID.
|
|
|
|
return: This method returns GID of removed download.
|
|
|
|
'''
|
|
|
|
params = [gid]
|
|
|
|
return self.sendJsonRPC(data=self.getRPCBody('aria2.forceRemove', params))
|
|
|
|
|
|
|
|
def pause(self, gid):
|
|
|
|
'''
|
|
|
|
This method pauses the download denoted by gid.
|
|
|
|
gid: string, GID.
|
|
|
|
return: This method returns GID of paused download.
|
|
|
|
'''
|
|
|
|
params = [gid]
|
|
|
|
return self.sendJsonRPC(data=self.getRPCBody('aria2.pause', params))
|
|
|
|
|
|
|
|
def pauseAll(self):
|
|
|
|
'''
|
|
|
|
This method is equal to calling aria2.pause() for every active/waiting download.
|
|
|
|
return: This method returns OK for success.
|
|
|
|
'''
|
|
|
|
return self.sendJsonRPC(data=self.getRPCBody('aria2.pauseAll'))
|
|
|
|
|
|
|
|
def forcePause(self, gid):
|
|
|
|
'''
|
|
|
|
This method pauses the download denoted by gid.
|
|
|
|
gid: string, GID.
|
|
|
|
return: This method returns GID of paused download.
|
|
|
|
'''
|
|
|
|
params = [gid]
|
|
|
|
return self.sendJsonRPC(data=self.getRPCBody('aria2.forcePause', params))
|
|
|
|
|
|
|
|
def forcePauseAll(self):
|
|
|
|
'''
|
|
|
|
This method is equal to calling aria2.forcePause() for every active/waiting download.
|
|
|
|
return: This method returns OK for success.
|
|
|
|
'''
|
|
|
|
return self.sendJsonRPC(data=self.getRPCBody('aria2.forcePauseAll'))
|
|
|
|
|
|
|
|
def unpause(self, gid):
|
|
|
|
'''
|
|
|
|
This method changes the status of the download denoted by gid from paused to waiting.
|
|
|
|
gid: string, GID.
|
|
|
|
return: This method returns GID of unpaused download.
|
|
|
|
'''
|
|
|
|
params = [gid]
|
|
|
|
return self.sendJsonRPC(data=self.getRPCBody('aria2.unpause', params))
|
|
|
|
|
|
|
|
def unpauseAll(self):
|
|
|
|
'''
|
|
|
|
This method is equal to calling aria2.unpause() for every active/waiting download.
|
|
|
|
return: This method returns OK for success.
|
|
|
|
'''
|
|
|
|
return self.sendJsonRPC(data=self.getRPCBody('aria2.unpauseAll'))
|
|
|
|
|
|
|
|
def tellStatus(self, gid, keys=None):
|
|
|
|
'''
|
|
|
|
This method returns download progress of the download denoted by gid.
|
|
|
|
gid: string, GID.
|
|
|
|
keys: list, keys for method response.
|
|
|
|
return: The method response is of type dict and it contains following keys.
|
|
|
|
'''
|
|
|
|
params = [gid]
|
|
|
|
if keys:
|
|
|
|
params.append(keys)
|
|
|
|
return self.sendJsonRPC(data=self.getRPCBody('aria2.tellStatus', params))
|
|
|
|
|
|
|
|
|
|
|
|
def getUris(self, gid):
|
|
|
|
'''
|
|
|
|
This method returns URIs used in the download denoted by gid.
|
|
|
|
gid: string, GID.
|
|
|
|
return: The method response is of type list and its element is of type dict and it contains following keys.
|
|
|
|
'''
|
|
|
|
params = [gid]
|
|
|
|
return self.sendJsonRPC(data=self.getRPCBody('aria2.getUris', params))
|
|
|
|
|
|
|
|
def getFiles(self, gid):
|
|
|
|
'''
|
|
|
|
This method returns file list of the download denoted by gid.
|
|
|
|
gid: string, GID.
|
|
|
|
return: The method response is of type list and its element is of type dict and it contains following keys.
|
|
|
|
'''
|
|
|
|
params = [gid]
|
|
|
|
return self.sendJsonRPC(data=self.getRPCBody('aria2.getFiles', params))
|
|
|
|
|
|
|
|
|
|
|
|
def getPeers(self, gid):
|
|
|
|
'''
|
|
|
|
This method returns peer list of the download denoted by gid.
|
|
|
|
gid: string, GID.
|
|
|
|
return: The method response is of type list and its element is of type dict and it contains following keys.
|
|
|
|
'''
|
|
|
|
return self.server.aria2.getPeers(gid)
|
|
|
|
|
|
|
|
def getServers(self, gid):
|
|
|
|
'''
|
|
|
|
This method returns currently connected HTTP(S)/FTP servers of the download denoted by gid.
|
|
|
|
gid: string, GID.
|
|
|
|
return: The method response is of type list and its element is of type dict and it contains following keys.
|
|
|
|
'''
|
|
|
|
return self.server.aria2.getServers(gid)
|
|
|
|
|
|
|
|
def tellActive(self, keys=None):
|
|
|
|
'''
|
|
|
|
This method returns the list of active downloads.
|
|
|
|
keys: keys for method response.
|
|
|
|
return: The method response is of type list and its element is of type dict and it contains following keys.
|
|
|
|
'''
|
|
|
|
return self.server.aria2.tellActive(keys)
|
|
|
|
|
|
|
|
def tellWaiting(self, offset, num, keys=None):
|
|
|
|
'''
|
|
|
|
This method returns the list of waiting download, including paused downloads.
|
|
|
|
offset: integer, the offset from the download waiting at the front.
|
|
|
|
num: integer, the number of downloads to be returned.
|
|
|
|
keys: keys for method response.
|
|
|
|
return: The method response is of type list and its element is of type dict and it contains following keys.
|
|
|
|
'''
|
|
|
|
return self.server.aria2.tellWaiting(offset, num, keys)
|
|
|
|
|
|
|
|
def tellStopped(self, offset, num, keys=None):
|
|
|
|
'''
|
|
|
|
This method returns the list of stopped download.
|
|
|
|
offset: integer, the offset from the download waiting at the front.
|
|
|
|
num: integer, the number of downloads to be returned.
|
|
|
|
keys: keys for method response.
|
|
|
|
return: The method response is of type list and its element is of type dict and it contains following keys.
|
|
|
|
'''
|
|
|
|
return self.server.aria2.tellStopped(offset, num, keys)
|
|
|
|
|
|
|
|
def changePosition(self, gid, pos, how):
|
|
|
|
'''
|
|
|
|
This method changes the position of the download denoted by gid.
|
|
|
|
gid: string, GID.
|
|
|
|
pos: integer, the position relative which to be changed.
|
|
|
|
how: string.
|
|
|
|
POS_SET, it moves the download to a position relative to the beginning of the queue.
|
|
|
|
POS_CUR, it moves the download to a position relative to the current position.
|
|
|
|
POS_END, it moves the download to a position relative to the end of the queue.
|
|
|
|
return: The response is of type integer and it is the destination position.
|
|
|
|
'''
|
|
|
|
return self.server.aria2.changePosition(gid, pos, how)
|
|
|
|
|
|
|
|
def changeUri(self, gid, fileIndex, delUris, addUris, position=None):
|
|
|
|
'''
|
|
|
|
This method removes URIs in delUris from and appends URIs in addUris to download denoted by gid.
|
|
|
|
gid: string, GID.
|
|
|
|
fileIndex: integer, file to affect (1-based)
|
|
|
|
delUris: list, URIs to be removed
|
|
|
|
addUris: list, URIs to be added
|
|
|
|
position: integer, where URIs are inserted, after URIs have been removed
|
|
|
|
return: This method returns a list which contains 2 integers. The first integer is the number of URIs deleted. The second integer is the number of URIs added.
|
|
|
|
'''
|
|
|
|
return self.server.aria2.changeUri(gid, fileIndex, delUris, addUris, position)
|
|
|
|
|
|
|
|
def getOption(self, gid):
|
|
|
|
'''
|
|
|
|
This method returns options of the download denoted by gid.
|
|
|
|
gid: string, GID.
|
|
|
|
return: The response is of type dict.
|
|
|
|
'''
|
|
|
|
params = [gid]
|
|
|
|
return self.sendJsonRPC(data=self.getRPCBody('aria2.getOption', params))
|
|
|
|
|
|
|
|
def changeOption(self, gid, options):
|
|
|
|
'''
|
|
|
|
This method changes options of the download denoted by gid dynamically.
|
|
|
|
gid: string, GID.
|
|
|
|
options: dict, the options.
|
|
|
|
return: This method returns OK for success.
|
|
|
|
'''
|
|
|
|
params = [gid,options]
|
|
|
|
return self.sendJsonRPC(data=self.getRPCBody('aria2.changeOption', params))
|
|
|
|
# return self.server.aria2.changeOption(gid, options)
|
|
|
|
|
|
|
|
def getGlobalOption(self):
|
|
|
|
'''
|
|
|
|
This method returns global options.
|
|
|
|
return: The method response is of type dict.
|
|
|
|
'''
|
|
|
|
return self.server.aria2.getGlobalOption()
|
|
|
|
|
|
|
|
def changeGlobalOption(self, options):
|
|
|
|
'''
|
|
|
|
This method changes global options dynamically.
|
|
|
|
options: dict, the options.
|
|
|
|
return: This method returns OK for success.
|
|
|
|
'''
|
|
|
|
return self.sendJsonRPC(data=self.getRPCBody('aria2.changeGlobalOption', options))
|
|
|
|
|
|
|
|
|
|
|
|
def getGlobalStat(self):
|
|
|
|
'''
|
|
|
|
This method returns global statistics such as overall download and upload speed.
|
|
|
|
return: The method response is of type struct and contains following keys.
|
|
|
|
'''
|
|
|
|
return self.sendJsonRPC(data=self.getRPCBody('aria2.getGlobalStat'))
|
|
|
|
|
|
|
|
|
|
|
|
def purgeDownloadResult(self):
|
|
|
|
'''
|
|
|
|
This method purges completed/error/removed downloads to free memory.
|
|
|
|
return: This method returns OK for success.
|
|
|
|
'''
|
|
|
|
return self.sendJsonRPC(data=self.getRPCBody('aria2.purgeDownloadResult'))
|
|
|
|
|
|
|
|
|
|
|
|
def removeDownloadResult(self, gid):
|
|
|
|
'''
|
|
|
|
This method removes completed/error/removed download denoted by gid from memory.
|
|
|
|
return: This method returns OK for success.
|
|
|
|
'''
|
|
|
|
return self.server.aria2.removeDownloadResult(gid)
|
|
|
|
|
|
|
|
def getVersion(self):
|
|
|
|
'''
|
|
|
|
This method returns version of the program and the list of enabled features.
|
|
|
|
return: The method response is of type dict and contains following keys.
|
|
|
|
'''
|
|
|
|
return self.sendJsonRPC(data=self.getRPCBody('aria2.getSessionInfo'))
|
|
|
|
|
|
|
|
|
|
|
|
def getSessionInfo(self):
|
|
|
|
'''
|
|
|
|
This method returns session information.
|
|
|
|
return: The response is of type dict.
|
|
|
|
'''
|
|
|
|
return self.sendJsonRPC(data=self.getRPCBody('aria2.getSessionInfo'))
|
|
|
|
|
|
|
|
|
|
|
|
def shutdown(self):
|
|
|
|
'''
|
|
|
|
This method shutdowns aria2.
|
|
|
|
return: This method returns OK for success.
|
|
|
|
'''
|
|
|
|
return self.sendJsonRPC(self.server_uri, data=self.getRPCBody('aria2.shutdown'))
|
|
|
|
|
|
|
|
|
|
|
|
def forceShutdown(self):
|
|
|
|
'''
|
|
|
|
This method shutdowns aria2.
|
|
|
|
return: This method returns OK for success.
|
|
|
|
'''
|
|
|
|
return self.sendJsonRPC(self.server_uri, data=self.getRPCBody('aria2.forceShutdown'))
|
|
|
|
|
|
|
|
|
|
|
|
def isAria2Installed():
|
|
|
|
for cmdpath in os.environ['PATH'].split(':'):
|
|
|
|
if os.path.isdir(cmdpath) and 'aria2c' in os.listdir(cmdpath):
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
def isAria2rpcRunning():
|
|
|
|
pgrep_process = subprocess.Popen(
|
|
|
|
'pgrep -l aria2', shell=True, stdout=subprocess.PIPE)
|
|
|
|
if pgrep_process.stdout.readline() == b'':
|
|
|
|
return False
|
|
|
|
else:
|
|
|
|
return True
|
|
|
|
|
|
|
|
if __name__=='__main__':
|
|
|
|
p = PyAria2()
|
|
|
|
|
|
|
|
|