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 | ||||
							
								
								
									
										401
									
								
								sonarr.py
									
										
									
									
									
								
							
							
						
						
									
										401
									
								
								sonarr.py
									
										
									
									
									
								
							|  | @ -1,3 +1,4 @@ | |||
| #!/usr/bin/env python3 | ||||
| # Do not edit this script. Edit configuration.py | ||||
| import sys | ||||
| import requests | ||||
|  | @ -5,257 +6,183 @@ 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( | ||||
|                 { | ||||
|                     "measurement": "Sonarr", | ||||
|                     "tags": { | ||||
|                         "type": "Missing", | ||||
|                         "sonarrId": sonarr_id, | ||||
|                         "server": server_id | ||||
|                     }, | ||||
|                     "time": now, | ||||
|                     "fields": { | ||||
|                         "name": series_title, | ||||
|                         "epname": episode_title, | ||||
|                         "sxe": sxe | ||||
|             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 | ||||
|                         }, | ||||
|                         "time": self.now, | ||||
|                         "fields": { | ||||
|                             "name": series_title, | ||||
|                             "epname": episode_title, | ||||
|                             "sxe": sxe, | ||||
|                             "airs": air_date | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             ) | ||||
|         # Empty missing or else things get foo bared | ||||
|         missing = [] | ||||
|                 ) | ||||
| 
 | ||||
|     return influx_payload | ||||
|     def get_upcoming(self): | ||||
|         endpoint = '/api/calendar/' | ||||
| 
 | ||||
|         for server in self.servers: | ||||
|             upcoming = [] | ||||
|             headers = {'X-Api-Key': server.api_key} | ||||
| 
 | ||||
| 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 = [], [] | ||||
|             get = self.session.get(server.url + endpoint, headers=headers).json() | ||||
|             tv_shows = [TVShow(**show) for show in get] | ||||
| 
 | ||||
|     for sonarr_url, sonarr_api_key, server_id in configuration.sonarr_server_list: | ||||
|             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)) | ||||
| 
 | ||||
|         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 | ||||
|             for series_title, sxe, sonarr_id, episode_title, air_date in upcoming: | ||||
|                 self.influx_payload.append( | ||||
|                     { | ||||
|                         "measurement": "Sonarr", | ||||
|                         "tags": { | ||||
|                             "type": "Soon", | ||||
|                             "sonarrId": sonarr_id, | ||||
|                             "server": server.id | ||||
|                         }, | ||||
|                         "time": self.now, | ||||
|                         "fields": { | ||||
|                             "name": series_title, | ||||
|                             "epname": episode_title, | ||||
|                             "sxe": sxe, | ||||
|                             "airs": air_date | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             ) | ||||
|                 ) | ||||
| 
 | ||||
|         # Empty missing or else things get foo bared | ||||
|         missing = [] | ||||
|     def get_future(self, future_days): | ||||
|         endpoint = '/api/calendar/' | ||||
|         future = str(date.today() + timedelta(days=future_days)) | ||||
| 
 | ||||
|     return influx_payload | ||||
|         for server in self.servers: | ||||
|             air_days = [] | ||||
|             headers = {'X-Api-Key': server.api_key} | ||||
|             params = {'start': self.today, 'end': future} | ||||
| 
 | ||||
|             get = self.session.get(server.url + endpoint, params=params, headers=headers).json() | ||||
|             tv_shows = [TVShow(**show) for show in get] | ||||
| 
 | ||||
| def get_upcoming_shows(): | ||||
|     # Set the time here so we have one timestamp to work with | ||||
|     now = now_iso() | ||||
|     upcoming = [] | ||||
|     influx_payload = [] | ||||
|             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 sonarr_url, sonarr_api_key, server_id in configuration.sonarr_server_list: | ||||
| 
 | ||||
|         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 series_title, sxe, sonarr_id, episode_title, air_date in upcoming: | ||||
|             influx_payload.append( | ||||
|                 { | ||||
|                     "measurement": "Sonarr", | ||||
|                     "tags": { | ||||
|                          "type": "Soon", | ||||
|                          "sonarrId": sonarr_id, | ||||
|                          "server": server_id | ||||
|                     }, | ||||
|                     "time": now, | ||||
|                     "fields": { | ||||
|                         "name": series_title, | ||||
|                         "epname": episode_title, | ||||
|                         "sxe": sxe, | ||||
|                         "airs": air_date | ||||
|             for series_title, dl_status, sxe, episode_title, air_date, sonarr_id in air_days: | ||||
|                 self.influx_payload.append( | ||||
|                     { | ||||
|                         "measurement": "Sonarr", | ||||
|                         "tags": { | ||||
|                             "type": "Future", | ||||
|                             "sonarrId": sonarr_id, | ||||
|                             "server": server.id | ||||
|                         }, | ||||
|                         "time": self.now, | ||||
|                         "fields": { | ||||
|                             "name": series_title, | ||||
|                             "epname": episode_title, | ||||
|                             "sxe": sxe, | ||||
|                             "airs": air_date, | ||||
|                             "downloaded": dl_status | ||||
|                         } | ||||
|                     } | ||||
|                  } | ||||
|             ) | ||||
|         # Empty upcoming or else things get foo bared | ||||
|         upcoming = [] | ||||
|                 ) | ||||
| 
 | ||||
|     return influx_payload | ||||
|     def get_queue(self): | ||||
|         endpoint = '/api/queue' | ||||
| 
 | ||||
|         for server in self.servers: | ||||
|             queue = [] | ||||
|             headers = {'X-Api-Key': server.api_key} | ||||
| 
 | ||||
| def get_future_shows(future_days): | ||||
|     # Set the time here so we have one timestamp to work with | ||||
|     now = now_iso() | ||||
|             get = self.session.get(server.url + endpoint, headers=headers).json() | ||||
|             download_queue = [Queue(**show) for show in get] | ||||
| 
 | ||||
|     today = str(date.today()) | ||||
|     future = str(date.today()+timedelta(days=future_days)) | ||||
|     air_days = [] | ||||
|     influx_payload = [] | ||||
|             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 | ||||
| 
 | ||||
|     for sonarr_url, sonarr_api_key, server_id in configuration.sonarr_server_list: | ||||
|                 queue.append((show.series['title'], show.episode['title'], show.protocol.upper(), | ||||
|                               protocol_id, sxe, show.id)) | ||||
| 
 | ||||
|         headers = {'X-Api-Key': sonarr_api_key} | ||||
|             for series_title, episode_title, protocol, protocol_id, sxe, sonarr_id in queue: | ||||
|                 self.influx_payload.append( | ||||
|                     { | ||||
|                         "measurement": "Sonarr", | ||||
|                         "tags": { | ||||
|                             "type": "Queue", | ||||
|                             "sonarrId": sonarr_id, | ||||
|                             "server": server.id | ||||
| 
 | ||||
|         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 series_title, dl_status, sxe, episode_title, air_date, sonarr_id in air_days: | ||||
|             influx_payload.append( | ||||
|                 { | ||||
|                     "measurement": "Sonarr", | ||||
|                     "tags": { | ||||
|                         "type": "Future", | ||||
|                         "sonarrId": sonarr_id, | ||||
|                         "server": server_id | ||||
|                     }, | ||||
|                     "time": now, | ||||
|                     "fields": { | ||||
|                         "name": series_title, | ||||
|                         "epname": episode_title, | ||||
|                         "sxe": sxe, | ||||
|                         "airs": air_date, | ||||
|                         "downloaded": dl_status | ||||
|                         }, | ||||
|                         "time": self.now, | ||||
|                         "fields": { | ||||
|                             "name": series_title, | ||||
|                             "epname": episode_title, | ||||
|                             "sxe": sxe, | ||||
|                             "protocol": protocol, | ||||
|                             "protocol_id": protocol_id | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             ) | ||||
|         # Empty air_days or else things get foo bared | ||||
|         air_days = [] | ||||
|                 ) | ||||
| 
 | ||||
|     return influx_payload | ||||
| 
 | ||||
| 
 | ||||
| def get_queue_shows(): | ||||
|     # Set the time here so we have one timestamp to work with | ||||
|     now = now_iso() | ||||
|     queue = [] | ||||
|     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/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': | ||||
|                 protocol_id = 1 | ||||
|             else: | ||||
|                 protocol_id = 0 | ||||
| 
 | ||||
|             queue.append((series_title, episode_title, protocol, protocol_id, sxe, tv_shows[show]['id'])) | ||||
| 
 | ||||
|         for series_title, episode_title, protocol, protocol_id, sxe, sonarr_id in queue: | ||||
|             influx_payload.append( | ||||
|                 { | ||||
|                     "measurement": "Sonarr", | ||||
|                     "tags": { | ||||
|                         "type": "Queue", | ||||
|                         "sonarrId": sonarr_id, | ||||
|                         "server": server_id | ||||
| 
 | ||||
|                     }, | ||||
|                     "time": now, | ||||
|                     "fields": { | ||||
|                         "name": series_title, | ||||
|                         "epname": episode_title, | ||||
|                         "sxe": sxe, | ||||
|                         "protocol": protocol, | ||||
|                         "protocol_id": protocol_id | ||||
|                     } | ||||
|                 } | ||||
|             ) | ||||
| 
 | ||||
|         # 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.' | ||||
|                                                    '\ni.e. --future 2 is Today and Tomorrow') | ||||
|     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…
	
	Add table
		Add a link
		
	
		Reference in a new issue