Skip to content

Commit

Permalink
Syno ds patch 1 (#1702)
Browse files Browse the repository at this point in the history
* Add Syno DS parsing #1671
as per https://forum.synology.com/enu/viewtopic.php?f=38&t=92856
* add config guidance
* add syno client
  • Loading branch information
clinton-hall committed Jan 13, 2020
1 parent 0827c5b commit b793ce7
Show file tree
Hide file tree
Showing 9 changed files with 487 additions and 2 deletions.
7 changes: 6 additions & 1 deletion autoProcessMedia.cfg.spec
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@
default_downloadDirectory =

[Torrent]
###### clientAgent - Supported clients: utorrent, transmission, deluge, rtorrent, vuze, qbittorrent, other
###### clientAgent - Supported clients: utorrent, transmission, deluge, rtorrent, vuze, qbittorrent, synods, other
clientAgent = other
###### useLink - Set to hard for physical links, sym for symbolic links, move to move, move-sym to move and link back, and no to not use links (copy)
useLink = hard
Expand Down Expand Up @@ -386,6 +386,11 @@
qBittorrentPort = 8080
qBittorrentUSR = your username
qBittorrentPWD = your password
###### Synology Download Station (You must edit this if you're using TorrentToMedia.py with Synology DS)
synoHost = localhost
synoPort = 9091
synoUSR = your username
synoPWD = your password
###### ADVANCED USE - ONLY EDIT IF YOU KNOW WHAT YOU'RE DOING ######
deleteOriginal = 0
chmodDirectory = 0
Expand Down
5 changes: 5 additions & 0 deletions core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,11 @@
TRANSMISSION_USER = None
TRANSMISSION_PASSWORD = None

SYNO_HOST = None
SYNO_PORT = None
SYNO_USER = None
SYNO_PASSWORD = None

DELUGE_HOST = None
DELUGE_PORT = None
DELUGE_USER = None
Expand Down
10 changes: 9 additions & 1 deletion core/plugins/downloaders/torrent/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

def configure_torrents(config):
torrent_config = config['Torrent']
core.TORRENT_CLIENT_AGENT = torrent_config['clientAgent'] # utorrent | deluge | transmission | rtorrent | vuze | qbittorrent |other
core.TORRENT_CLIENT_AGENT = torrent_config['clientAgent'] # utorrent | deluge | transmission | rtorrent | vuze | qbittorrent | synods | other
core.OUTPUT_DIRECTORY = torrent_config['outputDirectory'] # /abs/path/to/complete/
core.TORRENT_DEFAULT_DIRECTORY = torrent_config['default_downloadDirectory']

Expand All @@ -25,6 +25,7 @@ def configure_torrents(config):
configure_transmission(torrent_config)
configure_deluge(torrent_config)
configure_qbittorrent(torrent_config)
configure_syno(torrent_config)


def configure_torrent_linking(config):
Expand Down Expand Up @@ -69,6 +70,13 @@ def configure_transmission(config):
core.TRANSMISSION_PASSWORD = config['TransmissionPWD'] # mysecretpwr


def configure_syno(config):
core.SYNO_HOST = config['synoHost'] # localhost
core.SYNO_PORT = int(config['synoPort'])
core.SYNO_USER = config['synoUSR'] # mysecretusr
core.SYNO_PASSWORD = config['synoPWD'] # mysecretpwr


def configure_deluge(config):
core.DELUGE_HOST = config['DelugeHost'] # localhost
core.DELUGE_PORT = int(config['DelugePort']) # 8084
Expand Down
27 changes: 27 additions & 0 deletions core/plugins/downloaders/torrent/synology.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)

from syno.downloadstation import DownloadStation

import core
from core import logger


def configure_client():
agent = 'synology'
host = core.SYNO_HOST
port = core.SYNO_PORT
user = core.SYNO_USER
password = core.SYNO_PASSWORD

logger.debug('Connecting to {0}: http://{1}:{2}'.format(agent, host, port))
try:
client = DownloadStation(host, port, user, password)
except Exception:
logger.error('Failed to connect to synology')
else:
return client
8 changes: 8 additions & 0 deletions core/plugins/downloaders/torrent/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@
from .qbittorrent import configure_client as qbittorrent_client
from .transmission import configure_client as transmission_client
from .utorrent import configure_client as utorrent_client
from .synology import configure_client as synology_client

torrent_clients = {
'deluge': deluge_client,
'qbittorrent': qbittorrent_client,
'transmission': transmission_client,
'utorrent': utorrent_client,
'synods': synology_client,
}


Expand All @@ -39,6 +41,8 @@ def pause_torrent(client_agent, input_hash, input_id, input_name):
core.TORRENT_CLASS.stop(input_hash)
if client_agent == 'transmission' and core.TORRENT_CLASS != '':
core.TORRENT_CLASS.stop_torrent(input_id)
if client_agent == 'synods' and core.TORRENT_CLASS != '':
core.TORRENT_CLASS.pause_task(input_id)
if client_agent == 'deluge' and core.TORRENT_CLASS != '':
core.TORRENT_CLASS.core.pause_torrent([input_id])
if client_agent == 'qbittorrent' and core.TORRENT_CLASS != '':
Expand All @@ -57,6 +61,8 @@ def resume_torrent(client_agent, input_hash, input_id, input_name):
core.TORRENT_CLASS.start(input_hash)
if client_agent == 'transmission' and core.TORRENT_CLASS != '':
core.TORRENT_CLASS.start_torrent(input_id)
if client_agent == 'synods' and core.TORRENT_CLASS != '':
core.TORRENT_CLASS.resume_task(input_id)
if client_agent == 'deluge' and core.TORRENT_CLASS != '':
core.TORRENT_CLASS.core.resume_torrent([input_id])
if client_agent == 'qbittorrent' and core.TORRENT_CLASS != '':
Expand All @@ -75,6 +81,8 @@ def remove_torrent(client_agent, input_hash, input_id, input_name):
core.TORRENT_CLASS.remove(input_hash)
if client_agent == 'transmission' and core.TORRENT_CLASS != '':
core.TORRENT_CLASS.remove_torrent(input_id, True)
if client_agent == 'synods' and core.TORRENT_CLASS != '':
core.TORRENT_CLASS.delete_task(input_id)
if client_agent == 'deluge' and core.TORRENT_CLASS != '':
core.TORRENT_CLASS.core.remove_torrent(input_id, True)
if client_agent == 'qbittorrent' and core.TORRENT_CLASS != '':
Expand Down
32 changes: 32 additions & 0 deletions core/utils/parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import os

import core
from core import logger


def parse_other(args):
Expand Down Expand Up @@ -81,6 +82,36 @@ def parse_transmission(args):
return input_directory, input_name, input_category, input_hash, input_id


def parse_synods(args):
# Synology/Transmission usage: call TorrenToMedia.py (%TR_TORRENT_DIR% %TR_TORRENT_NAME% is passed on as environmental variables)
input_directory = ''
input_id = ''
input_category = ''
input_name = os.getenv('TR_TORRENT_NAME')
input_hash = os.getenv('TR_TORRENT_HASH')
if not input_name: # No info passed. Assume manual download.
return input_directory, input_name, input_category, input_hash, input_id
input_id = 'dbid_'.format(os.getenv('TR_TORRENT_ID'))
#res = core.TORRENT_CLASS.tasks_list(additional_param='detail')
res = core.TORRENT_CLASS.tasks_info(input_id, additional_param='detail')
logger.debug('result from syno {0}'.format(res))
if res['success']:
try:
tasks = res['data']['tasks']
task = [ task for task in tasks if task['id'] == input_id ][0]
input_id = task['id']
input_directory = task['additional']['detail']['destination']
except:
logger.error('unable to find download details in Synology DS')
#Syno paths appear to be relative. Let's test to see if the returned path exists, and if not append to /volume1/
if not os.path.isdir(input_directory):
for root in ['/volume1/', '/volume2/', '/volume3/', '/volume4/']:
if os.path.isdir(os.path.join(root, input_directory)):
input_directory = os.path.join(root, input_directory)
break
return input_directory, input_name, input_category, input_hash, input_id


def parse_vuze(args):
# vuze usage: C:\full\path\to\nzbToMedia\TorrentToMedia.py '%D%N%L%I%K%F'
try:
Expand Down Expand Up @@ -159,6 +190,7 @@ def parse_args(client_agent, args):
'transmission': parse_transmission,
'qbittorrent': parse_qbittorrent,
'vuze': parse_vuze,
'synods': parse_synods,
}

try:
Expand Down
Empty file added libs/custom/syno/__init__.py
Empty file.
124 changes: 124 additions & 0 deletions libs/custom/syno/auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import requests


class Authentication:
def __init__(self, ip_address, port, username, password):
self._ip_address = ip_address
self._port = port
self._username = username
self._password = password
self._sid = None
self._session_expire = True
self._base_url = 'http://%s:%s/webapi/' % (self._ip_address, self._port)

self.full_api_list = {}
self.app_api_list = {}

def login(self, application):
login_api = 'auth.cgi?api=SYNO.API.Auth'
param = {'version': '2', 'method': 'login', 'account': self._username,
'passwd': self._password, 'session': application, 'format': 'cookie'}

if not self._session_expire:
if self._sid is not None:
self._session_expire = False
return 'User already logged'
else:
session_request = requests.get(self._base_url + login_api, param)
self._sid = session_request.json()['data']['sid']
self._session_expire = False
return 'User logging... New session started!'

def logout(self, application):
logout_api = 'auth.cgi?api=SYNO.API.Auth'
param = {'version': '2', 'method': 'logout', 'session': application}

response = requests.get(self._base_url + logout_api, param)
if response.json()['success'] is True:
self._session_expire = True
self._sid = None
return 'Logged out'
else:
self._session_expire = True
self._sid = None
return 'No valid session is open'

def get_api_list(self, app=None):
query_path = 'query.cgi?api=SYNO.API.Info'
list_query = {'version': '1', 'method': 'query', 'query': 'all'}

response = requests.get(self._base_url + query_path, list_query).json()

if app is not None:
for key in response['data']:
if app.lower() in key.lower():
self.app_api_list[key] = response['data'][key]
else:
self.full_api_list = response['data']

return

def show_api_name_list(self):
prev_key = ''
for key in self.full_api_list:
if key != prev_key:
print(key)
prev_key = key
return

def show_json_response_type(self):
for key in self.full_api_list:
for sub_key in self.full_api_list[key]:
if sub_key == 'requestFormat':
if self.full_api_list[key]['requestFormat'] == 'JSON':
print(key + ' Returns JSON data')
return

def search_by_app(self, app):
print_check = 0
for key in self.full_api_list:
if app.lower() in key.lower():
print(key)
print_check += 1
continue
if print_check == 0:
print('Not Found')
return

def request_data(self, api_name, api_path, req_param, method=None, response_json=True): # 'post' or 'get'

# Convert all booleen in string in lowercase because Synology API is waiting for "true" or "false"
for k,v in req_param.items():
if isinstance(v, bool):
req_param[k] = str(v).lower()

if method is None:
method = 'get'

req_param['_sid'] = self._sid

if method is 'get':
url = ('%s%s' % (self._base_url, api_path)) + '?api=' + api_name
response = requests.get(url, req_param)

if response_json is True:
return response.json()
else:
return response

elif method is 'post':
url = ('%s%s' % (self._base_url, api_path)) + '?api=' + api_name
response = requests.post(url, req_param)

if response_json is True:
return response.json()
else:
return response

@property
def sid(self):
return self._sid

@property
def base_url(self):
return self._base_url
Loading

0 comments on commit b793ce7

Please sign in to comment.