From 2c3bb5811f49dc8657bbbcaaf3e74da544c0d4ea Mon Sep 17 00:00:00 2001 From: Akiya Date: Sat, 7 Jun 2025 10:59:31 +0200 Subject: [PATCH] add show deletion --- config.exemple.ini | 2 +- plexwasher.py | 55 +++++++++++++++++++++++++++++++++------------- 2 files changed, 41 insertions(+), 16 deletions(-) diff --git a/config.exemple.ini b/config.exemple.ini index ea93b3a..462b701 100644 --- a/config.exemple.ini +++ b/config.exemple.ini @@ -4,7 +4,7 @@ API_KEY = e18e33df8f7sdfae92d3a2344f2f60 [MEDIA_FILTER] ; Section_id de la librairie film à nettoyer -film_section_ids = 1 +film_section_ids = [1,2] ; nb de mois avant suppression pour un film jamais regardé deadline_never_watched = 60 ; nb de mois avant suppression depuis le dernier visionage diff --git a/plexwasher.py b/plexwasher.py index e50961c..33b6df2 100755 --- a/plexwasher.py +++ b/plexwasher.py @@ -6,6 +6,7 @@ import traceback import argparse import json import os +import ast import sys import requests from datetime import datetime @@ -19,10 +20,10 @@ class ApplicationError(Exception): SERVER_URL = "" API_KEY = "" -FILM_SECTION_IDS = "" +FILM_SECTION_IDS = [] DEADLINE_NEVER_WATCHED = "" DEADLINE_LAST_WATCHED = "" -FILES_TO_KEEP = "" +FILES_TO_KEEP = [] # Retrieve the list of records that does not follow the deadline policies from tautulli def get_unwatched_rating_keys(sectionId): @@ -84,8 +85,9 @@ def get_unwatched_rating_keys(sectionId): return unwatched -# Get the file path of the media in the filesystem -def get_media_path(rating_key): +# 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) response = json.loads(request.content) @@ -94,10 +96,28 @@ def get_media_path(rating_key): raise RequestError("Could not request media path for {} : {}".format(rating_key, response["response"]['message'])) match response["response"]["data"]["media_type"]: - case "movie": - return response["response"]["data"]["media_info"][0]["parts"][0]["file"] - case "show": - raise ApplicationError("media path for shows are not implemented yet") # TODO: handle case for shows + case "movie" | "episode": + # If the media is a video file we return it as a list of single element + return [response["response"]["data"]["media_info"][0]["parts"][0]["file"]] + + 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) + response = json.loads(request.content) + + if response["response"]['result'] == 'error': + raise RequestError("Could not request media path for {} : {}".format(rating_key, response["response"]['message'])) + + # Get the path of all childs recursively + paths = [] + for media in response["response"]["data"]["children_list"]: + if media["rating_key"] == "": + continue + paths.extend(get_media_paths(media["rating_key"])) + return paths + case _: raise ApplicationError("Unkown media type for rating key {}: {} ".format(rating_key, response["response"]["data"]["media_type"])) @@ -107,15 +127,20 @@ def get_files_to_remove(unwatched): logger.info("Getting path of unwatched medias") pathToRemove = [] for media in unwatched: - path = get_media_path(media) - if path not in FILES_TO_KEEP: - pathToRemove.append(path) + paths = get_media_paths(media) + for path in paths: + if path not in FILES_TO_KEEP: + pathToRemove.append(path) return pathToRemove # writes the list of files selected for deletion in a file def get_and_store_files_to_remove(): logger.info("Getting list of rating id for unwatched medias") - unwatched = get_unwatched_rating_keys(FILM_SECTION_IDS) + unwatched = [] + for sectionId in FILM_SECTION_IDS: + logger.debug("Getting list for sectionID={}".format(sectionId)) + unwatched.extend(get_unwatched_rating_keys(sectionId)) + logger.debug("list of rating_key of unwatched medias: {}".format(unwatched)) pathToRemove = get_files_to_remove(unwatched) @@ -191,12 +216,12 @@ API_KEY = serverParam["api_key"] # -- Read MEDIA_FILTER section -- mediaParam = config_obj["MEDIA_FILTER"] -FILM_SECTION_IDS = int(mediaParam["film_section_ids"]) +FILM_SECTION_IDS = ast.literal_eval(mediaParam["film_section_ids"]) DEADLINE_NEVER_WATCHED = int(mediaParam["deadline_never_watched"]) DEADLINE_LAST_WATCHED = int(mediaParam["deadline_last_watched"]) -FILES_TO_KEEP = mediaParam["files_to_ignore"] +FILES_TO_KEEP = ast.literal_eval(mediaParam["files_to_ignore"]) -logger.debug("Loaded config values: \n SERVER_URL: {}\n API_KEY: hidden 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 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)) ### --- Start logic --- ###