Add Overseerr Support (#210)

This commit is contained in:
samwiseg0 2022-01-14 23:16:44 -05:00
parent 9498a83bc8
commit 73b2686ba0
5 changed files with 312 additions and 3 deletions

View file

@ -14,6 +14,7 @@ from logging import getLogger, StreamHandler, Formatter, DEBUG
# Needed to check version of python
from varken import structures # noqa
from varken.ombi import OmbiAPI
from varken.overseerr import OverseerrAPI
from varken.unifi import UniFiAPI
from varken import VERSION, BRANCH, BUILD_DATE
from varken.sonarr import SonarrAPI
@ -156,6 +157,21 @@ if __name__ == "__main__":
at_time = schedule.every(server.issue_status_run_seconds).seconds
at_time.do(thread, OMBI.get_issue_counts).tag("ombi-{}-get_issue_counts".format(server.id))
if CONFIG.overseerr_enabled:
for server in CONFIG.overseerr_servers:
OVERSEER = OverseerrAPI(server, DBMANAGER)
if server.get_request_total_counts:
at_time = schedule.every(server.request_total_run_seconds).seconds
at_time.do(thread, OVERSEER.get_total_requests).tag("overseerr-{}-get_total_requests".format(server.id))
if server.get_request_status_counts:
at_time = schedule.every(server.request_status_run_seconds).seconds
at_time.do(thread, OVERSEER.get_request_status_counts).tag("overseerr-{}-get_request_status_counts"
.format(server.id))
if server.get_latest_requests:
at_time = schedule.every(server.num_latest_requests_seconds).seconds
at_time.do(thread, OVERSEER.get_latest_requests).tag("overseerr-{}-get_latest_requests"
.format(server.id))
if CONFIG.sickchill_enabled:
for server in CONFIG.sickchill_servers:
SICKCHILL = SickChillAPI(server, DBMANAGER)

View file

@ -3,7 +3,8 @@ sonarr_server_ids = 1,2
radarr_server_ids = 1,2
lidarr_server_ids = false
tautulli_server_ids = 1
ombi_server_ids = 1
ombi_server_ids = false
overseerr_server_ids = 1
sickchill_server_ids = false
unifi_server_ids = false
maxmind_license_key = xxxxxxxxxxxxxxxx
@ -95,6 +96,19 @@ request_total_run_seconds = 300
get_issue_status_counts = true
issue_status_run_seconds = 300
[overseerr-1]
url = overseerr.domain.tld
apikey = xxxxxxxxxxxxxxxx
ssl = false
verify_ssl = false
get_request_total_counts = true
request_total_run_seconds = 300
get_request_status_counts = true
request_status_run_seconds = 300
get_latest_requests = true
num_latest_requests_to_fetch = 10
num_latest_requests_seconds = 300
[sickchill-1]
url = sickchill.domain.tld:8081
apikey = xxxxxxxxxxxxxxxx

View file

@ -9,7 +9,7 @@ from configparser import ConfigParser, NoOptionError, NoSectionError
from varken.varkenlogger import BlacklistFilter
from varken.structures import SickChillServer, UniFiServer
from varken.helpers import clean_sid_check, rfc1918_ip_check, boolcheck
from varken.structures import SonarrServer, RadarrServer, OmbiServer, TautulliServer, InfluxServer
from varken.structures import SonarrServer, RadarrServer, OmbiServer, OverseerrServer, TautulliServer, InfluxServer
class INIParser(object):
@ -17,7 +17,7 @@ class INIParser(object):
self.config = None
self.data_folder = data_folder
self.filtered_strings = None
self.services = ['sonarr', 'radarr', 'lidarr', 'ombi', 'tautulli', 'sickchill', 'unifi']
self.services = ['sonarr', 'radarr', 'lidarr', 'ombi', 'overseerr', 'tautulli', 'sickchill', 'unifi']
self.logger = getLogger()
self.influx_server = InfluxServer()
@ -293,6 +293,38 @@ class INIParser(object):
issue_status_counts=issue_status_counts,
issue_status_run_seconds=issue_status_run_seconds)
if service == 'overseerr':
get_latest_requests = boolcheck(env.get(
f'VRKN_{envsection}_GET_LATEST_REQUESTS',
self.config.get(section, 'get_latest_requests')))
num_latest_requests_to_fetch = int(env.get(
f'VRKN_{envsection}_NUM_LATEST_REQUESTS',
self.config.getint(section, 'num_latest_requests_to_fetch')))
num_latest_requests_seconds = int(env.get(
f'VRKN_{envsection}_NUM_LATEST_REQUESTS_SECONDS',
self.config.getint(section, 'num_latest_requests_seconds')))
get_request_total_counts = boolcheck(env.get(
f'VRKN_{envsection}_GET_REQUEST_TOTAL_COUNTS',
self.config.get(section, 'get_request_total_counts')))
request_total_run_seconds = int(env.get(
f'VRKN_{envsection}_REQUEST_TOTAL_RUN_SECONDS',
self.config.getint(section, 'request_total_run_seconds')))
get_request_status_counts = boolcheck(env.get(
f'VRKN_{envsection}_GET_REQUEST_STATUS_COUNTS',
self.config.get(section, 'get_request_status_counts')))
request_status_run_seconds = int(env.get(
f'VRKN_{envsection}_REQUEST_STATUS_RUN_SECONDS',
self.config.getint(section, 'request_status_run_seconds')))
server = OverseerrServer(id=server_id, url=scheme + url, api_key=apikey,
verify_ssl=verify_ssl, get_latest_requests=get_latest_requests,
num_latest_requests_to_fetch=num_latest_requests_to_fetch,
num_latest_requests_seconds=num_latest_requests_seconds,
get_request_total_counts=get_request_total_counts,
request_total_run_seconds=request_total_run_seconds,
get_request_status_counts=get_request_status_counts,
request_status_run_seconds=request_status_run_seconds)
if service == 'sickchill':
get_missing = boolcheck(env.get(f'VRKN_{envsection}_GET_MISSING',
self.config.get(section, 'get_missing')))

179
varken/overseerr.py Normal file
View file

@ -0,0 +1,179 @@
from logging import getLogger
from requests import Session, Request
from datetime import datetime, timezone
from varken.helpers import connection_handler, hashit
from varken.structures import OverseerrRequest, OverseerrRequestCounts
class OverseerrAPI(object):
def __init__(self, server, dbmanager):
self.dbmanager = dbmanager
self.server = server
# Create session to reduce server web thread load, and globally define pageSize for all requests
self.session = Session()
self.session.headers = {'X-Api-Key': self.server.api_key}
self.logger = getLogger()
def __repr__(self):
return f"<overseerr-{self.server.id}>"
def get_total_requests(self):
now = datetime.now(timezone.utc).astimezone().isoformat()
endpoint = '/api/v1/request?take=200&filter=all&sort=added'
req = self.session.prepare_request(Request('GET', self.server.url + endpoint))
get_req = connection_handler(self.session, req, self.server.verify_ssl) or []
if not any([get_req]):
self.logger.error('No json replies. Discarding job')
return
tv_requests = []
movie_requests = []
for result in get_req['results']:
if result['type'] == 'tv':
try:
tv_requests.append(OverseerrRequest(**result))
except TypeError as e:
self.logger.error('TypeError has occurred : %s while creating OverseerrRequest structure for show. '
'data attempted is: %s', e, result)
if result['type'] == 'movie':
try:
movie_requests.append(OverseerrRequest(**result))
except TypeError as e:
self.logger.error('TypeError has occurred : %s while creating OverseerrRequest \
structure for movie. '
'data attempted is: %s', e, result)
if tv_requests:
tv_request_count = len(tv_requests)
if movie_requests:
movie_request_count = len(movie_requests)
influx_payload = [
{
"measurement": "Overseerr",
"tags": {
"type": "Request_Totals",
"server": self.server.id
},
"time": now,
"fields": {
"total": movie_request_count + tv_request_count,
"movies": movie_request_count,
"tv": tv_request_count
}
}
]
if influx_payload:
self.dbmanager.write_points(influx_payload)
else:
self.logger.debug("Empty dataset for overseerr module. Discarding...")
def get_request_status_counts(self):
now = datetime.now(timezone.utc).astimezone().isoformat()
endpoint = '/api/v1/request/count'
req = self.session.prepare_request(Request('GET', self.server.url + endpoint))
get_req = connection_handler(self.session, req, self.server.verify_ssl)
if not get_req:
return
requests = OverseerrRequestCounts(**get_req)
influx_payload = [
{
"measurement": "Overseerr",
"tags": {
"type": "Request_Counts"
},
"time": now,
"fields": {
"pending": requests.pending,
"approved": requests.approved,
"processing": requests.processing,
"available": requests.available
}
}
]
self.dbmanager.write_points(influx_payload)
def get_latest_requests(self):
now = datetime.now(timezone.utc).astimezone().isoformat()
endpoint = '/api/v1/request?take=' + str(self.server.num_latest_requests_to_fetch) + '&filter=all&sort=added'
movie_endpoint = '/api/v1/movie/'
tv_endpoint = '/api/v1/tv/'
# GET THE LATEST n REQUESTS
req = self.session.prepare_request(Request('GET', self.server.url + endpoint))
get_latest_req = connection_handler(self.session, req, self.server.verify_ssl)
# RETURN NOTHING IF NO RESULTS
if not get_latest_req:
return
influx_payload = []
# Request Type: Movie = 1, TV Show = 0
for result in get_latest_req['results']:
if result['type'] == 'tv':
req = self.session.prepare_request(Request('GET',
self.server.url +
tv_endpoint +
str(result['media']['tmdbId'])))
get_tv_req = connection_handler(self.session, req, self.server.verify_ssl)
hash_id = hashit(f'{get_tv_req["id"]}{get_tv_req["name"]}')
influx_payload.append(
{
"measurement": "Overseerr",
"tags": {
"type": "Requests",
"server": self.server.id,
"request_type": 0,
"status": get_tv_req['mediaInfo']['status'],
"title": get_tv_req['name'],
"requested_user": get_tv_req['mediaInfo']['requests'][0]['requestedBy']['plexUsername'],
"requested_date": get_tv_req['mediaInfo']['requests'][0]['requestedBy']['createdAt']
},
"time": now,
"fields": {
"hash": hash_id
}
}
)
if result['type'] == 'movie':
req = self.session.prepare_request(Request('GET',
self.server.url +
movie_endpoint +
str(result['media']['tmdbId'])))
get_movie_req = connection_handler(self.session, req, self.server.verify_ssl)
hash_id = hashit(f'{get_movie_req["id"]}{get_movie_req["title"]}')
influx_payload.append(
{
"measurement": "Overseerr",
"tags": {
"type": "Requests",
"server": self.server.id,
"request_type": 1,
"status": get_movie_req['mediaInfo']['status'],
"title": get_movie_req['title'],
"requested_user": get_movie_req['mediaInfo']['requests'][0]['requestedBy']['plexUsername'],
"requested_date": get_movie_req['mediaInfo']['requests'][0]['requestedBy']['createdAt']
},
"time": now,
"fields": {
"hash": hash_id
}
}
)
self.dbmanager.write_points(influx_payload)

View file

@ -57,6 +57,20 @@ class OmbiServer(NamedTuple):
verify_ssl: bool = False
class OverseerrServer(NamedTuple):
api_key: str = None
id: int = None
url: str = None
verify_ssl: bool = False
get_request_total_counts: bool = False
request_total_run_seconds: int = 30
get_request_status_counts: bool = False
request_status_run_seconds: int = 30
get_latest_requests: bool = False
num_latest_requests_to_fetch: int = 10
num_latest_requests_seconds: int = 30
class TautulliServer(NamedTuple):
api_key: str = None
fallback_ip: str = None
@ -173,6 +187,60 @@ class OmbiMovieRequest(NamedTuple):
requestStatus: str = None
# Overseerr
class OverseerrRequest(NamedTuple):
id: int = None
status: int = None
createdAt: str = None
updatedAt: str = None
type: str = None
is4k: bool = None
serverId: int = None
profileId: int = None
rootFolder: str = None
languageProfileId: int = None
tags: list = None
media: dict = None
seasons: list = None
modifiedBy: dict = None
requestedBy: dict = None
seasonCount: int = None
class OverseerrRequestCounts(NamedTuple):
pending: int = None
approved: int = None
processing: int = None
available: int = None
# Overseerr
class OverseerrRequest(NamedTuple):
id: int = None
status: int = None
createdAt: str = None
updatedAt: str = None
type: str = None
is4k: bool = None
serverId: int = None
profileId: int = None
rootFolder: str = None
languageProfileId: int = None
tags: list = None
media: dict = None
seasons: list = None
modifiedBy: dict = None
requestedBy: dict = None
seasonCount: int = None
class OverseerrRequestCounts(NamedTuple):
pending: int = None
approved: int = None
processing: int = None
available: int = None
# Sonarr
class SonarrTVShow(NamedTuple):
added: str = None