sonarr.py overhaul

This commit is contained in:
Nicholas St. Germain 2018-11-28 14:32:39 -06:00
parent 5b3e9d8649
commit 32b20c025c
2 changed files with 210 additions and 233 deletions

42
helpers.py Normal file
View 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
View file

@ -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(self, future_days):
endpoint = '/api/calendar/'
future = str(date.today() + timedelta(days=future_days))
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())
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()