Commit 5c03e671 authored by echel0n's avatar echel0n

Refactored backend API calls to instance attached to app instance.

parent 62c16e09
......@@ -44,7 +44,6 @@ from tornado.ioloop import IOLoop
import sickrage
from sickrage.core.announcements import Announcements
from sickrage.core.api import API
from sickrage.core.api.account import AccountAPI
from sickrage.core.caches.name_cache import NameCache
from sickrage.core.caches.quicksearch_cache import QuicksearchCache
from sickrage.core.common import SD, SKIPPED, WANTED
......@@ -169,6 +168,7 @@ class Core(object):
self.oidc_client = None
self.quicksearch_cache = None
self.announcements = None
self.api = None
def start(self):
self.started = True
......@@ -206,6 +206,7 @@ class Core(object):
self.upnp_client = UPNPClient()
self.quicksearch_cache = QuicksearchCache()
self.announcements = Announcements()
self.api = API()
# setup oidc client
realm = KeycloakRealm(server_url='https://auth.sickrage.ca', realm_name='sickrage')
......
......@@ -24,7 +24,6 @@ from sqlalchemy import orm
import sickrage
from sickrage.core.api import APIError
from sickrage.core.api.announcements import AnnouncementsAPI
from sickrage.core.databases.cache import CacheDB
......@@ -73,7 +72,7 @@ class Announcements(object):
threading.currentThread().setName(self.name)
try:
resp = AnnouncementsAPI().get_announcements()
resp = sickrage.app.api.get_announcements()
if resp and 'data' in resp:
for announcement in resp['data']:
if announcement['enabled']:
......
import requests
import time
from urllib.parse import urljoin
import requests
import requests.exceptions
from keycloak.exceptions import KeycloakClientError
from oauthlib.oauth2 import MissingTokenError, InvalidClientIdError, TokenExpiredError, InvalidGrantError, OAuth2Token
......@@ -9,7 +9,7 @@ from requests_oauthlib import OAuth2Session
from sqlalchemy import orm
import sickrage
from sickrage.core.api.exceptions import APIError, APITokenExpired
from sickrage.core.api.exceptions import APIError
from sickrage.core.databases.cache import CacheDB
......@@ -21,6 +21,16 @@ class API(object):
self.client_id = sickrage.app.oidc_client_id
self.client_secret = sickrage.app.oidc_client_secret
self.imdb = self.IMDbAPI(self)
self.account = self.AccountAPI(self)
self.provider = self.ProviderAPI(self)
self.google = self.GoogleDriveAPI(self)
self.announcement = self.AnnouncementsAPI(self)
self.torrent_cache = self.TorrentCacheAPI(self)
self.provider_cache = self.ProviderCacheAPI(self)
self._session = None
@property
def session(self):
extra = {
......@@ -31,10 +41,10 @@ class API(object):
def token_updater(value):
self.token = value
return OAuth2Session(token=self.token,
auto_refresh_kwargs=extra,
auto_refresh_url=self.token_url,
token_updater=token_updater)
if not self._session:
self._session = OAuth2Session(token=self.token, auto_refresh_kwargs=extra, auto_refresh_url=self.token_url, token_updater=token_updater)
return self._session
@property
@CacheDB.with_session
......@@ -80,7 +90,7 @@ class API(object):
@property
def userinfo(self):
return self._request('GET', 'userinfo')
return self.request('GET', 'userinfo')
def logout(self):
sickrage.app.oidc_client.logout(self.token.get('refresh_token'))
......@@ -98,15 +108,15 @@ class API(object):
self.token = sickrage.app.oidc_client.token_exchange(**exchange)
def allowed_usernames(self):
return self._request('GET', 'allowed-usernames')
return self.request('GET', 'allowed-usernames')
def download_privatekey(self):
return self._request('GET', 'account/private-key')
return self.request('GET', 'account/private-key')
def upload_privatekey(self, privatekey):
return self._request('POST', 'account/private-key', data=dict({'privatekey': privatekey}))
return self.request('POST', 'account/private-key', data=dict({'privatekey': privatekey}))
def _request(self, method, url, timeout=30, **kwargs):
def request(self, method, url, timeout=30, **kwargs):
latest_exception = None
for i in range(3):
......@@ -115,7 +125,8 @@ class API(object):
latest_exception = "SiCKRAGE backend API is currently unreachable ..."
continue
resp = self.session.request(method, urljoin(self.api_base, "/".join([self.api_version, url])), timeout=timeout, hooks={'response': self.throttle_hook}, **kwargs)
resp = self.session.request(method, urljoin(self.api_base, "/".join([self.api_version, url])), timeout=timeout,
hooks={'response': self.throttle_hook}, **kwargs)
resp.raise_for_status()
if resp.status_code == 204:
return
......@@ -154,3 +165,117 @@ class API(object):
if remaining == 1:
sickrage.app.log.debug("Throttling SiCKRAGE API Calls... Sleeping for 60 secs...\n")
time.sleep(60)
class AccountAPI:
def __init__(self, api):
self.api = api
def register_app_id(self):
return self.api.request('GET', 'account/app-id')
def unregister_app_id(self, app_id):
data = {
'app-id': app_id
}
return self.api.request('DELETE', 'account/app-id', data=data)
def upload_config(self, app_id, pkey_sig, config):
data = {
'app-id': app_id,
'pkey-sig': pkey_sig,
'config': config
}
return self.api.request('POST', 'account/config', data=data)
def download_config(self, pkey_sig):
data = {
'pkey-sig': pkey_sig
}
return self.api.request('GET', 'account/config', json=data)['config']
class AnnouncementsAPI:
def __init__(self, api):
self.api = api
def get_announcements(self):
return self.api.request('GET', 'announcements')
class ProviderAPI:
def __init__(self, api):
self.api = api
def get_urls(self, provider):
query = 'provider/{}/urls'.format(provider)
return self.api.request('GET', query)
def get_status(self, provider):
query = 'provider/{}/status'.format(provider)
return self.api.request('GET', query)
class ProviderCacheAPI:
def __init__(self, api):
self.api = api
def get(self, provider, series_id, season, episode):
query = 'cache/provider/{}/series-id/{}/season/{}/episode/{}'.format(provider, series_id, season, episode)
return self.api.request('GET', query)
def add(self, data):
return self.api.request('POST', 'cache/provider', json=data)
class TorrentCacheAPI:
def __init__(self, api):
self.api = api
def get(self, hash):
query = 'cache/torrent/{}'.format(hash)
return self.api.request('GET', query)
def add(self, url):
return self.api.request('POST', 'cache/torrent', json={'url': url})
class IMDbAPI:
def __init__(self, api):
self.api = api
def search_by_imdb_title(self, title):
query = 'imdb/search-by-title/{}'.format(title)
return self.api.request('GET', query)
def search_by_imdb_id(self, id):
query = 'imdb/search-by-id/{}'.format(id)
return self.api.request('GET', query)
class GoogleDriveAPI:
def __init__(self, api):
self.api = api
def is_connected(self):
query = 'google-drive/is-connected'
return self.api.request('GET', query)
def upload(self, file, folder):
query = 'google-drive/upload'
return self.api.request('POST', query, files={'file': open(file, 'rb')}, params={'folder': folder})
def download(self, id):
query = 'google-drive/download/{id}'.format(id=id)
return self.api.request('GET', query)
def delete(self, id):
query = 'google-drive/delete/{id}'.format(id=id)
return self.api.request('GET', query)
def search_files(self, id, term):
query = 'google-drive/search-files/{id}/{term}'.format(id=id, term=term)
return self.api.request('GET', query)
def list_files(self, id):
query = 'google-drive/list-files/{id}'.format(id=id)
return self.api.request('GET', query)
def clear_folder(self, id):
query = 'google-drive/clear-folder/{id}'.format(id=id)
return self.api.request('GET', query)
# ##############################################################################
# Author: echel0n <[email protected]>
# URL: https://sickrage.ca/
# Git: https://git.sickrage.ca/SiCKRAGE/sickrage.git
# -
# This file is part of SiCKRAGE.
# -
# SiCKRAGE is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# -
# SiCKRAGE is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# -
# You should have received a copy of the GNU General Public License
# along with SiCKRAGE. If not, see <http://www.gnu.org/licenses/>.
# ##############################################################################
from sickrage.core.api import API
class AccountAPI(API):
def register_app_id(self):
return self._request('GET', 'account/app-id')
def unregister_app_id(self, app_id):
data = {
'app-id': app_id
}
return self._request('DELETE', 'account/app-id', data=data)
def upload_config(self, app_id, pkey_sig, config):
data = {
'app-id': app_id,
'pkey-sig': pkey_sig,
'config': config
}
return self._request('POST', 'account/config', data=data)
def download_config(self, pkey_sig):
data = {
'pkey-sig': pkey_sig
}
return self._request('GET', 'account/config', json=data)['config']
# ##############################################################################
# Author: echel0n <[email protected]>
# URL: https://sickrage.ca/
# Git: https://git.sickrage.ca/SiCKRAGE/sickrage.git
# -
# This file is part of SiCKRAGE.
# -
# SiCKRAGE is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# -
# SiCKRAGE is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# -
# You should have received a copy of the GNU General Public License
# along with SiCKRAGE. If not, see <http://www.gnu.org/licenses/>.
# ##############################################################################
from sickrage.core.api import API
class AnnouncementsAPI(API):
def get_announcements(self):
return self._request('GET', 'announcements')
from sickrage.core.api import API
class ProviderCacheAPI(API):
def get(self, provider, series_id, season, episode):
query = 'cache/provider/{}/series-id/{}/season/{}/episode/{}'.format(provider, series_id, season, episode)
return self._request('GET', query)
def add(self, data):
return self._request('POST', 'cache/provider', json=data)
class TorrentCacheAPI(API):
def get(self, hash):
query = 'cache/torrent/{}'.format(hash)
return self._request('GET', query)
def add(self, url):
return self._request('POST', 'cache/torrent', json={'url': url})
from sickrage.core.api import API
class GoogleDriveAPI(API):
def is_connected(self):
query = 'google-drive/is-connected'
return self._request('GET', query)
def upload(self, file, folder):
query = 'google-drive/upload'
return self._request('POST', query, files={'file': open(file, 'rb')}, params={'folder': folder})
def download(self, id):
query = 'google-drive/download/{id}'.format(id=id)
return self._request('GET', query)
def delete(self, id):
query = 'google-drive/delete/{id}'.format(id=id)
return self._request('GET', query)
def search_files(self, id, term):
query = 'google-drive/search-files/{id}/{term}'.format(id=id, term=term)
return self._request('GET', query)
def list_files(self, id):
query = 'google-drive/list-files/{id}'.format(id=id)
return self._request('GET', query)
def clear_folder(self, id):
query = 'google-drive/clear-folder/{id}'.format(id=id)
return self._request('GET', query)
from sickrage.core.api import API
class IMDbAPI(API):
def search_by_imdb_title(self, title):
query = 'imdb/search-by-title/{}'.format(title)
return self._request('GET', query)
def search_by_imdb_id(self, id):
query = 'imdb/search-by-id/{}'.format(id)
return self._request('GET', query)
# ##############################################################################
# Author: echel0n <[email protected]>
# URL: https://sickrage.ca/
# Git: https://git.sickrage.ca/SiCKRAGE/sickrage.git
# -
# This file is part of SiCKRAGE.
# -
# SiCKRAGE is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# -
# SiCKRAGE is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# -
# You should have received a copy of the GNU General Public License
# along with SiCKRAGE. If not, see <http://www.gnu.org/licenses/>.
# ##############################################################################
from sickrage.core.api import API
class ProviderAPI(API):
def get_urls(self, provider):
query = 'provider/{}/urls'.format(provider)
return self._request('GET', query)
def get_status(self, provider):
query = 'provider/{}/status'.format(provider)
return self._request('GET', query)
\ No newline at end of file
......@@ -29,7 +29,6 @@ from sqlalchemy import orm
from sqlalchemy.exc import IntegrityError
import sickrage
from sickrage.core.api.cache import ProviderCacheAPI
from sickrage.core.common import Quality
from sickrage.core.databases.cache import CacheDB
from sickrage.core.exceptions import AuthException
......@@ -248,7 +247,7 @@ class TVCache(object):
# add to external provider cache database
if sickrage.app.config.enable_api_providers_cache and not self.provider.private:
try:
sickrage.app.io_loop.run_in_executor(None, functools.partial(ProviderCacheAPI().add, data=dbData))
sickrage.app.io_loop.run_in_executor(None, functools.partial(sickrage.app.api.provider_cache.add, data=dbData))
except Exception as e:
pass
except (InvalidShowException, InvalidNameException):
......@@ -262,7 +261,7 @@ class TVCache(object):
# get data from external database
if sickrage.app.config.enable_api_providers_cache and not self.provider.private:
try:
dbData += ProviderCacheAPI().get(self.providerID, show_id, season, episode)['data']
dbData += sickrage.app.api.provider_cache.get(self.providerID, show_id, season, episode)['data']
except Exception:
pass
......
......@@ -4,7 +4,6 @@ from base64 import b64decode
from tornado.escape import json_encode
import sickrage
from sickrage.core.api.google import GoogleDriveAPI
currentInfo = ''
percentDone = 0
......@@ -28,7 +27,7 @@ class GoogleDrive(object):
def walk_drive(self, folder_id):
dirs, nondirs = {}, {}
for item in GoogleDriveAPI().list_files(folder_id)['data']:
for item in sickrage.app.api.google.list_files(folder_id)['data']:
if item['type'] == "application/vnd.google-apps.folder":
dirs.update({str(item['id']): item['name']})
else:
......@@ -42,7 +41,7 @@ class GoogleDrive(object):
def sync_remote(self):
main_folder = 'appDataFolder'
folder_id = GoogleDriveAPI().search_files(main_folder, sickrage.app.config.sub_id)['data']
folder_id = sickrage.app.api.google.search_files(main_folder, sickrage.app.config.sub_id)['data']
local_dirs = set()
local_files = set()
......@@ -56,24 +55,24 @@ class GoogleDrive(object):
folder = folder.replace('\\', '/')
for f in files:
self.set_progress('Syncing {} to Google Drive'.format(os.path.join(root, f)), 0)
GoogleDriveAPI().upload(os.path.join(root, f), folder)
sickrage.app.api.google.upload(os.path.join(root, f), folder)
# removing deleted local folders/files from google drive
for drive_root, drive_folders, drive_files in self.walk_drive(folder_id):
for folder_id, folder_name in drive_folders.items():
if folder_name not in local_dirs:
GoogleDriveAPI().delete(folder_id)
sickrage.app.api.google.delete(folder_id)
for file_id, file_name in drive_files.items():
if file_name not in local_files:
GoogleDriveAPI().delete(file_id)
sickrage.app.api.google.delete(file_id)
def sync_local(self):
main_folder = 'appDataFolder'
folder_id = GoogleDriveAPI().search_files(main_folder, sickrage.app.config.sub_id)['data']
folder_id = sickrage.app.api.google.search_files(main_folder, sickrage.app.config.sub_id)['data']
for drive_root, drive_folders, drive_files in self.walk_drive(folder_id):
folder = drive_root.replace(folder_id, sickrage.app.data_dir)
folder = folder.replace('/', '\\')
for file_id, name in drive_files.items():
content = b64decode(GoogleDriveAPI().download(file_id)).strip()
content = b64decode(sickrage.app.api.google.download(file_id)).strip()
......@@ -35,7 +35,6 @@ from unidecode import unidecode
import sickrage
from sickrage.core.api import APIError
from sickrage.core.api.imdb import IMDbAPI
from sickrage.core.blackandwhitelist import BlackAndWhiteList
from sickrage.core.caches.image_cache import ImageCache
from sickrage.core.common import Quality, SKIPPED, WANTED, UNKNOWN, DOWNLOADED, IGNORED, SNATCHED, SNATCHED_PROPER, \
......@@ -472,7 +471,7 @@ class TVShow(MainDBBase):
if not self.imdb_id:
try:
resp = IMDbAPI().search_by_imdb_title(self.name)
resp = sickrage.app.api.imdb.search_by_imdb_title(self.name)
except APIError as e:
sickrage.app.log.error('{!r}'.format(e))
resp = {}
......@@ -490,7 +489,7 @@ class TVShow(MainDBBase):
sickrage.app.log.debug(str(self.indexer_id) + ": Obtaining IMDb info")
try:
imdb_info = IMDbAPI().search_by_imdb_id(self.imdb_id)
imdb_info = sickrage.app.api.imdb.search_by_imdb_id(self.imdb_id)
except APIError as e:
imdb_info = None
sickrage.app.log.error('{!r}'.format(e))
......
......@@ -22,8 +22,6 @@ import json
from abc import ABC
import sickrage
from sickrage.core import AccountAPI
from sickrage.core.api import API
from sickrage.core.webserver.handlers.base import BaseHandler
......@@ -49,23 +47,23 @@ class LoginHandler(BaseHandler, ABC):
sickrage.app.config.save()
if sickrage.app.config.sub_id != decoded_token.get('sub'):
if API().token:
allowed_usernames = API().allowed_usernames()['data']
if sickrage.app.api.token:
allowed_usernames = sickrage.app.api.allowed_usernames()['data']
if not decoded_token['preferred_username'] in allowed_usernames:
sickrage.app.log.debug("USERNAME:{} IP:{} - WEB-UI ACCESS DENIED".format(decoded_token['preferred_username'], self.request.remote_ip))
return self.redirect('/logout')
else:
return self.redirect('/logout')
else:
if API().token:
API().logout()
API().token = token
if sickrage.app.api.token:
sickrage.app.api.logout()
sickrage.app.api.token = token
except Exception as e:
sickrage.app.log.debug('{!r}'.format(e))
return self.redirect('/logout')
if not sickrage.app.config.app_id:
sickrage.app.config.app_id = AccountAPI().register_app_id()
sickrage.app.config.app_id = sickrage.app.api.account.register_app_id()
sickrage.app.config.save()
redirect_uri = self.get_argument('next', "/{}/".format(sickrage.app.config.default_page))
......
......@@ -29,8 +29,6 @@ from tornado.httputil import url_concat
from tornado.web import authenticated
import sickrage
from sickrage.core import AccountAPI
from sickrage.core.api import API
from sickrage.core.helpers import remove_article
from sickrage.core.tv.episode import TVEpisode
from sickrage.core.tv.show.coming_episodes import ComingEpisodes
......@@ -228,14 +226,14 @@ class UnlinkHandler(BaseHandler, ABC):
if not sickrage.app.config.sub_id == self.get_current_user().get('sub'):
return self.redirect("/{}/".format(sickrage.app.config.default_page))
AccountAPI().unregister_app_id(sickrage.app.config.app_id)
sickrage.app.api.account.unregister_app_id(sickrage.app.config.app_id)
sickrage.app.config.app_id = ""
sickrage.app.config.sub_id = ""
sickrage.app.config.save()
API().logout()
del API().token
sickrage.app.api.logout()
del sickrage.app.api.token
return self.redirect('/logout/')
......
......@@ -14,7 +14,6 @@ c<%inherit file="../layouts/config.mako"/>
from sickrage.core.helpers import anon_url
from sickrage.indexers import IndexerApi
from sickrage.metadata import GenericMetadata
from sickrage.core.api.google import GoogleDriveAPI
%>
<%block name="menus">
<li class="nav-item px-1"><a class="nav-link" data-toggle="tab" href="#misc">${_('Misc')}</a></li>
......@@ -57,7 +56,7 @@ c<%inherit file="../layouts/config.mako"/>
</div>
<div class="col-lg-9 col-md-8 col-sm-7 component-desc">
% try:
% if GoogleDriveAPI().is_connected()['success']:
% if sickrage.app.api.google.is_connected()['success']:
<div class="form-row">
<div class="col-md-12">
<span class="badge badge-success">CONNECTED</span>
......
......@@ -2,8 +2,6 @@
<%!
import requests
import sickrage
from sickrage.core.api.provider import ProviderAPI
%>
<%block name="content">
<div class="row">
......@@ -29,7 +27,7 @@
try:
online = True
if 'localhost' not in providerURL:
online = bool(ProviderAPI().get_status(providerID)['data']['status'])
online = bool(sickrage.app.api.provider.get_status(providerID)['data']['status'])
except Exception:
online = False
%>
......
......@@ -42,8 +42,6 @@ from requests.utils import add_dict_to_cookiejar, dict_from_cookiejar
import sickrage
from sickrage.core.api import APIError
from sickrage.core.api.cache import TorrentCacheAPI
from sickrage.core.api.provider import ProviderAPI
from sickrage.core.caches.tv_cache import TVCache
from sickrage.core.classes import NZBSearchResult, SearchResult, TorrentSearchResult
from sickrage.core.common import MULTI_EP_RESULT, Quality, SEASON_RESULT, cpu_presets
......@@ -108,7 +106,7 @@ class GenericProvider(object):
@property
def urls(self):
try:
resp = ProviderAPI().get_urls(self.id)
resp = sickrage.app.api.provider.get_urls(self.id)
if resp and 'data' in resp:
return json.loads(resp['data']['urls'])
except (JSONDecodeError, APIError) as e:
......@@ -623,10 +621,10 @@ class TorrentProvider(GenericProvider):
if info_hash:
try:
# get content from external API
result = verify_torrent(TorrentCacheAPI().get(info_hash))
result = verify_torrent(sickrage.app.api.torrent_cache.get(info_hash))
except APIError as e:
# add torrent to external API
TorrentCacheAPI().add(url)
sickrage.app.api.torrent_cache.add(url)
# get content from other torrent hash search engines
for torrent_url in [x.format(info_hash=info_hash) for x in self.bt_cache_urls]:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment