Created
December 25, 2025 22:54
-
-
Save fffonion/723ac0cfbde4cd69c992c33dce08e035 to your computer and use it in GitHub Desktop.
Jellyfin Season Verification
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import requests | |
| import sys | |
| from datetime import datetime | |
| # Configuration | |
| JELLYFIN_URL = "http://jellyfin:8096" # Change to your Jellyfin server URL | |
| API_KEY = "123456789" # Create one from Dashboard -> API keys | |
| LOG_FILE = "jellyfin_invalid_seasons.log" | |
| def log_message(message, log_file=LOG_FILE): | |
| """Write message to both console and log file.""" | |
| timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") | |
| log_entry = f"[{timestamp}] {message}" | |
| print(log_entry) | |
| with open(log_file, "a", encoding="utf-8") as f: | |
| f.write(log_entry + "\n") | |
| def get_all_series(base_url, api_key): | |
| """Fetch all TV series from Jellyfin.""" | |
| endpoint = f"{base_url}/Items" | |
| params = { | |
| "api_key": api_key, | |
| "IncludeItemTypes": "Series", | |
| "Recursive": "true", | |
| "Fields": "Path" | |
| } | |
| try: | |
| response = requests.get(endpoint, params=params) | |
| response.raise_for_status() | |
| return response.json().get("Items", []) | |
| except requests.exceptions.RequestException as e: | |
| log_message(f"ERROR: Failed to fetch series: {e}") | |
| sys.exit(1) | |
| def get_seasons_for_series(base_url, api_key, series_id): | |
| """Get all seasons for a specific series.""" | |
| endpoint = f"{base_url}/Shows/{series_id}/Seasons" | |
| params = {"api_key": api_key} | |
| try: | |
| response = requests.get(endpoint, params=params) | |
| response.raise_for_status() | |
| return response.json().get("Items", []) | |
| except requests.exceptions.RequestException as e: | |
| log_message(f"ERROR: Failed to fetch seasons for series {series_id}: {e}") | |
| return [] | |
| def check_season_exists(base_url, api_key, series_id, season_id): | |
| """Check if a season ID actually exists in Jellyfin.""" | |
| endpoint = f"{base_url}/Shows/{series_id}/Episodes" | |
| params = {"api_key": api_key, "seasonId": season_id, "limit": 1} | |
| try: | |
| response = requests.get(endpoint, params=params) | |
| if response.status_code == 200: | |
| return True, response.json() | |
| elif response.status_code == 404: | |
| return False, None | |
| elif response.status_code == 500: | |
| return False, None | |
| else: | |
| response.raise_for_status() | |
| return True, response.json() | |
| except requests.exceptions.RequestException as e: | |
| log_message(f"ERROR: Failed to verify season {season_id}: {e}") | |
| return None, None | |
| def main(): | |
| # Initialize log file | |
| with open(LOG_FILE, "w", encoding="utf-8") as f: | |
| f.write(f"Jellyfin Season Validation Log - Started at {datetime.now()}\n") | |
| f.write("=" * 80 + "\n\n") | |
| log_message("Starting Jellyfin season validation...") | |
| log_message(f"Server: {JELLYFIN_URL}\n") | |
| # Fetch all series | |
| all_series = get_all_series(JELLYFIN_URL, API_KEY) | |
| if not all_series: | |
| log_message("No TV series found on the server.") | |
| return | |
| log_message(f"Found {len(all_series)} TV series. Validating seasons...\n") | |
| total_seasons = 0 | |
| invalid_seasons = 0 | |
| problematic_shows = [] | |
| # Process each series | |
| for idx, series in enumerate(all_series, 1): | |
| series_id = series.get("Id") | |
| series_name = series.get("Name") | |
| series_path = series.get("Path", "N/A") | |
| print(f"[{idx}/{len(all_series)}] Checking: {series_name}") | |
| seasons = get_seasons_for_series(JELLYFIN_URL, API_KEY, series_id) | |
| if not seasons: | |
| continue | |
| show_has_issues = False | |
| invalid_season_list = [] | |
| for season in seasons: | |
| season_id = season.get("Id") | |
| season_name = season.get("Name", "Unknown Season") | |
| total_seasons += 1 | |
| exists, season_data = check_season_exists(JELLYFIN_URL, API_KEY, series_id, season_id) | |
| if exists is False: | |
| invalid_seasons += 1 | |
| show_has_issues = True | |
| invalid_season_list.append({ | |
| "season_id": season_id, | |
| "season_name": season_name | |
| }) | |
| log_message(f" ✗ INVALID: Season ID {season_id} ({season_name}) does not exist!") | |
| elif exists is None: | |
| show_has_issues = True | |
| invalid_season_list.append({ | |
| "season_id": season_id, | |
| "season_name": season_name | |
| }) | |
| log_message(f" ? ERROR: Could not verify season ID {season_id} ({season_name})") | |
| if show_has_issues: | |
| problematic_shows.append({ | |
| "show_id": series_id, | |
| "show_name": series_name, | |
| "show_path": series_path, | |
| "invalid_seasons": invalid_season_list | |
| }) | |
| # Summary | |
| log_message("\n" + "=" * 80) | |
| log_message("VALIDATION SUMMARY") | |
| log_message("=" * 80) | |
| log_message(f"Total TV Shows: {len(all_series)}") | |
| log_message(f"Total Seasons Checked: {total_seasons}") | |
| log_message(f"Invalid/Missing Seasons: {invalid_seasons}") | |
| log_message(f"Shows with Issues: {len(problematic_shows)}") | |
| if problematic_shows: | |
| log_message("\n" + "=" * 80) | |
| log_message("PROBLEMATIC SHOWS DETAILS") | |
| log_message("=" * 80) | |
| for show in problematic_shows: | |
| log_message(f"\nShow: {show['show_name']}") | |
| log_message(f" Show ID: {show['show_id']}") | |
| log_message(f" Path: {show['show_path']}") | |
| log_message(f" Invalid Seasons:") | |
| for season in show['invalid_seasons']: | |
| log_message(f" - {season['season_name']} (ID: {season['season_id']})") | |
| else: | |
| log_message("\n✓ All seasons validated successfully!") | |
| log_message(f"\nLog saved to: {LOG_FILE}") | |
| if __name__ == "__main__": | |
| # Check if requests is installed | |
| try: | |
| import requests | |
| except ImportError: | |
| print("Error: 'requests' library is not installed.") | |
| print("Install it using: pip install requests") | |
| sys.exit(1) | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment