Compare commits
1 Commits
4cdd1ef457
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 4330cb984a |
@@ -1,144 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# Plex TV Shows Cleanup Script with Sonarr Integration for Unraid
|
|
||||||
|
|
||||||
# Configuration
|
|
||||||
PLEX_TOKEN="uZn42JMVkQpyb_duFsvT" # Replace with your Plex API token
|
|
||||||
PLEX_SERVER_URL="https://192.168.50.111:32400" # Replace with your Plex server URL
|
|
||||||
TV_SHOWS_PATH="/mnt/user/stuff/videos/tvshows" # Replace with your TV shows directory path
|
|
||||||
SONARR_API_KEY="2537de37fded4874ae83da9cf3c14f34" # Replace with your Sonarr API key
|
|
||||||
SONARR_URL="http://192.168.50.111:8989" # Replace with your Sonarr server URL
|
|
||||||
SHOWS_LIST=("Agatha All Along" "Arcane") # Replace with your TV show names
|
|
||||||
|
|
||||||
# Log File Configuration
|
|
||||||
LOG_DIR="/mnt/user/appdata/customlog" # Directory for log files
|
|
||||||
LOG_FILE="$LOG_DIR/plex_cleanup_$(date '+%Y-%m-%d').log" # Daily log file
|
|
||||||
|
|
||||||
# Ensure dependencies
|
|
||||||
# command -v curl >/dev/null 2>&1 || { echo "curl is not installed. Exiting."; exit 1; }
|
|
||||||
# command -v jq >/dev/null 2>&1 || { echo "jq is not installed. Exiting."; exit 1; }
|
|
||||||
|
|
||||||
# Ensure log directory exists
|
|
||||||
mkdir -p "$LOG_DIR"
|
|
||||||
|
|
||||||
# Function to log messages
|
|
||||||
log() {
|
|
||||||
local message="$1"
|
|
||||||
echo "$(date '+%Y-%m-%d %H:%M:%S') - $message" | tee -a "$LOG_FILE"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Function to fetch the Plex library section ID for TV Shows
|
|
||||||
get_tv_shows_section_id() {
|
|
||||||
log "Fetching TV Shows section ID"
|
|
||||||
local plex_section_id
|
|
||||||
plex_section_id=$(curl -s -X GET "$PLEX_SERVER_URL/library/sections" -H "X-Plex-Token: $PLEX_TOKEN" | \
|
|
||||||
jq -r '.MediaContainer.Directory[] | select(.type == "show") | .key')
|
|
||||||
|
|
||||||
|
|
||||||
if [[ -z "$plex_section_id" ]]; then
|
|
||||||
log "Could not find TV Shows section ID. Exiting."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "$plex_section_id"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Function to fetch watched status for a show
|
|
||||||
fetch_watched_episodes() {
|
|
||||||
local show_name="$1"
|
|
||||||
local plex_section_id="$2"
|
|
||||||
local show_id
|
|
||||||
local watched_episodes
|
|
||||||
|
|
||||||
log "Fetching watched episodes for: $show_name"
|
|
||||||
|
|
||||||
# Get the show ID for the specific show
|
|
||||||
show_id=$(curl -s -X GET "$PLEX_SERVER_URL/library/sections/$plex_section_id/all" -H "X-Plex-Token: $PLEX_TOKEN" | \
|
|
||||||
jq -r ".MediaContainer.Metadata[] | select(.title == \"$show_name\") | .ratingKey")
|
|
||||||
|
|
||||||
if [[ -z "$show_id" ]]; then
|
|
||||||
log "Could not find show ID for $show_name. Skipping."
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Fetch watched episodes for the specific show
|
|
||||||
watched_episodes=$(curl -s -X GET "$PLEX_SERVER_URL/library/metadata/$show_id/allLeaves" -H "X-Plex-Token: $PLEX_TOKEN" | \
|
|
||||||
jq '.MediaContainer.Metadata[] | select(.viewCount != null) | .title')
|
|
||||||
|
|
||||||
echo "$watched_episodes"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Function to delete old seasons and mark unmonitored in Sonarr
|
|
||||||
delete_and_unmonitor_seasons() {
|
|
||||||
local show_name="$1"
|
|
||||||
local show_path="$2"
|
|
||||||
|
|
||||||
log "Processing directory: $show_path"
|
|
||||||
|
|
||||||
for season_dir in "$show_path"/*; do
|
|
||||||
if [[ -d "$season_dir" ]]; then
|
|
||||||
season_name=$(basename "$season_dir")
|
|
||||||
|
|
||||||
if [[ "$season_name" =~ ^Season[[:space:]][[:digit:]]+$ ]]; then
|
|
||||||
log "Deleting watched season: $season_name"
|
|
||||||
rm -rf "$season_dir"
|
|
||||||
|
|
||||||
# Mark as unmonitored in Sonarr
|
|
||||||
season_number=$(echo "$season_name" | grep -oE '[0-9]+')
|
|
||||||
mark_unmonitored_in_sonarr "$show_name" "$season_number"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
# Function to mark a season as unmonitored in Sonarr
|
|
||||||
mark_unmonitored_in_sonarr() {
|
|
||||||
local show_name="$1"
|
|
||||||
local season_number="$2"
|
|
||||||
local series_id
|
|
||||||
local seasons_data
|
|
||||||
|
|
||||||
log "Marking $show_name - Season $season_number as unmonitored in Sonarr."
|
|
||||||
|
|
||||||
# Get the series ID
|
|
||||||
series_id=$(curl -s "$SONARR_URL/api/v3/series" -H "X-Api-Key: $SONARR_API_KEY" | \
|
|
||||||
jq -r ".[] | select(.title==\"$show_name\") | .id")
|
|
||||||
|
|
||||||
if [[ -z "$series_id" ]]; then
|
|
||||||
log "Could not find series ID for $show_name in Sonarr. Skipping."
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Fetch seasons data
|
|
||||||
seasons_data=$(curl -s "$SONARR_URL/api/v3/series/$series_id" -H "X-Api-Key: $SONARR_API_KEY")
|
|
||||||
|
|
||||||
# Update the monitored status for the specific season
|
|
||||||
updated_seasons=$(echo "$seasons_data" | jq ".seasons | map(if .seasonNumber == $season_number then .monitored = false else . end)")
|
|
||||||
|
|
||||||
# Send the updated data back to Sonarr
|
|
||||||
curl -s -X PUT "$SONARR_URL/api/v3/series/$series_id" \
|
|
||||||
-H "X-Api-Key: $SONARR_API_KEY" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
--data "$(echo "$seasons_data" | jq ".seasons = $updated_seasons")" > /dev/null
|
|
||||||
|
|
||||||
log "Marked $show_name - Season $season_number as unmonitored in Sonarr."
|
|
||||||
}
|
|
||||||
|
|
||||||
# Main Script Execution
|
|
||||||
log "Starting Plex TV Shows Cleanup with Sonarr Integration"
|
|
||||||
|
|
||||||
plex_section_id=$(get_tv_shows_section_id)
|
|
||||||
|
|
||||||
for show in "${SHOWS_LIST[@]}"; do
|
|
||||||
log "Checking show: $show"
|
|
||||||
watched=$(fetch_watched_episodes "$show" "$plex_section_id")
|
|
||||||
|
|
||||||
if [[ "$watched" -gt 0 ]]; then
|
|
||||||
log "Show '$show' has watched episodes. Cleaning up..."
|
|
||||||
show_path="$TV_SHOWS_PATH/$show"
|
|
||||||
delete_and_unmonitor_seasons "$show" "$show_path"
|
|
||||||
else
|
|
||||||
log "No watched episodes for '$show'. Skipping cleanup."
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
log "Plex TV Shows Cleanup with Sonarr Integration completed"
|
|
||||||
+137
@@ -0,0 +1,137 @@
|
|||||||
|
from plexapi.server import PlexServer
|
||||||
|
import requests
|
||||||
|
|
||||||
|
PLEX_TOKEN = "uZn42JMVkQpyb_duFsvT"
|
||||||
|
PLEX_SERVER_URL = "http://192.168.50.111:32400"
|
||||||
|
SONARR_API_KEY = "2537de37fded4874ae83da9cf3c14f34"
|
||||||
|
SONARR_SERVER_URL = "http://192.168.50.111:8989"
|
||||||
|
|
||||||
|
plex = PlexServer(PLEX_SERVER_URL, PLEX_TOKEN)
|
||||||
|
tv_shows = plex.library.section('TV Shows')
|
||||||
|
|
||||||
|
# List of TV show titles to exclude
|
||||||
|
exclude_shows = ["Stargate SG-1", "Space Sheriff Gavan", "Spider-Man and His Amazing Friends","Super Sentai", "Superman & Lois", "UFO Robot Grendizer","Zorro",
|
||||||
|
"Saved by the Bell: The College Years","Saber Rider and the Star Sheriffs","Prison Break","Power Rangers","The Outer Limits (1995)","MacGyver","Knight Rider",
|
||||||
|
"Chuck","Breaking Bad","Amazing Stories (1985)","Airwolf","The Adventures of Superboy (1988)"]
|
||||||
|
|
||||||
|
def unmonitor_all_excluded_shows():
|
||||||
|
for show_title in exclude_shows:
|
||||||
|
show = tv_shows.get(title=show_title)
|
||||||
|
print(f"Unmonitor Title: {show.title}")
|
||||||
|
tvdb_id = get_tvdb_id(show)
|
||||||
|
series_id = get_series_id_from_tvdb(tvdb_id)
|
||||||
|
seasons = show.seasons()
|
||||||
|
for season in seasons:
|
||||||
|
mark_season_unmonitored(series_id, season.index)
|
||||||
|
print(f"All seasons of '{show_title}' have been marked as unmonitored.")
|
||||||
|
|
||||||
|
|
||||||
|
# Function to check if the last 3 episodes of a season are watched
|
||||||
|
def last_3_episodes_watched(season):
|
||||||
|
episodes = sorted(season.episodes(), key=lambda ep: ep.index)
|
||||||
|
return all(ep.isWatched for ep in episodes[-3:])
|
||||||
|
|
||||||
|
# Function to check if Sonarr API is alive and the token is correct
|
||||||
|
def verify_sonarr_api():
|
||||||
|
url = f"{SONARR_SERVER_URL}/api/v3/system/status?apikey={SONARR_API_KEY}"
|
||||||
|
try:
|
||||||
|
response = requests.get(url)
|
||||||
|
response.raise_for_status()
|
||||||
|
print("Sonarr API is alive and the token is correct.")
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
print(f"Failed to verify Sonarr API: {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
# Function to get the series ID from the TVDB ID
|
||||||
|
def get_series_id_from_tvdb(tvdb_id):
|
||||||
|
print(f"Fetching series ID for TVDB ID: {tvdb_id}")
|
||||||
|
url = f"{SONARR_SERVER_URL}/api/v3/series/lookup?term=tvdb:{tvdb_id}&apikey={SONARR_API_KEY}"
|
||||||
|
response = requests.get(url)
|
||||||
|
response.raise_for_status()
|
||||||
|
series = response.json()
|
||||||
|
if series:
|
||||||
|
print(f"Series found: {series[0]['title']} (ID: {series[0]['id']})")
|
||||||
|
return series[0]['id']
|
||||||
|
else:
|
||||||
|
raise ValueError(f"No series found for TVDB ID: {tvdb_id}")
|
||||||
|
|
||||||
|
def get_series_id_from_tvdb(tvdb_id):
|
||||||
|
url = f"{SONARR_SERVER_URL}/api/v3/series/lookup?term=tvdb:{tvdb_id}&apikey={SONARR_API_KEY}"
|
||||||
|
response = requests.get(url)
|
||||||
|
response.raise_for_status()
|
||||||
|
series = response.json()
|
||||||
|
if series:
|
||||||
|
return series[0]['id']
|
||||||
|
else:
|
||||||
|
raise ValueError(f"No series found for TVDB ID: {tvdb_id}")
|
||||||
|
|
||||||
|
# Function to mark a season as unmonitored in Sonarr v4
|
||||||
|
def mark_season_unmonitored_bak(series_id, season_number):
|
||||||
|
url = f"{SONARR_SERVER_URL}/api/v3/series/{series_id}/season/{season_number}?apikey={SONARR_API_KEY}"
|
||||||
|
response = requests.put(url, json={"monitored": False})
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
def mark_season_unmonitored(series_id, season_number):
|
||||||
|
url = f"{SONARR_SERVER_URL}/api/v3/series/{series_id}?apikey={SONARR_API_KEY}"
|
||||||
|
response = requests.get(url)
|
||||||
|
# print(f"GET Response Status Code: {response.status_code}")
|
||||||
|
# print(f"GET Response Content: {response.content}")
|
||||||
|
response.raise_for_status()
|
||||||
|
series = response.json()
|
||||||
|
|
||||||
|
for season in series['seasons']:
|
||||||
|
if season['seasonNumber'] == season_number:
|
||||||
|
season['monitored'] = False
|
||||||
|
|
||||||
|
response = requests.put(url, json=series)
|
||||||
|
# print(f"PUT Response Status Code: {response.status_code}")
|
||||||
|
# print(f"PUT Response Content: {response.content}")
|
||||||
|
response.raise_for_status()
|
||||||
|
print(f"Season {season_number} marked as unmonitored for series ID {series_id}.")
|
||||||
|
|
||||||
|
|
||||||
|
def get_tvdb_id(show):
|
||||||
|
for guid in reversed(show.guids):
|
||||||
|
if guid.id.startswith("tvdb"):
|
||||||
|
return guid.id.split("://")[1]
|
||||||
|
raise ValueError(f"No series found for TVDB ID: {show.title}")
|
||||||
|
|
||||||
|
# Verify Sonarr API before proceeding
|
||||||
|
verify_sonarr_api()
|
||||||
|
|
||||||
|
# Call this function after processing the main shows
|
||||||
|
# unmonitor_all_excluded_shows()
|
||||||
|
|
||||||
|
# Iterate over all TV shows and apply the deletion rules
|
||||||
|
for show in tv_shows.all():
|
||||||
|
if show.title not in exclude_shows:
|
||||||
|
#print(f"TV Show: {show}")
|
||||||
|
|
||||||
|
print(f"Title: {show.title}")
|
||||||
|
#print(f"Year: {show.year}")
|
||||||
|
#print(f"Rating: {show.rating}")
|
||||||
|
#print(f"Summary: {show.summary}")
|
||||||
|
#print(f"Studio: {show.studio}")
|
||||||
|
# print(f"Actors: {', '.join(actor.tag for actor in show.actors)}")
|
||||||
|
#print(f"Seasons: {show.childCount}")
|
||||||
|
#print(f"Views: {show.viewCount}")
|
||||||
|
#print(f"Guid: {show.guid}")
|
||||||
|
#print("="*40)
|
||||||
|
|
||||||
|
seasons = sorted(show.seasons(), key=lambda s: s.index)
|
||||||
|
if len(seasons) > 1: # Ensure there is a previous season to delete
|
||||||
|
latest_season = seasons[-1]
|
||||||
|
if len(latest_season.episodes()) >= 3 and last_3_episodes_watched(latest_season):
|
||||||
|
# tvdb_id = show.guid.split('/')[-1]
|
||||||
|
tvdb_id = get_tvdb_id(show)
|
||||||
|
print(f"TVDB ID: {tvdb_id}")
|
||||||
|
series_id = get_series_id_from_tvdb(tvdb_id)
|
||||||
|
for season in seasons[:-1]: # Excluding the latest season
|
||||||
|
# Mark the season as unmonitored in Sonarr v4 before deleting
|
||||||
|
mark_season_unmonitored(series_id, season.index)
|
||||||
|
print(f" - Marking Season {season.index} as unmonitored and ready to delete")
|
||||||
|
# season.delete()
|
||||||
|
else:
|
||||||
|
for episode in latest_season.episodes():
|
||||||
|
if episode.isWatched:
|
||||||
|
print(f" - Watched Episode: {episode.title}")
|
||||||
Reference in New Issue
Block a user