#!/usr/bin/env python3 """ TV Show Directory Analyzer Scans a TV show directory structure and generates a CSV report with: - Show metadata - Season folder validation - Directory structure issues - Size metrics and ratios """ import os import sys import csv from pathlib import Path from collections import defaultdict def get_folder_size_gb(path): """Calculate total size of a folder in GB""" total_size = 0 try: for dirpath, dirnames, filenames in os.walk(path): for filename in filenames: filepath = os.path.join(dirpath, filename) try: total_size += os.path.getsize(filepath) except (OSError, FileNotFoundError): pass except (PermissionError, OSError): pass return total_size / (1024 ** 3) def count_episodes_in_folder(folder_path): """Count files in a folder (proxy for episode count)""" try: return len([f for f in os.listdir(folder_path) if os.path.isfile(os.path.join(folder_path, f))]) except (PermissionError, OSError): return 0 def is_season_folder(folder_name): """Check if folder name matches Season pattern""" name_lower = folder_name.lower().strip() # Matches: Season 1, season 1, Season1, S01, etc. if name_lower.startswith('season'): return True if name_lower.startswith('s') and len(name_lower) <= 4: try: int(name_lower[1:]) return True except ValueError: pass return False def is_featurette_folder(folder_name): """Check if folder appears to be featurettes""" name_lower = folder_name.lower() return 'featurette' in name_lower or 'special' in name_lower or 'bonus' in name_lower def analyze_show_directory(show_path): """ Analyze a single show directory Returns dict with analysis results """ show_name = os.path.basename(show_path) try: subdirs = [d for d in os.listdir(show_path) if os.path.isdir(os.path.join(show_path, d))] except (PermissionError, OSError) as e: return { 'show_name': show_name, 'total_size_gb': 0, 'season_folders': 0, 'non_season_folders': [], 'episode_count': 0, 'size_per_episode': 0, 'issues': f'Permission/Access Error: {str(e)}' } season_folders = [] non_season_folders = [] total_episodes = 0 for subdir in subdirs: subdir_path = os.path.join(show_path, subdir) if is_season_folder(subdir): season_folders.append(subdir) total_episodes += count_episodes_in_folder(subdir_path) else: non_season_folders.append(subdir) # Calculate size total_size_gb = get_folder_size_gb(show_path) # Calculate size per episode size_per_episode = total_size_gb / total_episodes if total_episodes > 0 else 0 # Identify issues issues = [] if not season_folders: issues.append("NO_SEASON_FOLDERS") non_flagged_issues = [f for f in non_season_folders if not is_featurette_folder(f)] if non_flagged_issues: issues.append(f"UNEXPECTED_FOLDERS: {', '.join(non_flagged_issues)}") featurette_folders = [f for f in non_season_folders if is_featurette_folder(f)] return { 'show_name': show_name, 'total_size_gb': round(total_size_gb, 2), 'season_folders': len(season_folders), 'season_folder_names': ', '.join(sorted(season_folders)) if season_folders else 'NONE', 'non_season_folders': ', '.join(non_season_folders) if non_season_folders else 'None', 'featurette_folders': ', '.join(featurette_folders) if featurette_folders else 'None', 'episode_count': total_episodes, 'size_per_episode': round(size_per_episode, 3), 'issues': '; '.join(issues) if issues else 'OK' } def main(): if len(sys.argv) > 1: directory = sys.argv[1] else: directory = input("Enter the path to your TV shows directory: ").strip() directory = os.path.expanduser(directory) if not os.path.isdir(directory): print(f"Error: Directory not found: {directory}") sys.exit(1) print(f"Scanning directory: {directory}") print("This may take a while for large collections...\n") # Get all show directories (first level subdirectories) try: show_folders = [d for d in os.listdir(directory) if os.path.isdir(os.path.join(directory, d))] except (PermissionError, OSError) as e: print(f"Error accessing directory: {e}") sys.exit(1) if not show_folders: print("No directories found in the specified path.") sys.exit(1) results = [] for i, show_folder in enumerate(sorted(show_folders), 1): show_path = os.path.join(directory, show_folder) print(f"[{i}/{len(show_folders)}] Processing: {show_folder}") analysis = analyze_show_directory(show_path) results.append(analysis) # Write to CSV output_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'show_analysis.csv') fieldnames = [ 'show_name', 'total_size_gb', 'season_folders', 'season_folder_names', 'non_season_folders', 'featurette_folders', 'episode_count', 'size_per_episode', 'issues' ] try: with open(output_file, 'w', newline='', encoding='utf-8') as csvfile: writer = csv.DictWriter(csvfile, fieldnames=fieldnames) writer.writeheader() writer.writerows(results) print(f"\nāœ“ Analysis complete!") print(f"āœ“ Results saved to: {output_file}") print(f"āœ“ Analyzed {len(results)} shows") except (PermissionError, IOError) as e: print(f"Error writing CSV file: {e}") sys.exit(1) if __name__ == '__main__': main()