Merge pull request #214 from RobinDadswell/RadarrAndSonarrV3Api

This commit is contained in:
samwiseg0 2021-09-20 15:24:29 -04:00 committed by GitHub
commit e3f2ee967c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 198 additions and 70 deletions

View file

@ -2,7 +2,7 @@ from logging import getLogger
from requests import Session, Request from requests import Session, Request
from datetime import datetime, timezone from datetime import datetime, timezone
from varken.structures import RadarrMovie, Queue from varken.structures import QueuePages, RadarrMovie, RadarrQueue
from varken.helpers import hashit, connection_handler from varken.helpers import hashit, connection_handler
@ -18,8 +18,19 @@ class RadarrAPI(object):
def __repr__(self): def __repr__(self):
return f"<radarr-{self.server.id}>" return f"<radarr-{self.server.id}>"
def get_movie(self,id):
endpoint = '/api/v3/movie/'
req = self.session.prepare_request(Request('GET', self.server.url + endpoint + str(id)))
get = connection_handler(self.session, req, self.server.verify_ssl)
if not get:
return
return RadarrMovie(**get)
def get_missing(self): def get_missing(self):
endpoint = '/api/movie' endpoint = '/api/v3/movie'
now = datetime.now(timezone.utc).astimezone().isoformat() now = datetime.now(timezone.utc).astimezone().isoformat()
influx_payload = [] influx_payload = []
missing = [] missing = []
@ -37,7 +48,7 @@ class RadarrAPI(object):
return return
for movie in movies: for movie in movies:
if movie.monitored and not movie.downloaded: if movie.monitored and not movie.hasFile:
if movie.isAvailable: if movie.isAvailable:
ma = 0 ma = 0
else: else:
@ -69,32 +80,45 @@ class RadarrAPI(object):
self.dbmanager.write_points(influx_payload) self.dbmanager.write_points(influx_payload)
def get_queue(self): def get_queue(self):
endpoint = '/api/queue' endpoint = '/api/v3/queue'
now = datetime.now(timezone.utc).astimezone().isoformat() now = datetime.now(timezone.utc).astimezone().isoformat()
influx_payload = [] influx_payload = []
pageSize = 250
params = {'pageSize': pageSize}
queueResponse = []
queue = [] queue = []
req = self.session.prepare_request(Request('GET', self.server.url + endpoint)) req = self.session.prepare_request(Request('GET', self.server.url + endpoint,params=params))
get = connection_handler(self.session, req, self.server.verify_ssl) get = connection_handler(self.session, req, self.server.verify_ssl)
if not get: if not get:
return return
response = QueuePages(**get)
queueResponse.extend(response.records)
for movie in get: while response.totalRecords > response.page * response.pageSize:
try: page = response.page + 1
movie['movie'] = RadarrMovie(**movie['movie']) params = {'pageSize': pageSize, 'page': page}
except TypeError as e: req = self.session.prepare_request(Request('GET', self.server.url + endpoint,params=params))
self.logger.error('TypeError has occurred : %s while creating RadarrMovie structure', e) get = connection_handler(self.session, req, self.server.verify_ssl)
if not get:
return return
try: response = QueuePages(**get)
download_queue = [Queue(**movie) for movie in get] queueResponse.extend(response.records)
except TypeError as e:
self.logger.error('TypeError has occurred : %s while creating Queue structure', e) download_queue = []
for queueItem in queueResponse:
try:
download_queue.append(RadarrQueue(**queueItem))
except TypeError as e:
self.logger.error('TypeError has occurred : %s while creating RadarrQueue structure', e)
return
if not download_queue:
return return
for queue_item in download_queue: for queue_item in download_queue:
movie = queue_item.movie movie = self.get_movie(queue_item.movieId)
name = f'{movie.title} ({movie.year})' name = f'{movie.title} ({movie.year})'

View file

@ -2,7 +2,7 @@ from logging import getLogger
from requests import Session, Request from requests import Session, Request
from datetime import datetime, timezone, date, timedelta from datetime import datetime, timezone, date, timedelta
from varken.structures import Queue, SonarrTVShow from varken.structures import SonarrEpisode, SonarrQueue, QueuePages, SonarrTVShow
from varken.helpers import hashit, connection_handler from varken.helpers import hashit, connection_handler
@ -18,9 +18,33 @@ class SonarrAPI(object):
def __repr__(self): def __repr__(self):
return f"<sonarr-{self.server.id}>" return f"<sonarr-{self.server.id}>"
def get_series(self, id):
endpoint = '/api/v3/series/'
req = self.session.prepare_request(Request('GET', self.server.url + endpoint + str(id)))
get = connection_handler(self.session, req, self.server.verify_ssl)
if not get:
return
return SonarrTVShow(**get)
def get_episode(self, id):
endpoint = '/api/v3/episode'
params = {'episodeIds': id}
req = self.session.prepare_request(Request('GET', self.server.url + endpoint,params = params))
get = connection_handler(self.session, req, self.server.verify_ssl)
if not get:
return
return SonarrEpisode(**get[0])
def get_calendar(self, query="Missing"): def get_calendar(self, query="Missing"):
endpoint = '/api/calendar/' endpoint = '/api/v3/calendar/'
today = str(date.today()) today = str(date.today())
last_days = str(date.today() - timedelta(days=self.server.missing_days)) last_days = str(date.today() - timedelta(days=self.server.missing_days))
future = str(date.today() + timedelta(days=self.server.future_days)) future = str(date.today() + timedelta(days=self.server.future_days))
@ -42,22 +66,23 @@ class SonarrAPI(object):
tv_shows = [] tv_shows = []
for show in get: for show in get:
try: try:
tv_shows.append(SonarrTVShow(**show)) tv_shows.append(SonarrEpisode(**show))
except TypeError as e: except TypeError as e:
self.logger.error('TypeError has occurred : %s while creating SonarrTVShow structure for show. Data ' self.logger.error('TypeError has occurred : %s while creating SonarrEpisode structure for show. Data '
'attempted is: %s', e, show) 'attempted is: %s', e, show)
for show in tv_shows: for episode in tv_shows:
sxe = f'S{show.seasonNumber:0>2}E{show.episodeNumber:0>2}' tvShow = self.get_series(episode.seriesId)
if show.hasFile: sxe = f'S{episode.seasonNumber:0>2}E{episode.episodeNumber:0>2}'
if episode.hasFile:
downloaded = 1 downloaded = 1
else: else:
downloaded = 0 downloaded = 0
if query == "Missing": if query == "Missing":
if show.monitored and not downloaded: if episode.monitored and not downloaded:
missing.append((show.series['title'], downloaded, sxe, show.title, show.airDateUtc, show.id)) missing.append((tvShow.title, downloaded, sxe, episode.title, episode.airDateUtc, episode.seriesId))
else: else:
air_days.append((show.series['title'], downloaded, sxe, show.title, show.airDateUtc, show.id)) air_days.append((tvShow.title, downloaded, sxe, episode.title, episode.airDateUtc, episode.seriesId))
for series_title, dl_status, sxe, episode_title, air_date_utc, sonarr_id in (air_days or missing): for series_title, dl_status, sxe, episode_title, air_date_utc, sonarr_id in (air_days or missing):
hash_id = hashit(f'{self.server.id}{series_title}{sxe}') hash_id = hashit(f'{self.server.id}{series_title}{sxe}')
@ -85,41 +110,59 @@ class SonarrAPI(object):
def get_queue(self): def get_queue(self):
influx_payload = [] influx_payload = []
endpoint = '/api/queue' endpoint = '/api/v3/queue'
now = datetime.now(timezone.utc).astimezone().isoformat() now = datetime.now(timezone.utc).astimezone().isoformat()
pageSize = 250
params = {'pageSize': pageSize}
queueResponse = []
queue = [] queue = []
req = self.session.prepare_request(Request('GET', self.server.url + endpoint)) req = self.session.prepare_request(Request('GET', self.server.url + endpoint,params=params))
get = connection_handler(self.session, req, self.server.verify_ssl) get = connection_handler(self.session, req, self.server.verify_ssl)
if not get: if not get:
return return
response = QueuePages(**get)
queueResponse.extend(response.records)
while response.totalRecords > response.page * response.pageSize:
page = response.page + 1
params = {'pageSize': pageSize, 'page': page}
req = self.session.prepare_request(Request('GET', self.server.url + endpoint,params=params))
get = connection_handler(self.session, req, self.server.verify_ssl)
if not get:
return
response = QueuePages(**get)
queueResponse.extend(response.records)
download_queue = [] download_queue = []
for show in get: for queueItem in queueResponse:
try: try:
download_queue.append(Queue(**show)) download_queue.append(SonarrQueue(**queueItem))
except TypeError as e: except TypeError as e:
self.logger.error('TypeError has occurred : %s while creating Queue structure. Data attempted is: ' self.logger.error('TypeError has occurred : %s while creating Queue structure. Data attempted is: '
'%s', e, show) '%s', e, queueItem)
if not download_queue: if not download_queue:
return return
for show in download_queue: for queueItem in download_queue:
tvShow = self.get_series(queueItem.seriesId)
episode = self.get_episode(queueItem.episodeId)
try: try:
sxe = f"S{show.episode['seasonNumber']:0>2}E{show.episode['episodeNumber']:0>2}" sxe = f"S{episode.seasonNumber:0>2}E{episode.episodeNumber:0>2}"
except TypeError as e: except TypeError as e:
self.logger.error('TypeError has occurred : %s while processing the sonarr queue. \ self.logger.error('TypeError has occurred : %s while processing the sonarr queue. \
Remove invalid queue entry. Data attempted is: %s', e, show) Remove invalid queue entry. Data attempted is: %s', e, queueItem)
continue continue
if show.protocol.upper() == 'USENET': if queueItem.protocol.upper() == 'USENET':
protocol_id = 1 protocol_id = 1
else: else:
protocol_id = 0 protocol_id = 0
queue.append((show.series['title'], show.episode['title'], show.protocol.upper(), queue.append((tvShow.title, episode.title, queueItem.protocol.upper(),
protocol_id, sxe, show.id, show.quality['quality']['name'])) protocol_id, sxe, queueItem.seriesId, queueItem.quality['quality']['name']))
for series_title, episode_title, protocol, protocol_id, sxe, sonarr_id, quality in queue: for series_title, episode_title, protocol, protocol_id, sxe, sonarr_id, quality in queue:
hash_id = hashit(f'{self.server.id}{series_title}{sxe}') hash_id = hashit(f'{self.server.id}{series_title}{sxe}')

View file

@ -91,22 +91,13 @@ class UniFiServer(NamedTuple):
# Shared # Shared
class Queue(NamedTuple): class QueuePages(NamedTuple):
downloadId: str = None page: int = None
episode: dict = None pageSize: int = None
estimatedCompletionTime: str = None sortKey: str = None
id: int = None sortDirection: str = None
movie: dict = None totalRecords: str = None
protocol: str = None records: list = None
quality: dict = None
series: dict = None
size: float = None
sizeleft: float = None
status: str = None
statusMessages: list = None
timeleft: str = None
title: str = None
trackedDownloadStatus: str = None
# Ombi Structures # Ombi Structures
@ -184,35 +175,88 @@ class OmbiMovieRequest(NamedTuple):
# Sonarr # Sonarr
class SonarrTVShow(NamedTuple): class SonarrTVShow(NamedTuple):
added: str = None
airTime: str = None
alternateTitles: list = None
certification: str = None
cleanTitle: str = None
ended: bool = None
firstAired: str = None
genres: list = None
id: int = None
images: list = None
imdbId: str = None
languageProfileId: int = None
monitored: bool = None
nextAiring: str = None
network: str = None
overview: str = None
path: str = None
previousAiring: str = None
qualityProfileId: int = None
ratings: dict = None
rootFolderPath: str = None
runtime: int = None
seasonFolder: bool = None
seasons: list = None
seriesType: str = None
sortTitle: str = None
statistics: dict = None
status: str = None
tags: list = None
title: str = None
titleSlug: str = None
tvdbId: int = None
tvMazeId: int = None
tvRageId: int = None
useSceneNumbering: bool = None
year: int = None
class SonarrEpisode(NamedTuple):
absoluteEpisodeNumber: int = None absoluteEpisodeNumber: int = None
airDate: str = None airDate: str = None
airDateUtc: str = None airDateUtc: str = None
episodeFile: dict = None
episodeFileId: int = None episodeFileId: int = None
episodeNumber: int = None episodeNumber: int = None
grabbed: bool = None
hasFile: bool = None hasFile: bool = None
id: int = None id: int = None
lastSearchTime: str = None
monitored: bool = None monitored: bool = None
overview: str = None overview: str = None
sceneAbsoluteEpisodeNumber: int = None
sceneEpisodeNumber: int = None
sceneSeasonNumber: int = None
seasonNumber: int = None seasonNumber: int = None
series: dict = None
seriesId: int = None seriesId: int = None
title: str = None title: str = None
unverifiedSceneNumbering: bool = None unverifiedSceneNumbering: bool = None
class SonarrQueue(NamedTuple):
downloadClient: str = None
downloadId: str = None
episodeId: int = None
id: int = None
indexer: str = None
language: dict = None
protocol: str = None
quality: dict = None
size: float = None
sizeleft: float = None
status: str = None
statusMessages: list = None
title: str = None
trackedDownloadState: str = None
trackedDownloadStatus: str = None
seriesId: int = None
# Radarr # Radarr
class RadarrMovie(NamedTuple): class RadarrMovie(NamedTuple):
added: str = None added: str = None
addOptions: str = None alternateTitles: list = None
alternativeTitles: list = None
certification: str = None certification: str = None
cleanTitle: str = None cleanTitle: str = None
downloaded: bool = None collection: dict = None
digitalRelease: str = None
folderName: str = None folderName: str = None
genres: list = None genres: list = None
hasFile: bool = None hasFile: bool = None
@ -221,32 +265,49 @@ class RadarrMovie(NamedTuple):
imdbId: str = None imdbId: str = None
inCinemas: str = None inCinemas: str = None
isAvailable: bool = None isAvailable: bool = None
lastInfoSync: str = None
minimumAvailability: str = None minimumAvailability: str = None
monitored: bool = None monitored: bool = None
movieFile: dict = None movieFile: dict = None
originalTitle: str = None
overview: str = None overview: str = None
path: str = None path: str = None
pathState: str = None
physicalRelease: str = None physicalRelease: str = None
physicalReleaseNote: str = None
profileId: int = None
qualityProfileId: int = None qualityProfileId: int = None
ratings: dict = None ratings: dict = None
runtime: int = None runtime: int = None
secondaryYear: str = None secondaryYear: int = None
secondaryYearSourceId: int = None secondaryYearSourceId: int = None
sizeOnDisk: int = None sizeOnDisk: float = None
sortTitle: str = None sortTitle: str = None
status: str = None status: str = None
studio: str = None studio: str = None
tags: list = None tags: list = None
title: str = None
titleSlug: str = None titleSlug: str = None
tmdbId: int = None tmdbId: int = None
website: str = None website: str = None
year: int = None year: int = None
youTubeTrailerId: str = None youTubeTrailerId: str = None
title: str = None
# Radarr Queue
class RadarrQueue(NamedTuple):
customFormats: list = None
downloadClient: str = None
downloadId: str = None
id: int = None
indexer: str = None
languages: list = None
movieId: int = None
protocol: str = None
quality: dict = None
size: float = None
sizeleft: float = None
status: str = None
statusMessages: list = None
title: str = None
trackedDownloadState: str = None
trackedDownloadStatus: str = None
# Sickchill # Sickchill