diff --git a/config.exemple.ini b/config.exemple.ini index 32e6beb..b10601c 100644 --- a/config.exemple.ini +++ b/config.exemple.ini @@ -1,7 +1,15 @@ [TAUTULLI] -SERVER_URL = https://tautulli.exemple.com/ +SERVER_URL = https://tautulli.exemple.com API_KEY = e18e33df8f7sdfae92d3a2344f2f60 +[RADARR] +SERVER_URL = https://radarr.exemple.com +API_KEY = e1werwrdfae92d3a2344f2f60 + +[SONARR] +SERVER_URL = https://sonarr.exemple.com +API_KEY = e18e33df8f7sdfasdfaf2f60 + [MEDIA_FILTER] ; Section_id de la librairie film à nettoyer film_section_ids = [1,2] diff --git a/plexwasher.py b/plexwasher.py index ed63055..6f6857a 100755 --- a/plexwasher.py +++ b/plexwasher.py @@ -11,6 +11,7 @@ import sys import requests from datetime import datetime from dateutil.relativedelta import relativedelta +import re class RequestError(Exception): pass @@ -18,8 +19,12 @@ class RequestError(Exception): class ApplicationError(Exception): pass -SERVER_URL = "" -API_KEY = "" +TAUTULLI_SERVER_URL = "" +TAUTULLI_API_KEY = "" +SONARR_SERVER_URL = "" +SONARR_API_KEY = "" +RADARR_SERVER_URL = "" +RADARR_API_KEY = "" FILM_SECTION_IDS = [] DEADLINE_NEVER_WATCHED = "" DEADLINE_LAST_WATCHED = "" @@ -35,8 +40,8 @@ def get_unwatched_rating_keys(sectionId): deadlineLastWacthed = datetime.now() - relativedelta(months=DEADLINE_LAST_WATCHED) # Send http request to refresh the list and get the number of media to safeguard from infinite loop - payload = {'apikey': API_KEY, 'cmd': 'get_library_media_info', 'section_id': sectionId, 'order_column': 'last_played', 'order_dir': 'asc','length': 1, 'start': 0, 'refresh ': 'true'} - request = requests.get(SERVER_URL+"api/v2", params=payload) + payload = {'apikey': TAUTULLI_API_KEY, 'cmd': 'get_library_media_info', 'section_id': sectionId, 'order_column': 'last_played', 'order_dir': 'asc','length': 1, 'start': 0, 'refresh ': 'true'} + request = requests.get(TAUTULLI_SERVER_URL+"/api/v2", params=payload) response = json.loads(request.content) if response["response"]['result'] == 'error': @@ -49,8 +54,8 @@ def get_unwatched_rating_keys(sectionId): recentlyWatched = False # Send http request to retrieve the next n=length items - payload = {'apikey': API_KEY, 'cmd': 'get_library_media_info', 'section_id': sectionId, 'order_column': 'last_played', 'order_dir': 'asc', 'length': length, 'start': start} - request = requests.get(SERVER_URL+"api/v2", params=payload) + payload = {'apikey': TAUTULLI_API_KEY, 'cmd': 'get_library_media_info', 'section_id': sectionId, 'order_column': 'last_played', 'order_dir': 'asc', 'length': length, 'start': start} + request = requests.get(TAUTULLI_SERVER_URL+"/api/v2", params=payload) response = json.loads(request.content) if response["response"]['result'] == 'error': @@ -88,8 +93,8 @@ def get_unwatched_rating_keys(sectionId): # Returns the path list of all the files composing the media def get_media_paths(rating_key): # Get the metadata of the media - payload = {'apikey': API_KEY, 'cmd': 'get_metadata', 'rating_key': rating_key} - request = requests.get(SERVER_URL+"api/v2", params=payload) + payload = {'apikey': TAUTULLI_API_KEY, 'cmd': 'get_metadata', 'rating_key': rating_key} + request = requests.get(TAUTULLI_SERVER_URL+"/api/v2", params=payload) response = json.loads(request.content) if response["response"]['result'] == 'error': @@ -103,8 +108,8 @@ def get_media_paths(rating_key): case "show" | "season": # If the media is a group of media we get the path of all its components # Getting the list of child - payload = {'apikey': API_KEY, 'cmd': 'get_children_metadata', 'rating_key': rating_key} - request = requests.get(SERVER_URL+"api/v2", params=payload) + payload = {'apikey': TAUTULLI_API_KEY, 'cmd': 'get_children_metadata', 'rating_key': rating_key} + request = requests.get(TAUTULLI_SERVER_URL+"/api/v2", params=payload) response = json.loads(request.content) if response["response"]['result'] == 'error': @@ -153,20 +158,87 @@ def get_and_store_files_to_remove(): for path in pathToRemove: f.write(f"{path}\n") +def delete_from_radarr(movies, path): + for movie in movies: + if path.contains(movie["path"]): + # Send http request to delete movie + payload = {'apikey': RADARR_SERVER_URL} + request = requests.delete(RADARR_SERVER_URL+"/api/v3/"+movie["id"], params=payload) + if request.status_code == 200: + logger.debug("Successfully deleted {}".format(movie["name"])) + return True + + logger.debug("Failed to delete {} using raddar".format(movie["name"])) + return False + +def delete_from_sonarr(name, deletedSeries, series): + if deletedSeries.contains(name): + logger.debug("Skipping deletion of {} as it has already been deleted".format(name)) + return True + + for serie in series: + if serie["title"] == name: + # Send http request to delete serie + payload = {'apikey': SONARR_SERVER_URL} + request = requests.delete(SONARR_SERVER_URL+"/api/v3/series/"+serie["id"], params=payload) + if request.status_code == 200: + deletedSeries.append(name) + logger.debug("Successfully deleted {}".format(name)) + return True + + logger.debug("Failed to delete {} using sonarr".format(name)) + return False + + # Delete all files and empty parent folder listed in an input file def delete_files(inputFile): + # Send http request to get the list of series + payload = {'apikey': SONARR_SERVER_URL} + request = requests.get(SONARR_SERVER_URL+"/api/v3/series", params=payload) + series = json.loads(request.content) + deletedSeries = [] + + # Send http request to get the list of movies + payload = {'apikey': RADARR_SERVER_URL} + request = requests.get(RADARR_SERVER_URL+"/api/v3/movie", params=payload) + movies = json.loads(request.content) + logger.info("Deleting all unwanted media files listed in \"{}\"".format(inputFile)) with open(inputFile, 'r') as f: for path in f: path = path.strip() logger.debug("Deleting file: " + path) + + # try to delete a serie with sonarr + try: + match = re.search(r'/Plex/Series/([^/]+)/', path) + if match: + logger.debug("{} matched series path pattern for '{}'".format(path, match.group(1))) + if delete_from_sonarr(match.group(1), deletedSeries, series): + continue + + except Exception as e: + logger.warning("Error while trying to remove with sonarr: " + e) + + # try to delete a movie with radarr + try: + match = re.search(r'/Plex/Films/([^/]+)/', path) + if match: + logger.debug("{} matched movies path pattern".format(path)) + if delete_from_radarr(path, movies): + continue + + except Exception as e: + logger.warning("Error while trying to remove with radarr: " + e) + + # failed to delete with sonarr/radarr so we delete the file directly + logger.debug("Failed to delete with sonarr/radarr. -> Deleting the file directly") os.remove(path) parent = os.path.split(path)[0] dir = os.listdir(parent) if len(dir) == 0: logger.debug("Deleting empty folder: " + parent) os.rmdir(parent) - ###### ----- main ----- ###### @@ -211,8 +283,18 @@ config_obj.read("config.ini") # -- Read TAUTULLI section -- serverParam = config_obj["TAUTULLI"] -SERVER_URL = serverParam["server_url"] -API_KEY = serverParam["api_key"] +TAUTULLI_SERVER_URL = serverParam["server_url"] +TAUTULLI_API_KEY = serverParam["api_key"] + +# -- Read RADARR section -- +serverParam = config_obj["RADARR"] +RADARR_SERVER_URL = serverParam["server_url"] +RADARR_API_KEY = serverParam["api_key"] + +# -- Read SONARR section -- +serverParam = config_obj["SONARR"] +SONARR_SERVER_URL = serverParam["server_url"] +SONARR_API_KEY = serverParam["api_key"] # -- Read MEDIA_FILTER section -- mediaParam = config_obj["MEDIA_FILTER"] @@ -221,7 +303,18 @@ DEADLINE_NEVER_WATCHED = int(mediaParam["deadline_never_watched"]) DEADLINE_LAST_WATCHED = int(mediaParam["deadline_last_watched"]) FILES_TO_KEEP = ast.literal_eval(mediaParam["files_to_ignore"]) -logger.debug("Loaded config values: \n SERVER_URL: {}\n API_KEY: hidden\n FILM_SECTION_IDS: {}\n DEADLINE_NEVER_WATCHED: {}\n DEADLINE_LAST_WATCHED: {}\n FILES_TO_KEEP: {}".format(SERVER_URL, FILM_SECTION_IDS, DEADLINE_NEVER_WATCHED, DEADLINE_LAST_WATCHED, FILES_TO_KEEP)) +logger.debug("Loaded config values: \n" \ + " TAUTULLI_SERVER_URL: {}\n" \ + " TAUTULLI_API_KEY: hidden\n" \ + " RADARR_SERVER_URL: {}\n" \ + " RADARR_API_KEY: hidden\n" \ + " SONARR_SERVER_URL: {}\n" \ + " SONARR_API_KEY: hidden\n" \ + " FILM_SECTION_IDS: {}\n" \ + " DEADLINE_NEVER_WATCHED: {}\n" \ + " DEADLINE_LAST_WATCHED: {}\n" \ + " FILES_TO_KEEP: {}" + .format(TAUTULLI_SERVER_URL,RADARR_SERVER_URL, SONARR_SERVER_URL, FILM_SECTION_IDS, DEADLINE_NEVER_WATCHED, DEADLINE_LAST_WATCHED, FILES_TO_KEEP)) ### --- Start logic --- ### diff --git a/res.json b/res.json new file mode 100644 index 0000000..4ac6a6e --- /dev/null +++ b/res.json @@ -0,0 +1,6 @@ +{ + "type": "https://tools.ietf.org/html/rfc7231#section-6.5.4", + "title": "Not Found", + "status": 404, + "traceId": "00-77d20c7dd3576429ff7f88d469bc985d-fe6e7027f3c5717b-00" +} \ No newline at end of file