sonarr.py overhaul
This commit is contained in:
parent
5b3e9d8649
commit
32b20c025c
2 changed files with 210 additions and 233 deletions
42
helpers.py
Normal file
42
helpers.py
Normal file
|
@ -0,0 +1,42 @@
|
|||
from typing import NamedTuple
|
||||
|
||||
|
||||
class TVShow(NamedTuple):
|
||||
seriesId: int
|
||||
episodeFileId: int
|
||||
seasonNumber: int
|
||||
episodeNumber: int
|
||||
title: str
|
||||
airDate: str
|
||||
airDateUtc: str
|
||||
overview: str
|
||||
episodeFile: dict
|
||||
hasFile: bool
|
||||
monitored: bool
|
||||
unverifiedSceneNumbering: bool
|
||||
absoluteEpisodeNumber: int
|
||||
series: dict
|
||||
id: int
|
||||
|
||||
|
||||
class Queue(NamedTuple):
|
||||
series: dict
|
||||
episode: dict
|
||||
quality: dict
|
||||
size: float
|
||||
title: str
|
||||
sizeleft: float
|
||||
timeleft: str
|
||||
estimatedCompletionTime: str
|
||||
status: str
|
||||
trackedDownloadStatus: str
|
||||
statusMessages: list
|
||||
downloadId: str
|
||||
protocol: str
|
||||
id: int
|
||||
|
||||
|
||||
class Server(NamedTuple):
|
||||
url: str
|
||||
api_key: str
|
||||
id: int
|
273
sonarr.py
273
sonarr.py
|
@ -1,3 +1,4 @@
|
|||
#!/usr/bin/env python3
|
||||
# Do not edit this script. Edit configuration.py
|
||||
import sys
|
||||
import requests
|
||||
|
@ -5,140 +6,94 @@ from datetime import datetime, timezone, date, timedelta
|
|||
from influxdb import InfluxDBClient
|
||||
import argparse
|
||||
from argparse import RawTextHelpFormatter
|
||||
import configuration
|
||||
import configuration as config
|
||||
from helpers import Server, TVShow, Queue
|
||||
|
||||
|
||||
def now_iso():
|
||||
now = datetime.now(timezone.utc).astimezone().isoformat()
|
||||
return now
|
||||
class SonarrAPI(object):
|
||||
TVShow.__new__.__defaults__ = (None,) * len(TVShow._fields)
|
||||
|
||||
def __init__(self):
|
||||
self.now = datetime.now(timezone.utc).astimezone().isoformat()
|
||||
self.today = str(date.today())
|
||||
self.influx = InfluxDBClient(config.influxdb_url, config.influxdb_port, config.influxdb_username,
|
||||
config.influxdb_password, config.sonarr_influxdb_db_name)
|
||||
self.influx_payload = []
|
||||
self.servers = self.get_servers()
|
||||
self.session = requests.Session()
|
||||
self.session.params = {'pageSize': 1000}
|
||||
|
||||
def influx_sender(influx_payload):
|
||||
influx = InfluxDBClient(configuration.influxdb_url, configuration.influxdb_port, configuration.influxdb_username,
|
||||
configuration.influxdb_password, configuration.sonarr_influxdb_db_name)
|
||||
influx.write_points(influx_payload)
|
||||
@staticmethod
|
||||
def get_servers():
|
||||
if not config.sonarr_server_list:
|
||||
sys.exit("No Sonarr servers defined in config")
|
||||
|
||||
servers = []
|
||||
for url, api_key, server_id in config.sonarr_server_list:
|
||||
servers.append(Server(url=url, api_key=api_key, id=server_id))
|
||||
|
||||
def get_all_missing_shows():
|
||||
# Set the time here so we have one timestamp to work with
|
||||
now = now_iso()
|
||||
missing, influx_payload = [], []
|
||||
return servers
|
||||
|
||||
for sonarr_url, sonarr_api_key, server_id in configuration.sonarr_server_list:
|
||||
def get_missing(self, days_past):
|
||||
endpoint = '/api/calendar'
|
||||
last_days = str(date.today() + timedelta(days=-days_past))
|
||||
params = {'start': last_days, 'end': self.today}
|
||||
|
||||
headers = {'X-Api-Key': sonarr_api_key}
|
||||
for server in self.servers:
|
||||
missing = []
|
||||
headers = {'X-Api-Key': server.api_key}
|
||||
|
||||
get_tv_shows = requests.get('{}/api/wanted/missing/?pageSize=1000'.format(sonarr_url),
|
||||
headers=headers).json()['records']
|
||||
get = self.session.get(server.url + endpoint, params=params, headers=headers).json()
|
||||
tv_shows = [TVShow(**show) for show in get]
|
||||
|
||||
tv_shows = {d['id']: d for d in get_tv_shows}
|
||||
for show in tv_shows:
|
||||
if not show.hasFile:
|
||||
sxe = 'S{:0>2}E{:0>2}'.format(show.seasonNumber, show.episodeNumber)
|
||||
missing.append((show.series['title'], sxe, show.airDate, show.title, show.id))
|
||||
|
||||
for show in tv_shows.keys():
|
||||
series_title = '{}'.format(tv_shows[show]['series']['title'])
|
||||
sxe = 'S{:0>2}E{:0>2}'.format(tv_shows[show]['seasonNumber'], tv_shows[show]['episodeNumber'])
|
||||
missing.append((series_title, sxe, tv_shows[show]['id'], tv_shows[show]['title']))
|
||||
|
||||
for series_title, sxe, sonarr_id, episode_title in missing:
|
||||
influx_payload.append(
|
||||
for series_title, sxe, air_date, episode_title, sonarr_id in missing:
|
||||
self.influx_payload.append(
|
||||
{
|
||||
"measurement": "Sonarr",
|
||||
"tags": {
|
||||
"type": "Missing",
|
||||
"sonarrId": sonarr_id,
|
||||
"server": server_id
|
||||
"server": server.id
|
||||
},
|
||||
"time": now,
|
||||
"time": self.now,
|
||||
"fields": {
|
||||
"name": series_title,
|
||||
"epname": episode_title,
|
||||
"sxe": sxe
|
||||
}
|
||||
}
|
||||
)
|
||||
# Empty missing or else things get foo bared
|
||||
missing = []
|
||||
|
||||
return influx_payload
|
||||
|
||||
|
||||
def get_missing_shows(days_past):
|
||||
# Set the time here so we have one timestamp to work with
|
||||
now = now_iso()
|
||||
last_days = str(date.today()+timedelta(days=-days_past))
|
||||
today = str(date.today())
|
||||
missing, influx_payload = [], []
|
||||
|
||||
for sonarr_url, sonarr_api_key, server_id in configuration.sonarr_server_list:
|
||||
|
||||
headers = {'X-Api-Key': sonarr_api_key}
|
||||
|
||||
get_tv_shows = requests.get('{}/api/calendar/?start={}&end={}&pageSize=1000'.format(sonarr_url, last_days,
|
||||
today),
|
||||
headers=headers).json()
|
||||
|
||||
tv_shows = {d['id']: d for d in get_tv_shows}
|
||||
|
||||
for show in tv_shows.keys():
|
||||
if not (tv_shows[show]['hasFile']):
|
||||
series_title = '{}'.format(tv_shows[show]['series']['title'])
|
||||
sxe = 'S{:0>2}E{:0>2}'.format(tv_shows[show]['seasonNumber'], tv_shows[show]['episodeNumber'])
|
||||
air_date = (tv_shows[show]['airDate'])
|
||||
missing.append((series_title, sxe, air_date, tv_shows[show]['id']))
|
||||
|
||||
for series_title, sxe, air_date, sonarr_id in missing:
|
||||
influx_payload.append(
|
||||
{
|
||||
"measurement": "Sonarr",
|
||||
"tags": {
|
||||
"type": "Missing_Days",
|
||||
"sonarrId": sonarr_id,
|
||||
"server": server_id
|
||||
},
|
||||
"time": now,
|
||||
"fields": {
|
||||
"name": series_title,
|
||||
"sxe": sxe,
|
||||
"airs": air_date
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
# Empty missing or else things get foo bared
|
||||
missing = []
|
||||
def get_upcoming(self):
|
||||
endpoint = '/api/calendar/'
|
||||
|
||||
return influx_payload
|
||||
|
||||
|
||||
def get_upcoming_shows():
|
||||
# Set the time here so we have one timestamp to work with
|
||||
now = now_iso()
|
||||
for server in self.servers:
|
||||
upcoming = []
|
||||
influx_payload = []
|
||||
headers = {'X-Api-Key': server.api_key}
|
||||
|
||||
for sonarr_url, sonarr_api_key, server_id in configuration.sonarr_server_list:
|
||||
get = self.session.get(server.url + endpoint, headers=headers).json()
|
||||
tv_shows = [TVShow(**show) for show in get]
|
||||
|
||||
headers = {'X-Api-Key': sonarr_api_key}
|
||||
|
||||
upcoming_shows_request = requests.get('{}/api/calendar/'.format(sonarr_url), headers=headers).json()
|
||||
|
||||
upcoming_shows = {d['id']: d for d in upcoming_shows_request}
|
||||
|
||||
for show in upcoming_shows.keys():
|
||||
series_title = '{}'.format(upcoming_shows[show]['series']['title'])
|
||||
sxe = 'S{:0>2}E{:0>2}'.format(upcoming_shows[show]['seasonNumber'], upcoming_shows[show]['episodeNumber'])
|
||||
upcoming.append((series_title, sxe, upcoming_shows[show]['id'], upcoming_shows[show]['title'],
|
||||
upcoming_shows[show]['airDate']))
|
||||
for show in tv_shows:
|
||||
sxe = 'S{:0>2}E{:0>2}'.format(show.seasonNumber, show.episodeNumber)
|
||||
upcoming.append((show.series['title'], sxe, show.id, show.title, show.airDate))
|
||||
|
||||
for series_title, sxe, sonarr_id, episode_title, air_date in upcoming:
|
||||
influx_payload.append(
|
||||
self.influx_payload.append(
|
||||
{
|
||||
"measurement": "Sonarr",
|
||||
"tags": {
|
||||
"type": "Soon",
|
||||
"sonarrId": sonarr_id,
|
||||
"server": server_id
|
||||
"server": server.id
|
||||
},
|
||||
"time": now,
|
||||
"time": self.now,
|
||||
"fields": {
|
||||
"name": series_title,
|
||||
"epname": episode_title,
|
||||
|
@ -147,47 +102,33 @@ def get_upcoming_shows():
|
|||
}
|
||||
}
|
||||
)
|
||||
# Empty upcoming or else things get foo bared
|
||||
upcoming = []
|
||||
|
||||
return influx_payload
|
||||
|
||||
|
||||
def get_future_shows(future_days):
|
||||
# Set the time here so we have one timestamp to work with
|
||||
now = now_iso()
|
||||
|
||||
today = str(date.today())
|
||||
def get_future(self, future_days):
|
||||
endpoint = '/api/calendar/'
|
||||
future = str(date.today() + timedelta(days=future_days))
|
||||
|
||||
for server in self.servers:
|
||||
air_days = []
|
||||
influx_payload = []
|
||||
headers = {'X-Api-Key': server.api_key}
|
||||
params = {'start': self.today, 'end': future}
|
||||
|
||||
for sonarr_url, sonarr_api_key, server_id in configuration.sonarr_server_list:
|
||||
get = self.session.get(server.url + endpoint, params=params, headers=headers).json()
|
||||
tv_shows = [TVShow(**show) for show in get]
|
||||
|
||||
headers = {'X-Api-Key': sonarr_api_key}
|
||||
|
||||
get_tv_shows = requests.get('{}/api/calendar/?start={}&end={}&pageSize=200'.format(sonarr_url, today, future),
|
||||
headers=headers).json()
|
||||
|
||||
tv_shows = {d['id']: d for d in get_tv_shows}
|
||||
|
||||
for show in tv_shows.keys():
|
||||
series_title = '{}'.format(tv_shows[show]['series']['title'])
|
||||
dl_status = int(tv_shows[show]['hasFile'])
|
||||
sxe = 'S{:0>2}E{:0>2}'.format(tv_shows[show]['seasonNumber'], tv_shows[show]['episodeNumber'])
|
||||
air_days.append((series_title, dl_status, sxe, tv_shows[show]['title'], tv_shows[show]['airDate'],
|
||||
tv_shows[show]['id']))
|
||||
for show in tv_shows:
|
||||
sxe = 'S{:0>2}E{:0>2}'.format(show.seasonNumber, show.episodeNumber)
|
||||
air_days.append((show.series['title'], show.hasFile, sxe, show.title, show.airDate, show.id))
|
||||
|
||||
for series_title, dl_status, sxe, episode_title, air_date, sonarr_id in air_days:
|
||||
influx_payload.append(
|
||||
self.influx_payload.append(
|
||||
{
|
||||
"measurement": "Sonarr",
|
||||
"tags": {
|
||||
"type": "Future",
|
||||
"sonarrId": sonarr_id,
|
||||
"server": server_id
|
||||
"server": server.id
|
||||
},
|
||||
"time": now,
|
||||
"time": self.now,
|
||||
"fields": {
|
||||
"name": series_title,
|
||||
"epname": episode_title,
|
||||
|
@ -197,51 +138,38 @@ def get_future_shows(future_days):
|
|||
}
|
||||
}
|
||||
)
|
||||
# Empty air_days or else things get foo bared
|
||||
air_days = []
|
||||
|
||||
return influx_payload
|
||||
def get_queue(self):
|
||||
endpoint = '/api/queue'
|
||||
|
||||
|
||||
def get_queue_shows():
|
||||
# Set the time here so we have one timestamp to work with
|
||||
now = now_iso()
|
||||
for server in self.servers:
|
||||
queue = []
|
||||
influx_payload = []
|
||||
headers = {'X-Api-Key': server.api_key}
|
||||
|
||||
for sonarr_url, sonarr_api_key, server_id in configuration.sonarr_server_list:
|
||||
get = self.session.get(server.url + endpoint, headers=headers).json()
|
||||
download_queue = [Queue(**show) for show in get]
|
||||
|
||||
headers = {'X-Api-Key': sonarr_api_key}
|
||||
|
||||
get_tv_shows = requests.get('{}/api/queue'.format(sonarr_url),
|
||||
headers=headers).json()
|
||||
|
||||
tv_shows = {d['id']: d for d in get_tv_shows}
|
||||
|
||||
for show in tv_shows.keys():
|
||||
series_title = '{}'.format(tv_shows[show]['series']['title'])
|
||||
episode_title = '{}'.format(tv_shows[show]['episode']['title'])
|
||||
protocol = tv_shows[show]['protocol'].upper()
|
||||
sxe = 'S{:0>2}E{:0>2}'.format(tv_shows[show]['episode']['seasonNumber'],
|
||||
tv_shows[show]['episode']['episodeNumber'])
|
||||
if protocol == 'USENET':
|
||||
for show in download_queue:
|
||||
sxe = 'S{:0>2}E{:0>2}'.format(show.episode['seasonNumber'], show.episode['episodeNumber'])
|
||||
if show.protocol.upper() == 'USENET':
|
||||
protocol_id = 1
|
||||
else:
|
||||
protocol_id = 0
|
||||
|
||||
queue.append((series_title, episode_title, protocol, protocol_id, sxe, tv_shows[show]['id']))
|
||||
queue.append((show.series['title'], show.episode['title'], show.protocol.upper(),
|
||||
protocol_id, sxe, show.id))
|
||||
|
||||
for series_title, episode_title, protocol, protocol_id, sxe, sonarr_id in queue:
|
||||
influx_payload.append(
|
||||
self.influx_payload.append(
|
||||
{
|
||||
"measurement": "Sonarr",
|
||||
"tags": {
|
||||
"type": "Queue",
|
||||
"sonarrId": sonarr_id,
|
||||
"server": server_id
|
||||
"server": server.id
|
||||
|
||||
},
|
||||
"time": now,
|
||||
"time": self.now,
|
||||
"fields": {
|
||||
"name": series_title,
|
||||
"epname": episode_title,
|
||||
|
@ -252,10 +180,9 @@ def get_queue_shows():
|
|||
}
|
||||
)
|
||||
|
||||
# Empty queue or else things get foo bared
|
||||
queue = []
|
||||
|
||||
return influx_payload
|
||||
def influx_push(self):
|
||||
# TODO: error handling for failed connection
|
||||
self.influx.write_points(self.influx_payload)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
@ -263,25 +190,33 @@ if __name__ == "__main__":
|
|||
description='Script to aid in data gathering from Sonarr',
|
||||
formatter_class=RawTextHelpFormatter)
|
||||
|
||||
parser.add_argument("--missing", action='store_true', help='Get all missing TV shows')
|
||||
parser.add_argument("--missing_days", type=int, help='Get missing TV shows in past X days')
|
||||
parser.add_argument("--missing", metavar='$days', type=int, help='Get missing TV shows in past X days'
|
||||
'\ni.e. --missing 7 is in the last week')
|
||||
parser.add_argument("--missing_days", metavar='$days', type=int, help='legacy command. Deprecated in favor of'
|
||||
' --missing'
|
||||
'\nfunctions identically to --missing'
|
||||
'\nNote: Will be removed in a future release')
|
||||
parser.add_argument("--upcoming", action='store_true', help='Get upcoming TV shows')
|
||||
parser.add_argument("--future", type=int, help='Get TV shows on X days into the future. Includes today.'
|
||||
parser.add_argument("--future", metavar='$days', type=int, help='Get TV shows on X days into the future. '
|
||||
'Includes today.'
|
||||
'\ni.e. --future 2 is Today and Tomorrow')
|
||||
parser.add_argument("--queue", action='store_true', help='Get TV shows in queue')
|
||||
|
||||
opts = parser.parse_args()
|
||||
sonarr = SonarrAPI()
|
||||
|
||||
if opts.missing:
|
||||
influx_sender(get_all_missing_shows())
|
||||
elif opts.missing_days:
|
||||
influx_sender(get_missing_shows(opts.missing_days))
|
||||
elif opts.upcoming:
|
||||
influx_sender(get_upcoming_shows())
|
||||
elif opts.future:
|
||||
influx_sender(get_future_shows(opts.future))
|
||||
elif opts.queue:
|
||||
influx_sender(get_queue_shows())
|
||||
elif len(sys.argv) == 1:
|
||||
if len(sys.argv) == 1:
|
||||
parser.print_help(sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
if any([opts.missing, opts.missing_days]):
|
||||
days = opts.missing if opts.missing else opts.missing_days
|
||||
sonarr.get_missing(days)
|
||||
if opts.upcoming:
|
||||
sonarr.get_upcoming()
|
||||
if opts.future:
|
||||
sonarr.get_future(opts.future)
|
||||
if opts.queue:
|
||||
sonarr.get_queue()
|
||||
|
||||
sonarr.influx_push()
|
||||
|
|
Loading…
Reference in a new issue