Add Overseerr Support (#210)
This commit is contained in:
		
							parent
							
								
									9498a83bc8
								
							
						
					
					
						commit
						73b2686ba0
					
				
					 5 changed files with 312 additions and 3 deletions
				
			
		
							
								
								
									
										16
									
								
								Varken.py
									
										
									
									
									
								
							
							
						
						
									
										16
									
								
								Varken.py
									
										
									
									
									
								
							|  | @ -14,6 +14,7 @@ from logging import getLogger, StreamHandler, Formatter, DEBUG | ||||||
| # Needed to check version of python | # Needed to check version of python | ||||||
| from varken import structures  # noqa | from varken import structures  # noqa | ||||||
| from varken.ombi import OmbiAPI | from varken.ombi import OmbiAPI | ||||||
|  | from varken.overseerr import OverseerrAPI | ||||||
| from varken.unifi import UniFiAPI | from varken.unifi import UniFiAPI | ||||||
| from varken import VERSION, BRANCH, BUILD_DATE | from varken import VERSION, BRANCH, BUILD_DATE | ||||||
| from varken.sonarr import SonarrAPI | from varken.sonarr import SonarrAPI | ||||||
|  | @ -156,6 +157,21 @@ if __name__ == "__main__": | ||||||
|                 at_time = schedule.every(server.issue_status_run_seconds).seconds |                 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)) |                 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: |     if CONFIG.sickchill_enabled: | ||||||
|         for server in CONFIG.sickchill_servers: |         for server in CONFIG.sickchill_servers: | ||||||
|             SICKCHILL = SickChillAPI(server, DBMANAGER) |             SICKCHILL = SickChillAPI(server, DBMANAGER) | ||||||
|  |  | ||||||
|  | @ -3,7 +3,8 @@ sonarr_server_ids = 1,2 | ||||||
| radarr_server_ids = 1,2 | radarr_server_ids = 1,2 | ||||||
| lidarr_server_ids = false | lidarr_server_ids = false | ||||||
| tautulli_server_ids = 1 | tautulli_server_ids = 1 | ||||||
| ombi_server_ids = 1 | ombi_server_ids = false | ||||||
|  | overseerr_server_ids = 1 | ||||||
| sickchill_server_ids = false | sickchill_server_ids = false | ||||||
| unifi_server_ids = false | unifi_server_ids = false | ||||||
| maxmind_license_key = xxxxxxxxxxxxxxxx | maxmind_license_key = xxxxxxxxxxxxxxxx | ||||||
|  | @ -95,6 +96,19 @@ request_total_run_seconds = 300 | ||||||
| get_issue_status_counts = true | get_issue_status_counts = true | ||||||
| issue_status_run_seconds = 300 | 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] | [sickchill-1] | ||||||
| url = sickchill.domain.tld:8081 | url = sickchill.domain.tld:8081 | ||||||
| apikey = xxxxxxxxxxxxxxxx | apikey = xxxxxxxxxxxxxxxx | ||||||
|  |  | ||||||
|  | @ -9,7 +9,7 @@ from configparser import ConfigParser, NoOptionError, NoSectionError | ||||||
| from varken.varkenlogger import BlacklistFilter | from varken.varkenlogger import BlacklistFilter | ||||||
| from varken.structures import SickChillServer, UniFiServer | from varken.structures import SickChillServer, UniFiServer | ||||||
| from varken.helpers import clean_sid_check, rfc1918_ip_check, boolcheck | 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): | class INIParser(object): | ||||||
|  | @ -17,7 +17,7 @@ class INIParser(object): | ||||||
|         self.config = None |         self.config = None | ||||||
|         self.data_folder = data_folder |         self.data_folder = data_folder | ||||||
|         self.filtered_strings = None |         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.logger = getLogger() | ||||||
|         self.influx_server = InfluxServer() |         self.influx_server = InfluxServer() | ||||||
|  | @ -293,6 +293,38 @@ class INIParser(object): | ||||||
|                                                 issue_status_counts=issue_status_counts, |                                                 issue_status_counts=issue_status_counts, | ||||||
|                                                 issue_status_run_seconds=issue_status_run_seconds) |                                                 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': |                         if service == 'sickchill': | ||||||
|                             get_missing = boolcheck(env.get(f'VRKN_{envsection}_GET_MISSING', |                             get_missing = boolcheck(env.get(f'VRKN_{envsection}_GET_MISSING', | ||||||
|                                                             self.config.get(section, 'get_missing'))) |                                                             self.config.get(section, 'get_missing'))) | ||||||
|  |  | ||||||
							
								
								
									
										179
									
								
								varken/overseerr.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										179
									
								
								varken/overseerr.py
									
										
									
									
									
										Normal 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) | ||||||
|  | @ -57,6 +57,20 @@ class OmbiServer(NamedTuple): | ||||||
|     verify_ssl: bool = False |     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): | class TautulliServer(NamedTuple): | ||||||
|     api_key: str = None |     api_key: str = None | ||||||
|     fallback_ip: str = None |     fallback_ip: str = None | ||||||
|  | @ -173,6 +187,60 @@ class OmbiMovieRequest(NamedTuple): | ||||||
|     requestStatus: str = None |     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 | # Sonarr | ||||||
| class SonarrTVShow(NamedTuple): | class SonarrTVShow(NamedTuple): | ||||||
|     added: str = None |     added: str = None | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue