mirror of
https://github.com/clinton-hall/nzbToMedia.git
synced 2025-08-19 21:03:14 -07:00
Medusa apiv2 (#1812)
* add fork Medusa-apiV2 * Added classes for sickbeard (base) and PyMedusa. * refactored part of the forks.py code -> InitSickBeard class. * Add .vscode to gitignore * Further refactor forks.py -> sickbeard.py * Working example for pyMedusa when fork is 'medusa' (no api key) * fix import for Py2 Co-authored-by: clinton-hall <fock_wulf@hotmail.com>
This commit is contained in:
parent
0acf78f196
commit
6a6b25fece
7 changed files with 492 additions and 256 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -13,3 +13,4 @@
|
||||||
/venv/
|
/venv/
|
||||||
*.dist-info
|
*.dist-info
|
||||||
*.egg-info
|
*.egg-info
|
||||||
|
/.vscode
|
||||||
|
|
|
@ -102,6 +102,7 @@ FORK_SICKCHILL_API = 'SickChill-api'
|
||||||
FORK_SICKBEARD_API = 'SickBeard-api'
|
FORK_SICKBEARD_API = 'SickBeard-api'
|
||||||
FORK_MEDUSA = 'Medusa'
|
FORK_MEDUSA = 'Medusa'
|
||||||
FORK_MEDUSA_API = 'Medusa-api'
|
FORK_MEDUSA_API = 'Medusa-api'
|
||||||
|
FORK_MEDUSA_APIV2 = 'Medusa-apiv2'
|
||||||
FORK_SICKGEAR = 'SickGear'
|
FORK_SICKGEAR = 'SickGear'
|
||||||
FORK_SICKGEAR_API = 'SickGear-api'
|
FORK_SICKGEAR_API = 'SickGear-api'
|
||||||
FORK_STHENO = 'Stheno'
|
FORK_STHENO = 'Stheno'
|
||||||
|
@ -115,6 +116,7 @@ FORKS = {
|
||||||
FORK_SICKBEARD_API: {'path': None, 'failed': None, 'process_method': None, 'force_replace': None, 'return_data': None, 'type': None, 'delete': None, 'force_next': None, 'cmd': 'postprocess'},
|
FORK_SICKBEARD_API: {'path': None, 'failed': None, 'process_method': None, 'force_replace': None, 'return_data': None, 'type': None, 'delete': None, 'force_next': None, 'cmd': 'postprocess'},
|
||||||
FORK_MEDUSA: {'proc_dir': None, 'failed': None, 'process_method': None, 'force': None, 'delete_on': None, 'ignore_subs': None},
|
FORK_MEDUSA: {'proc_dir': None, 'failed': None, 'process_method': None, 'force': None, 'delete_on': None, 'ignore_subs': None},
|
||||||
FORK_MEDUSA_API: {'path': None, 'failed': None, 'process_method': None, 'force_replace': None, 'return_data': None, 'type': None, 'delete_files': None, 'is_priority': None, 'cmd': 'postprocess'},
|
FORK_MEDUSA_API: {'path': None, 'failed': None, 'process_method': None, 'force_replace': None, 'return_data': None, 'type': None, 'delete_files': None, 'is_priority': None, 'cmd': 'postprocess'},
|
||||||
|
FORK_MEDUSA_APIV2: {'proc_dir': None, 'resource': None, 'failed': None, 'process_method': None, 'force': None, 'delete_on': None, 'is_priority': None},
|
||||||
FORK_SICKGEAR: {'dir': None, 'failed': None, 'process_method': None, 'force': None},
|
FORK_SICKGEAR: {'dir': None, 'failed': None, 'process_method': None, 'force': None},
|
||||||
FORK_SICKGEAR_API: {'path': None, 'process_method': None, 'force_replace': None, 'return_data': None, 'type': None, 'is priority': None, 'failed': None, 'cmd': 'sg.postprocess'},
|
FORK_SICKGEAR_API: {'path': None, 'process_method': None, 'force_replace': None, 'return_data': None, 'type': None, 'is priority': None, 'failed': None, 'cmd': 'sg.postprocess'},
|
||||||
FORK_STHENO: {'proc_dir': None, 'failed': None, 'process_method': None, 'force': None, 'delete_on': None, 'ignore_subs': None},
|
FORK_STHENO: {'proc_dir': None, 'failed': None, 'process_method': None, 'force': None, 'delete_on': None, 'ignore_subs': None},
|
||||||
|
|
0
core/auto_process/managers/__init__.py
Normal file
0
core/auto_process/managers/__init__.py
Normal file
38
core/auto_process/managers/pymedusa.py
Normal file
38
core/auto_process/managers/pymedusa.py
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from core import logger
|
||||||
|
|
||||||
|
from .sickbeard import SickBeard
|
||||||
|
|
||||||
|
|
||||||
|
class PyMedusa(SickBeard):
|
||||||
|
"""PyMedusa class."""
|
||||||
|
|
||||||
|
def __init__(self, sb_init):
|
||||||
|
super(PyMedusa, self).__init__(sb_init)
|
||||||
|
self.cfg = self.sb_init.config # in case we need something that's not already directly on self.sb_init.
|
||||||
|
|
||||||
|
def _configure():
|
||||||
|
"""Configure pymedusa with config options."""
|
||||||
|
|
||||||
|
def _create_url(self):
|
||||||
|
if self.sb_init.apikey:
|
||||||
|
return '{0}{1}:{2}{3}/api/{4}/'.format(self.sb_init.protocol, self.sb_init.host, self.sb_init.port, self.sb_init.web_root, self.sb_init.apikey)
|
||||||
|
return '{0}{1}:{2}{3}/home/postprocess/processEpisode'.format(self.sb_init.protocol, self.sb_init.host, self.sb_init.port, self.sb_init.web_root)
|
||||||
|
|
||||||
|
def api_call(self):
|
||||||
|
"""Perform the api call with PyMedusa."""
|
||||||
|
s = requests.Session()
|
||||||
|
|
||||||
|
self._process_fork_prarams()
|
||||||
|
url = self._create_url()
|
||||||
|
|
||||||
|
logger.debug('Opening URL: {0} with params: {1}'.format(url, self.sb_init.fork_params), self.sb_init.section)
|
||||||
|
if not self.sb_init.apikey and self.sb_init.username and self.sb_init.password:
|
||||||
|
login = '{0}{1}:{2}{3}/login'.format(self.sb_init.protocol, self.sb_init.host, self.sb_init.port, self.sb_init.web_root)
|
||||||
|
login_params = {'username': self.sb_init.username, 'password': self.sb_init.password}
|
||||||
|
r = s.get(login, verify=False, timeout=(30, 60))
|
||||||
|
if r.status_code in [401, 403] and r.cookies.get('_xsrf'):
|
||||||
|
login_params['_xsrf'] = r.cookies.get('_xsrf')
|
||||||
|
s.post(login, data=login_params, stream=True, verify=False, timeout=(30, 60))
|
||||||
|
return s.get(url, auth=(self.sb_init.username, self.sb_init.password), params=self.sb_init.fork_params, stream=True, verify=False, timeout=(30, 1800))
|
426
core/auto_process/managers/sickbeard.py
Normal file
426
core/auto_process/managers/sickbeard.py
Normal file
|
@ -0,0 +1,426 @@
|
||||||
|
# coding=utf-8
|
||||||
|
|
||||||
|
from __future__ import (
|
||||||
|
absolute_import,
|
||||||
|
division,
|
||||||
|
print_function,
|
||||||
|
unicode_literals,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
import copy
|
||||||
|
|
||||||
|
import core
|
||||||
|
from core import logger
|
||||||
|
from core.utils import (
|
||||||
|
convert_to_ascii,
|
||||||
|
flatten,
|
||||||
|
list_media_files,
|
||||||
|
remote_dir,
|
||||||
|
remove_dir,
|
||||||
|
server_responding,
|
||||||
|
)
|
||||||
|
|
||||||
|
from oauthlib.oauth2 import LegacyApplicationClient
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from requests_oauthlib import OAuth2Session
|
||||||
|
|
||||||
|
import six
|
||||||
|
from six import iteritems
|
||||||
|
|
||||||
|
|
||||||
|
class InitSickBeard(object):
|
||||||
|
"""Sickbeard init class.
|
||||||
|
|
||||||
|
Used to determin which sickbeard fork object to initialize.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, cfg, section, input_category):
|
||||||
|
# As a bonus let's also put the config on self.
|
||||||
|
self.config = cfg
|
||||||
|
self.section = section
|
||||||
|
self.input_category = input_category
|
||||||
|
|
||||||
|
self.host = cfg['host']
|
||||||
|
self.port = cfg['port']
|
||||||
|
self.ssl = int(cfg.get('ssl', 0))
|
||||||
|
self.web_root = cfg.get('web_root', '')
|
||||||
|
self.protocol = 'https://' if self.ssl else 'http://'
|
||||||
|
self.username = cfg.get('username', '')
|
||||||
|
self.password = cfg.get('password', '')
|
||||||
|
self.apikey = cfg.get('apikey', '')
|
||||||
|
self.api_version = int(cfg.get('api_version', 2))
|
||||||
|
self.sso_username = cfg.get('sso_username', '')
|
||||||
|
self.sso_password = cfg.get('sso_password', '')
|
||||||
|
|
||||||
|
self.fork = ''
|
||||||
|
self.fork_params = None
|
||||||
|
self.fork_obj = None
|
||||||
|
|
||||||
|
replace = {
|
||||||
|
'medusa': 'Medusa',
|
||||||
|
'medusa-api': 'Medusa-api',
|
||||||
|
'sickbeard-api': 'SickBeard-api',
|
||||||
|
'sickgear': 'SickGear',
|
||||||
|
'sickchill': 'SickChill',
|
||||||
|
'stheno': 'Stheno',
|
||||||
|
}
|
||||||
|
_val = cfg.get('fork', 'auto')
|
||||||
|
f1 = replace.get(_val, _val)
|
||||||
|
try:
|
||||||
|
self.fork = f1, core.FORKS[f1]
|
||||||
|
except KeyError:
|
||||||
|
self.fork = 'auto'
|
||||||
|
self.protocol = 'https://' if self.ssl else 'http://'
|
||||||
|
|
||||||
|
def auto_fork(self):
|
||||||
|
# auto-detect correct section
|
||||||
|
# config settings
|
||||||
|
if core.FORK_SET: # keep using determined fork for multiple (manual) post-processing
|
||||||
|
logger.info('{section}:{category} fork already set to {fork}'.format
|
||||||
|
(section=self.section, category=self.input_category, fork=core.FORK_SET[0]))
|
||||||
|
return core.FORK_SET[0], core.FORK_SET[1]
|
||||||
|
|
||||||
|
cfg = dict(core.CFG[self.section][self.input_category])
|
||||||
|
|
||||||
|
replace = {
|
||||||
|
'medusa': 'Medusa',
|
||||||
|
'medusa-api': 'Medusa-api',
|
||||||
|
'sickbeard-api': 'SickBeard-api',
|
||||||
|
'sickgear': 'SickGear',
|
||||||
|
'sickchill': 'SickChill',
|
||||||
|
'stheno': 'Stheno',
|
||||||
|
}
|
||||||
|
_val = cfg.get('fork', 'auto')
|
||||||
|
f1 = replace.get(_val, _val)
|
||||||
|
try:
|
||||||
|
fork = f1, core.FORKS[f1]
|
||||||
|
except KeyError:
|
||||||
|
fork = 'auto'
|
||||||
|
protocol = 'https://' if self.ssl else 'http://'
|
||||||
|
|
||||||
|
if self.section == 'NzbDrone':
|
||||||
|
logger.info('Attempting to verify {category} fork'.format
|
||||||
|
(category=self.input_category))
|
||||||
|
url = '{protocol}{host}:{port}{root}/api/rootfolder'.format(
|
||||||
|
protocol=protocol, host=self.host, port=self.port, root=self.web_root,
|
||||||
|
)
|
||||||
|
headers = {'X-Api-Key': self.apikey}
|
||||||
|
try:
|
||||||
|
r = requests.get(url, headers=headers, stream=True, verify=False)
|
||||||
|
except requests.ConnectionError:
|
||||||
|
logger.warning('Could not connect to {0}:{1} to verify fork!'.format(self.section, self.input_category))
|
||||||
|
|
||||||
|
if not r.ok:
|
||||||
|
logger.warning('Connection to {section}:{category} failed! '
|
||||||
|
'Check your configuration'.format
|
||||||
|
(section=self.section, category=self.input_category))
|
||||||
|
|
||||||
|
fork = ['default', {}]
|
||||||
|
|
||||||
|
elif self.section == 'SiCKRAGE':
|
||||||
|
logger.info('Attempting to verify {category} fork'.format
|
||||||
|
(category=self.input_category))
|
||||||
|
|
||||||
|
if self.api_version >= 2:
|
||||||
|
url = '{protocol}{host}:{port}{root}/api/v{api_version}/ping'.format(
|
||||||
|
protocol=protocol, host=self.host, port=self.port, root=self.web_root, api_version=self.api_version
|
||||||
|
)
|
||||||
|
api_params = {}
|
||||||
|
else:
|
||||||
|
url = '{protocol}{host}:{port}{root}/api/v{api_version}/{apikey}/'.format(
|
||||||
|
protocol=protocol, host=self.host, port=self.port, root=self.web_root, api_version=self.api_version, apikey=self.apikey,
|
||||||
|
)
|
||||||
|
api_params = {'cmd': 'postprocess', 'help': '1'}
|
||||||
|
|
||||||
|
try:
|
||||||
|
if self.api_version >= 2 and self.sso_username and self.sso_password:
|
||||||
|
oauth = OAuth2Session(client=LegacyApplicationClient(client_id=core.SICKRAGE_OAUTH_CLIENT_ID))
|
||||||
|
oauth_token = oauth.fetch_token(client_id=core.SICKRAGE_OAUTH_CLIENT_ID,
|
||||||
|
token_url=core.SICKRAGE_OAUTH_TOKEN_URL,
|
||||||
|
username=self.sso_username,
|
||||||
|
password=self.sso_password)
|
||||||
|
r = requests.get(url, headers={'Authorization': 'Bearer ' + oauth_token['access_token']}, stream=True, verify=False)
|
||||||
|
else:
|
||||||
|
r = requests.get(url, params=api_params, stream=True, verify=False)
|
||||||
|
|
||||||
|
if not r.ok:
|
||||||
|
logger.warning('Connection to {section}:{category} failed! '
|
||||||
|
'Check your configuration'.format(
|
||||||
|
section=self.section, category=self.input_category
|
||||||
|
))
|
||||||
|
except requests.ConnectionError:
|
||||||
|
logger.warning('Could not connect to {0}:{1} to verify API version!'.format(self.section, self.input_category))
|
||||||
|
|
||||||
|
params = {
|
||||||
|
'path': None,
|
||||||
|
'failed': None,
|
||||||
|
'process_method': None,
|
||||||
|
'force_replace': None,
|
||||||
|
'return_data': None,
|
||||||
|
'type': None,
|
||||||
|
'delete': None,
|
||||||
|
'force_next': None,
|
||||||
|
'is_priority': None
|
||||||
|
}
|
||||||
|
|
||||||
|
fork = ['default', params]
|
||||||
|
|
||||||
|
elif fork == 'auto':
|
||||||
|
fork = self.detect_fork()
|
||||||
|
|
||||||
|
logger.info('{section}:{category} fork set to {fork}'.format
|
||||||
|
(section=self.section, category=self.input_category, fork=fork[0]))
|
||||||
|
core.FORK_SET = fork
|
||||||
|
self.fork, self.fork_params = fork[0], fork[1]
|
||||||
|
# This will create the fork object, and attach to self.fork_obj.
|
||||||
|
self._init_fork()
|
||||||
|
return self.fork, self.fork_params
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _api_check(r, params, rem_params):
|
||||||
|
try:
|
||||||
|
json_data = r.json()
|
||||||
|
except ValueError:
|
||||||
|
logger.error('Failed to get JSON data from response')
|
||||||
|
logger.debug('Response received')
|
||||||
|
raise
|
||||||
|
|
||||||
|
try:
|
||||||
|
json_data = json_data['data']
|
||||||
|
except KeyError:
|
||||||
|
logger.error('Failed to get data from JSON')
|
||||||
|
logger.debug('Response received: {}'.format(json_data))
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
if six.PY3:
|
||||||
|
str_type = (str)
|
||||||
|
else:
|
||||||
|
str_type = (str, unicode)
|
||||||
|
if isinstance(json_data, str_type):
|
||||||
|
return rem_params, False
|
||||||
|
json_data = json_data.get('data', json_data)
|
||||||
|
|
||||||
|
try:
|
||||||
|
optional_parameters = json_data['optionalParameters'].keys()
|
||||||
|
# Find excess parameters
|
||||||
|
excess_parameters = set(params).difference(optional_parameters)
|
||||||
|
excess_parameters.remove('cmd') # Don't remove cmd from api params
|
||||||
|
logger.debug('Removing excess parameters: {}'.format(sorted(excess_parameters)))
|
||||||
|
rem_params.extend(excess_parameters)
|
||||||
|
return rem_params, True
|
||||||
|
except:
|
||||||
|
logger.error('Failed to identify optionalParameters')
|
||||||
|
return rem_params, False
|
||||||
|
|
||||||
|
def detect_fork(self):
|
||||||
|
"""Try to detect a specific fork."""
|
||||||
|
detected = False
|
||||||
|
params = core.ALL_FORKS
|
||||||
|
rem_params = []
|
||||||
|
logger.info('Attempting to auto-detect {category} fork'.format(category=self.input_category))
|
||||||
|
# define the order to test. Default must be first since the default fork doesn't reject parameters.
|
||||||
|
# then in order of most unique parameters.
|
||||||
|
|
||||||
|
if self.apikey:
|
||||||
|
url = '{protocol}{host}:{port}{root}/api/{apikey}/'.format(
|
||||||
|
protocol=self.protocol, host=self.host, port=self.port, root=self.web_root, apikey=self.apikey,
|
||||||
|
)
|
||||||
|
api_params = {'cmd': 'sg.postprocess', 'help': '1'}
|
||||||
|
else:
|
||||||
|
url = '{protocol}{host}:{port}{root}/home/postprocess/'.format(
|
||||||
|
protocol=self.protocol, host=self.host, port=self.port, root=self.web_root,
|
||||||
|
)
|
||||||
|
api_params = {}
|
||||||
|
|
||||||
|
# attempting to auto-detect fork
|
||||||
|
try:
|
||||||
|
s = requests.Session()
|
||||||
|
|
||||||
|
if not self.apikey and self.username and self.password:
|
||||||
|
login = '{protocol}{host}:{port}{root}/login'.format(
|
||||||
|
protocol=self.protocol, host=self.host, port=self.port, root=self.web_root)
|
||||||
|
login_params = {'username': self.username, 'password': self.password}
|
||||||
|
r = s.get(login, verify=False, timeout=(30, 60))
|
||||||
|
if r.status_code in [401, 403] and r.cookies.get('_xsrf'):
|
||||||
|
login_params['_xsrf'] = r.cookies.get('_xsrf')
|
||||||
|
s.post(login, data=login_params, stream=True, verify=False)
|
||||||
|
r = s.get(url, auth=(self.username, self.password), params=api_params, verify=False)
|
||||||
|
except requests.ConnectionError:
|
||||||
|
logger.info('Could not connect to {section}:{category} to perform auto-fork detection!'.format
|
||||||
|
(section=self.section, category=self.input_category))
|
||||||
|
r = []
|
||||||
|
|
||||||
|
if r and r.ok:
|
||||||
|
if self.apikey:
|
||||||
|
rem_params, found = self._api_check(r, params, rem_params)
|
||||||
|
if found:
|
||||||
|
params['cmd'] = 'sg.postprocess'
|
||||||
|
else: # try different api set for non-SickGear forks.
|
||||||
|
api_params = {'cmd': 'help', 'subject': 'postprocess'}
|
||||||
|
try:
|
||||||
|
if not self.apikey and self.username and self.password:
|
||||||
|
r = s.get(url, auth=(self.username, self.password), params=api_params, verify=False)
|
||||||
|
else:
|
||||||
|
r = s.get(url, params=api_params, verify=False)
|
||||||
|
except requests.ConnectionError:
|
||||||
|
logger.info('Could not connect to {section}:{category} to perform auto-fork detection!'.format
|
||||||
|
(section=self.section, category=self.input_category))
|
||||||
|
rem_params, found = self._api_check(r, params, rem_params)
|
||||||
|
params['cmd'] = 'postprocess'
|
||||||
|
else:
|
||||||
|
# Find excess parameters
|
||||||
|
rem_params.extend(
|
||||||
|
param
|
||||||
|
for param in params
|
||||||
|
if 'name="{param}"'.format(param=param) not in r.text
|
||||||
|
)
|
||||||
|
|
||||||
|
# Remove excess params
|
||||||
|
for param in rem_params:
|
||||||
|
params.pop(param)
|
||||||
|
|
||||||
|
for fork in sorted(iteritems(core.FORKS), reverse=False):
|
||||||
|
if params == fork[1]:
|
||||||
|
detected = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if detected:
|
||||||
|
self.fork = fork
|
||||||
|
logger.info('{section}:{category} fork auto-detection successful ...'.format
|
||||||
|
(section=self.section, category=self.input_category))
|
||||||
|
elif rem_params:
|
||||||
|
logger.info('{section}:{category} fork auto-detection found custom params {params}'.format
|
||||||
|
(section=self.section, category=self.input_category, params=params))
|
||||||
|
self.fork = ['custom', params]
|
||||||
|
else:
|
||||||
|
logger.info('{section}:{category} fork auto-detection failed'.format
|
||||||
|
(section=self.section, category=self.input_category))
|
||||||
|
self.fork = list(core.FORKS.items())[list(core.FORKS.keys()).index(core.FORK_DEFAULT)]
|
||||||
|
|
||||||
|
return fork
|
||||||
|
|
||||||
|
def _init_fork(self):
|
||||||
|
from .pymedusa import PyMedusa
|
||||||
|
mapped_forks = {
|
||||||
|
'Medusa': PyMedusa
|
||||||
|
}
|
||||||
|
logger.debug('Create object for fork {fork}'.format(fork=self.fork))
|
||||||
|
if self.fork and mapped_forks.get(self.fork):
|
||||||
|
# Create the fork object and pass self (SickBeardInit) to it for all the data, like Config.
|
||||||
|
self.fork = mapped_forks[self.fork](self)
|
||||||
|
else:
|
||||||
|
logger.info('{section}:{category} Could not create a fork object for {fork}. Probaly class not added yet.'.format(
|
||||||
|
section=self.section, category=self.input_category, fork=self.fork)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SickBeard(object):
|
||||||
|
"""Sickbeard base class."""
|
||||||
|
|
||||||
|
def __init__(self, sb_init):
|
||||||
|
"""SB constructor."""
|
||||||
|
self.sb_init = sb_init
|
||||||
|
self.failed = None
|
||||||
|
self.status = None
|
||||||
|
self.input_name = None
|
||||||
|
self.dir_name = None
|
||||||
|
|
||||||
|
self.delete_failed = int(self.sb_init.config.get('delete_failed', 0))
|
||||||
|
self.nzb_extraction_by = self.sb_init.config.get('nzbExtractionBy', 'Downloader')
|
||||||
|
self.process_method = self.sb_init.config.get('process_method')
|
||||||
|
self.remote_path = int(self.sb_init.config.get('remote_path', 0))
|
||||||
|
self.wait_for = int(self.sb_init.config.get('wait_for', 2))
|
||||||
|
self.force = int(self.sb_init.config.get('force', 0))
|
||||||
|
self.delete_on = int(self.sb_init.config.get('delete_on', 0))
|
||||||
|
self.ignore_subs = int(self.sb_init.config.get('ignore_subs', 0))
|
||||||
|
|
||||||
|
# get importmode, default to 'Move' for consistency with legacy
|
||||||
|
self.import_mode = self.sb_init.config.get('importMode', 'Move')
|
||||||
|
|
||||||
|
def initialize(self, dir_name, input_name=None, failed=False, client_agent='manual'):
|
||||||
|
"""We need to call this explicitely because we need some variables.
|
||||||
|
|
||||||
|
We can't pass these directly through the constructor.
|
||||||
|
"""
|
||||||
|
self.dir_name = dir_name
|
||||||
|
self.input_name = input_name
|
||||||
|
self.failed = failed
|
||||||
|
self.status = int(self.failed)
|
||||||
|
if self.status > 0 and core.NOEXTRACTFAILED:
|
||||||
|
self.extract = 0
|
||||||
|
else:
|
||||||
|
self.extract = int(self.sb_init.config.get('extract', 0))
|
||||||
|
if client_agent == core.TORRENT_CLIENT_AGENT and core.USE_LINK == 'move-sym':
|
||||||
|
self.process_method = 'symlink'
|
||||||
|
|
||||||
|
def _create_url(self):
|
||||||
|
if self.sb_init.apikey:
|
||||||
|
return '{0}{1}:{2}{3}/api/{4}/'.format(self.sb_init.protocol, self.sb_init.host, self.sb_init.port, self.sb_init.web_root, self.sb_init.apikey)
|
||||||
|
return '{0}{1}:{2}{3}/home/postprocess/processEpisode'.format(self.sb_init.protocol, self.sb_init.host, self.sb_init.port, self.sb_init.web_root)
|
||||||
|
|
||||||
|
def _process_fork_prarams(self):
|
||||||
|
# configure SB params to pass
|
||||||
|
fork_params = self.sb_init.fork_params
|
||||||
|
fork_params['quiet'] = 1
|
||||||
|
fork_params['proc_type'] = 'manual'
|
||||||
|
if self.input_name is not None:
|
||||||
|
fork_params['nzbName'] = self.input_name
|
||||||
|
|
||||||
|
for param in copy.copy(fork_params):
|
||||||
|
if param == 'failed':
|
||||||
|
if self.failed > 1:
|
||||||
|
self.failed = 1
|
||||||
|
fork_params[param] = self.failed
|
||||||
|
if 'proc_type' in fork_params:
|
||||||
|
del fork_params['proc_type']
|
||||||
|
if 'type' in fork_params:
|
||||||
|
del fork_params['type']
|
||||||
|
|
||||||
|
if param == 'return_data':
|
||||||
|
fork_params[param] = 0
|
||||||
|
if 'quiet' in fork_params:
|
||||||
|
del fork_params['quiet']
|
||||||
|
|
||||||
|
if param == 'type':
|
||||||
|
if 'type' in fork_params: # only set if we haven't already deleted for 'failed' above.
|
||||||
|
fork_params[param] = 'manual'
|
||||||
|
if 'proc_type' in fork_params:
|
||||||
|
del fork_params['proc_type']
|
||||||
|
|
||||||
|
if param in ['dir_name', 'dir', 'proc_dir', 'process_directory', 'path']:
|
||||||
|
fork_params[param] = self.dir_name
|
||||||
|
if self.remote_path:
|
||||||
|
fork_params[param] = remote_dir(self.dir_name)
|
||||||
|
|
||||||
|
if param == 'process_method':
|
||||||
|
if self.process_method:
|
||||||
|
fork_params[param] = self.process_method
|
||||||
|
else:
|
||||||
|
del fork_params[param]
|
||||||
|
|
||||||
|
if param in ['force', 'force_replace']:
|
||||||
|
if self.force:
|
||||||
|
fork_params[param] = self.force
|
||||||
|
else:
|
||||||
|
del fork_params[param]
|
||||||
|
|
||||||
|
if param in ['delete_on', 'delete']:
|
||||||
|
if self.delete_on:
|
||||||
|
fork_params[param] = self.delete_on
|
||||||
|
else:
|
||||||
|
del fork_params[param]
|
||||||
|
|
||||||
|
if param == 'ignore_subs':
|
||||||
|
if self.ignore_subs:
|
||||||
|
fork_params[param] = self.ignore_subs
|
||||||
|
else:
|
||||||
|
del fork_params[param]
|
||||||
|
|
||||||
|
if param == 'force_next':
|
||||||
|
fork_params[param] = 1
|
||||||
|
|
||||||
|
# delete any unused params so we don't pass them to SB by mistake
|
||||||
|
[fork_params.pop(k) for k, v in list(fork_params.items()) if v is None]
|
|
@ -24,7 +24,7 @@ from core.auto_process.common import (
|
||||||
command_complete,
|
command_complete,
|
||||||
completed_download_handling,
|
completed_download_handling,
|
||||||
)
|
)
|
||||||
from core.forks import auto_fork
|
from core.auto_process.managers.sickbeard import InitSickBeard
|
||||||
from core.plugins.downloaders.nzb.utils import report_nzb
|
from core.plugins.downloaders.nzb.utils import report_nzb
|
||||||
from core.plugins.subtitles import import_subs
|
from core.plugins.subtitles import import_subs
|
||||||
from core.scene_exceptions import process_all_exceptions
|
from core.scene_exceptions import process_all_exceptions
|
||||||
|
@ -37,6 +37,7 @@ from core.utils import (
|
||||||
server_responding,
|
server_responding,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
requests.packages.urllib3.disable_warnings()
|
requests.packages.urllib3.disable_warnings()
|
||||||
|
|
||||||
|
|
||||||
|
@ -55,9 +56,15 @@ def process(section, dir_name, input_name=None, failed=False, client_agent='manu
|
||||||
sso_username = cfg.get('sso_username', '')
|
sso_username = cfg.get('sso_username', '')
|
||||||
sso_password = cfg.get('sso_password', '')
|
sso_password = cfg.get('sso_password', '')
|
||||||
|
|
||||||
|
# Refactor into an OO structure.
|
||||||
|
# For now let's do botch the OO and the serialized code, until everything has been migrated.
|
||||||
|
init_sickbeard = InitSickBeard(cfg, section, input_category)
|
||||||
|
|
||||||
if server_responding('{0}{1}:{2}{3}'.format(protocol, host, port, web_root)):
|
if server_responding('{0}{1}:{2}{3}'.format(protocol, host, port, web_root)):
|
||||||
# auto-detect correct fork
|
# auto-detect correct fork
|
||||||
fork, fork_params = auto_fork(section, input_category)
|
# During reactor we also return fork, fork_params. But these are also stored in the object.
|
||||||
|
# Should be changed after refactor.
|
||||||
|
fork, fork_params = init_sickbeard.auto_fork()
|
||||||
elif not username and not apikey and not sso_username:
|
elif not username and not apikey and not sso_username:
|
||||||
logger.info('No SickBeard / SiCKRAGE username or Sonarr apikey entered. Performing transcoder functions only')
|
logger.info('No SickBeard / SiCKRAGE username or Sonarr apikey entered. Performing transcoder functions only')
|
||||||
fork, fork_params = 'None', {}
|
fork, fork_params = 'None', {}
|
||||||
|
@ -184,6 +191,9 @@ def process(section, dir_name, input_name=None, failed=False, client_agent='manu
|
||||||
status_code=1,
|
status_code=1,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Part of the refactor
|
||||||
|
init_sickbeard.fork.initialize(dir_name, input_name, failed, client_agent='manual')
|
||||||
|
|
||||||
# configure SB params to pass
|
# configure SB params to pass
|
||||||
fork_params['quiet'] = 1
|
fork_params['quiet'] = 1
|
||||||
fork_params['proc_type'] = 'manual'
|
fork_params['proc_type'] = 'manual'
|
||||||
|
@ -311,17 +321,20 @@ def process(section, dir_name, input_name=None, failed=False, client_agent='manu
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if section == 'SickBeard':
|
if section == 'SickBeard':
|
||||||
s = requests.Session()
|
if init_sickbeard.fork:
|
||||||
|
r = init_sickbeard.fork.api_call()
|
||||||
|
else:
|
||||||
|
s = requests.Session()
|
||||||
|
|
||||||
logger.debug('Opening URL: {0} with params: {1}'.format(url, fork_params), section)
|
logger.debug('Opening URL: {0} with params: {1}'.format(url, fork_params), section)
|
||||||
if not apikey and username and password:
|
if not apikey and username and password:
|
||||||
login = '{0}{1}:{2}{3}/login'.format(protocol, host, port, web_root)
|
login = '{0}{1}:{2}{3}/login'.format(protocol, host, port, web_root)
|
||||||
login_params = {'username': username, 'password': password}
|
login_params = {'username': username, 'password': password}
|
||||||
r = s.get(login, verify=False, timeout=(30, 60))
|
r = s.get(login, verify=False, timeout=(30, 60))
|
||||||
if r.status_code in [401, 403] and r.cookies.get('_xsrf'):
|
if r.status_code in [401, 403] and r.cookies.get('_xsrf'):
|
||||||
login_params['_xsrf'] = r.cookies.get('_xsrf')
|
login_params['_xsrf'] = r.cookies.get('_xsrf')
|
||||||
s.post(login, data=login_params, stream=True, verify=False, timeout=(30, 60))
|
s.post(login, data=login_params, stream=True, verify=False, timeout=(30, 60))
|
||||||
r = s.get(url, auth=(username, password), params=fork_params, stream=True, verify=False, timeout=(30, 1800))
|
r = s.get(url, auth=(username, password), params=fork_params, stream=True, verify=False, timeout=(30, 1800))
|
||||||
elif section == 'SiCKRAGE':
|
elif section == 'SiCKRAGE':
|
||||||
s = requests.Session()
|
s = requests.Session()
|
||||||
|
|
||||||
|
|
244
core/forks.py
244
core/forks.py
|
@ -1,244 +0,0 @@
|
||||||
# coding=utf-8
|
|
||||||
|
|
||||||
from __future__ import (
|
|
||||||
absolute_import,
|
|
||||||
division,
|
|
||||||
print_function,
|
|
||||||
unicode_literals,
|
|
||||||
)
|
|
||||||
|
|
||||||
import requests
|
|
||||||
import six
|
|
||||||
from oauthlib.oauth2 import LegacyApplicationClient
|
|
||||||
from requests_oauthlib import OAuth2Session
|
|
||||||
from six import iteritems
|
|
||||||
|
|
||||||
import core
|
|
||||||
from core import logger
|
|
||||||
|
|
||||||
|
|
||||||
def api_check(r, params, rem_params):
|
|
||||||
try:
|
|
||||||
json_data = r.json()
|
|
||||||
except ValueError:
|
|
||||||
logger.error('Failed to get JSON data from response')
|
|
||||||
logger.debug('Response received')
|
|
||||||
raise
|
|
||||||
|
|
||||||
try:
|
|
||||||
json_data = json_data['data']
|
|
||||||
except KeyError:
|
|
||||||
logger.error('Failed to get data from JSON')
|
|
||||||
logger.debug('Response received: {}'.format(json_data))
|
|
||||||
raise
|
|
||||||
else:
|
|
||||||
if six.PY3:
|
|
||||||
str_type = (str)
|
|
||||||
else:
|
|
||||||
str_type = (str, unicode)
|
|
||||||
if isinstance(json_data, str_type):
|
|
||||||
return rem_params, False
|
|
||||||
json_data = json_data.get('data', json_data)
|
|
||||||
|
|
||||||
try:
|
|
||||||
optional_parameters = json_data['optionalParameters'].keys()
|
|
||||||
# Find excess parameters
|
|
||||||
excess_parameters = set(params).difference(optional_parameters)
|
|
||||||
excess_parameters.remove('cmd') # Don't remove cmd from api params
|
|
||||||
logger.debug('Removing excess parameters: {}'.format(sorted(excess_parameters)))
|
|
||||||
rem_params.extend(excess_parameters)
|
|
||||||
return rem_params, True
|
|
||||||
except:
|
|
||||||
logger.error('Failed to identify optionalParameters')
|
|
||||||
return rem_params, False
|
|
||||||
|
|
||||||
|
|
||||||
def auto_fork(section, input_category):
|
|
||||||
# auto-detect correct section
|
|
||||||
# config settings
|
|
||||||
if core.FORK_SET: # keep using determined fork for multiple (manual) post-processing
|
|
||||||
logger.info('{section}:{category} fork already set to {fork}'.format
|
|
||||||
(section=section, category=input_category, fork=core.FORK_SET[0]))
|
|
||||||
return core.FORK_SET[0], core.FORK_SET[1]
|
|
||||||
|
|
||||||
cfg = dict(core.CFG[section][input_category])
|
|
||||||
|
|
||||||
host = cfg.get('host')
|
|
||||||
port = cfg.get('port')
|
|
||||||
username = cfg.get('username', '')
|
|
||||||
password = cfg.get('password', '')
|
|
||||||
sso_username = cfg.get('sso_username', '')
|
|
||||||
sso_password = cfg.get('sso_password', '')
|
|
||||||
apikey = cfg.get('apikey', '')
|
|
||||||
api_version = int(cfg.get('api_version', 2))
|
|
||||||
ssl = int(cfg.get('ssl', 0))
|
|
||||||
web_root = cfg.get('web_root', '')
|
|
||||||
replace = {
|
|
||||||
'medusa': 'Medusa',
|
|
||||||
'medusa-api': 'Medusa-api',
|
|
||||||
'sickbeard-api': 'SickBeard-api',
|
|
||||||
'sickgear': 'SickGear',
|
|
||||||
'sickchill': 'SickChill',
|
|
||||||
'stheno': 'Stheno',
|
|
||||||
}
|
|
||||||
_val = cfg.get('fork', 'auto')
|
|
||||||
f1 = replace.get(_val, _val)
|
|
||||||
try:
|
|
||||||
fork = f1, core.FORKS[f1]
|
|
||||||
except KeyError:
|
|
||||||
fork = 'auto'
|
|
||||||
protocol = 'https://' if ssl else 'http://'
|
|
||||||
|
|
||||||
detected = False
|
|
||||||
if section == 'NzbDrone':
|
|
||||||
logger.info('Attempting to verify {category} fork'.format
|
|
||||||
(category=input_category))
|
|
||||||
url = '{protocol}{host}:{port}{root}/api/rootfolder'.format(
|
|
||||||
protocol=protocol, host=host, port=port, root=web_root,
|
|
||||||
)
|
|
||||||
headers = {'X-Api-Key': apikey}
|
|
||||||
try:
|
|
||||||
r = requests.get(url, headers=headers, stream=True, verify=False)
|
|
||||||
except requests.ConnectionError:
|
|
||||||
logger.warning('Could not connect to {0}:{1} to verify fork!'.format(section, input_category))
|
|
||||||
|
|
||||||
if not r.ok:
|
|
||||||
logger.warning('Connection to {section}:{category} failed! '
|
|
||||||
'Check your configuration'.format
|
|
||||||
(section=section, category=input_category))
|
|
||||||
|
|
||||||
fork = ['default', {}]
|
|
||||||
|
|
||||||
elif section == 'SiCKRAGE':
|
|
||||||
logger.info('Attempting to verify {category} fork'.format
|
|
||||||
(category=input_category))
|
|
||||||
|
|
||||||
if api_version >= 2:
|
|
||||||
url = '{protocol}{host}:{port}{root}/api/v{api_version}/ping'.format(
|
|
||||||
protocol=protocol, host=host, port=port, root=web_root, api_version=api_version
|
|
||||||
)
|
|
||||||
api_params = {}
|
|
||||||
else:
|
|
||||||
url = '{protocol}{host}:{port}{root}/api/v{api_version}/{apikey}/'.format(
|
|
||||||
protocol=protocol, host=host, port=port, root=web_root, api_version=api_version, apikey=apikey,
|
|
||||||
)
|
|
||||||
api_params = {'cmd': 'postprocess', 'help': '1'}
|
|
||||||
|
|
||||||
try:
|
|
||||||
if api_version >= 2 and sso_username and sso_password:
|
|
||||||
oauth = OAuth2Session(client=LegacyApplicationClient(client_id=core.SICKRAGE_OAUTH_CLIENT_ID))
|
|
||||||
oauth_token = oauth.fetch_token(client_id=core.SICKRAGE_OAUTH_CLIENT_ID,
|
|
||||||
token_url=core.SICKRAGE_OAUTH_TOKEN_URL,
|
|
||||||
username=sso_username,
|
|
||||||
password=sso_password)
|
|
||||||
r = requests.get(url, headers={'Authorization': 'Bearer ' + oauth_token['access_token']}, stream=True, verify=False)
|
|
||||||
else:
|
|
||||||
r = requests.get(url, params=api_params, stream=True, verify=False)
|
|
||||||
|
|
||||||
if not r.ok:
|
|
||||||
logger.warning('Connection to {section}:{category} failed! '
|
|
||||||
'Check your configuration'.format
|
|
||||||
(section=section, category=input_category))
|
|
||||||
except requests.ConnectionError:
|
|
||||||
logger.warning('Could not connect to {0}:{1} to verify API version!'.format(section, input_category))
|
|
||||||
|
|
||||||
params = {
|
|
||||||
'path': None,
|
|
||||||
'failed': None,
|
|
||||||
'process_method': None,
|
|
||||||
'force_replace': None,
|
|
||||||
'return_data': None,
|
|
||||||
'type': None,
|
|
||||||
'delete': None,
|
|
||||||
'force_next': None,
|
|
||||||
'is_priority': None
|
|
||||||
}
|
|
||||||
|
|
||||||
fork = ['default', params]
|
|
||||||
|
|
||||||
elif fork == 'auto':
|
|
||||||
params = core.ALL_FORKS
|
|
||||||
rem_params = []
|
|
||||||
logger.info('Attempting to auto-detect {category} fork'.format(category=input_category))
|
|
||||||
# define the order to test. Default must be first since the default fork doesn't reject parameters.
|
|
||||||
# then in order of most unique parameters.
|
|
||||||
|
|
||||||
if apikey:
|
|
||||||
url = '{protocol}{host}:{port}{root}/api/{apikey}/'.format(
|
|
||||||
protocol=protocol, host=host, port=port, root=web_root, apikey=apikey,
|
|
||||||
)
|
|
||||||
api_params = {'cmd': 'sg.postprocess', 'help': '1'}
|
|
||||||
else:
|
|
||||||
url = '{protocol}{host}:{port}{root}/home/postprocess/'.format(
|
|
||||||
protocol=protocol, host=host, port=port, root=web_root,
|
|
||||||
)
|
|
||||||
api_params = {}
|
|
||||||
|
|
||||||
# attempting to auto-detect fork
|
|
||||||
try:
|
|
||||||
s = requests.Session()
|
|
||||||
|
|
||||||
if not apikey and username and password:
|
|
||||||
login = '{protocol}{host}:{port}{root}/login'.format(
|
|
||||||
protocol=protocol, host=host, port=port, root=web_root)
|
|
||||||
login_params = {'username': username, 'password': password}
|
|
||||||
r = s.get(login, verify=False, timeout=(30, 60))
|
|
||||||
if r.status_code in [401, 403] and r.cookies.get('_xsrf'):
|
|
||||||
login_params['_xsrf'] = r.cookies.get('_xsrf')
|
|
||||||
s.post(login, data=login_params, stream=True, verify=False)
|
|
||||||
r = s.get(url, auth=(username, password), params=api_params, verify=False)
|
|
||||||
except requests.ConnectionError:
|
|
||||||
logger.info('Could not connect to {section}:{category} to perform auto-fork detection!'.format
|
|
||||||
(section=section, category=input_category))
|
|
||||||
r = []
|
|
||||||
|
|
||||||
if r and r.ok:
|
|
||||||
if apikey:
|
|
||||||
rem_params, found = api_check(r, params, rem_params)
|
|
||||||
if found:
|
|
||||||
params['cmd'] = 'sg.postprocess'
|
|
||||||
else: # try different api set for non-SickGear forks.
|
|
||||||
api_params = {'cmd': 'help', 'subject': 'postprocess'}
|
|
||||||
try:
|
|
||||||
if not apikey and username and password:
|
|
||||||
r = s.get(url, auth=(username, password), params=api_params, verify=False)
|
|
||||||
else:
|
|
||||||
r = s.get(url, params=api_params, verify=False)
|
|
||||||
except requests.ConnectionError:
|
|
||||||
logger.info('Could not connect to {section}:{category} to perform auto-fork detection!'.format
|
|
||||||
(section=section, category=input_category))
|
|
||||||
rem_params, found = api_check(r, params, rem_params)
|
|
||||||
params['cmd'] = 'postprocess'
|
|
||||||
else:
|
|
||||||
# Find excess parameters
|
|
||||||
rem_params.extend(
|
|
||||||
param
|
|
||||||
for param in params
|
|
||||||
if 'name="{param}"'.format(param=param) not in r.text
|
|
||||||
)
|
|
||||||
|
|
||||||
# Remove excess params
|
|
||||||
for param in rem_params:
|
|
||||||
params.pop(param)
|
|
||||||
|
|
||||||
for fork in sorted(iteritems(core.FORKS), reverse=False):
|
|
||||||
if params == fork[1]:
|
|
||||||
detected = True
|
|
||||||
break
|
|
||||||
|
|
||||||
if detected:
|
|
||||||
logger.info('{section}:{category} fork auto-detection successful ...'.format
|
|
||||||
(section=section, category=input_category))
|
|
||||||
elif rem_params:
|
|
||||||
logger.info('{section}:{category} fork auto-detection found custom params {params}'.format
|
|
||||||
(section=section, category=input_category, params=params))
|
|
||||||
fork = ['custom', params]
|
|
||||||
else:
|
|
||||||
logger.info('{section}:{category} fork auto-detection failed'.format
|
|
||||||
(section=section, category=input_category))
|
|
||||||
fork = list(core.FORKS.items())[list(core.FORKS.keys()).index(core.FORK_DEFAULT)]
|
|
||||||
|
|
||||||
logger.info('{section}:{category} fork set to {fork}'.format
|
|
||||||
(section=section, category=input_category, fork=fork[0]))
|
|
||||||
core.FORK_SET = fork
|
|
||||||
return fork[0], fork[1]
|
|
Loading…
Add table
Add a link
Reference in a new issue