#!/usr/bin/env python3 """ Tracking Management Utility for Karaoke Playlist Downloader Provides tools to manage and analyze the tracking database. """ import argparse import sys import json from pathlib import Path from tracking_manager import SongStatus, TrackingManager def show_statistics(tracker): """Show overall statistics.""" stats = tracker.get_statistics() print("🎀 Karaoke Downloader Statistics") print("=" * 50) print(f"πŸ“Š Total Songs: {stats['total_songs']}") print(f"πŸ“ Total Playlists: {stats['total_playlists']}") print(f"βœ… Downloaded Songs: {stats['downloaded_songs']}") print(f"❌ Failed Songs: {stats['failed_songs']}") print(f"⚠️ Partial Downloads: {stats['partial_songs']}") print(f"πŸ’Ύ Total Size: {stats['total_size_mb']} MB") print(f"πŸ•’ Last Updated: {stats['last_updated']}") if stats["total_songs"] > 0: success_rate = (stats["downloaded_songs"] / stats["total_songs"]) * 100 print(f"πŸ“ˆ Success Rate: {success_rate:.1f}%") def list_playlists(tracker): """List all playlists with their statistics.""" playlists = tracker.data["playlists"] if not playlists: print("πŸ“­ No playlists found in tracking database.") return print("πŸ“‹ Playlists in Database") print("=" * 50) for playlist_id, playlist in playlists.items(): print(f"\n🎡 {playlist['name']}") print(f" ID: {playlist_id}") print(f" URL: {playlist['url']}") print(f" Songs: {playlist['total_songs']}") print(f" Downloaded: {playlist['downloaded_songs']}") print(f" Failed: {playlist['failed_songs']}") print(f" Added: {playlist['added_date']}") print(f" Last Synced: {playlist['last_synced'] or 'Never'}") def show_playlist_details(tracker, playlist_id): """Show detailed information about a specific playlist.""" if playlist_id not in tracker.data["playlists"]: print(f"❌ Playlist '{playlist_id}' not found in tracking database.") return playlist = tracker.data["playlists"][playlist_id] songs = tracker.get_playlist_songs(playlist_id) print(f"🎡 Playlist Details: {playlist['name']}") print("=" * 50) print(f"ID: {playlist_id}") print(f"URL: {playlist['url']}") print(f"Total Songs: {playlist['total_songs']}") print(f"Downloaded: {playlist['downloaded_songs']}") print(f"Failed: {playlist['failed_songs']}") print(f"Added: {playlist['added_date']}") print(f"Last Synced: {playlist['last_synced'] or 'Never'}") print(f"\nπŸ“ Songs:") for i, song in enumerate(songs, 1): status_icon = { SongStatus.DOWNLOADED: "βœ…", SongStatus.CONVERTED: "βœ…", SongStatus.DOWNLOADING: "⏳", SongStatus.PARTIAL: "⚠️", SongStatus.FAILED: "❌", SongStatus.NOT_DOWNLOADED: "⏸️", }.get(song["status"], "❓") formats = ", ".join(song["formats"].keys()) if song["formats"] else "None" print(f" {i:2d}. {status_icon} {song['title']}") print(f" Video ID: {song['video_id']}") print(f" Status: {song['status']}") print(f" Formats: {formats}") if song["last_error"]: print(f" Error: {song['last_error']}") print() def show_failed_songs(tracker, playlist_id=None): """Show all failed songs.""" if playlist_id: songs = tracker.get_failed_songs(playlist_id) if not songs: print(f"βœ… No failed songs found in playlist '{playlist_id}'.") return print(f"❌ Failed Songs in Playlist: {playlist_id}") else: songs = [ song for song in tracker.data["songs"].values() if song["status"] == SongStatus.FAILED ] if not songs: print("βœ… No failed songs found in any playlist.") return print("❌ All Failed Songs") print("=" * 50) for song in songs: playlist_name = tracker.data["playlists"][song["playlist_id"]]["name"] print(f"\n🎡 {song['title']}") print(f" Playlist: {playlist_name}") print(f" Video ID: {song['video_id']}") print(f" Attempts: {song['download_attempts']}") print(f" Error: {song['last_error']}") print(f" Last Updated: {song['last_updated']}") def show_partial_downloads(tracker, playlist_id=None): """Show all partial downloads.""" if playlist_id: songs = tracker.get_partial_downloads(playlist_id) if not songs: print(f"βœ… No partial downloads found in playlist '{playlist_id}'.") return print(f"⚠️ Partial Downloads in Playlist: {playlist_id}") else: songs = [ song for song in tracker.data["songs"].values() if song["status"] == SongStatus.PARTIAL ] if not songs: print("βœ… No partial downloads found in any playlist.") return print("⚠️ All Partial Downloads") print("=" * 50) for song in songs: playlist_name = tracker.data["playlists"][song["playlist_id"]]["name"] print(f"\n🎡 {song['title']}") print(f" Playlist: {playlist_name}") print(f" Video ID: {song['video_id']}") print(f" Formats: {', '.join(song['formats'].keys())}") print(f" Last Updated: {song['last_updated']}") def cleanup_orphaned_entries(tracker, downloads_dir): """Clean up orphaned tracking entries.""" orphaned = tracker.cleanup_orphaned_files(downloads_dir) if orphaned: print(f"🧹 Cleaned up {len(orphaned)} orphaned tracking entries:") for song_id in orphaned: song = tracker.data["songs"].get(song_id) if song: print(f" - {song['title']} (ID: {song['video_id']})") else: print("βœ… No orphaned entries found.") def export_database(tracker, output_file): """Export the tracking database to a JSON file.""" try: with open(output_file, "w", encoding="utf-8") as f: json.dump(tracker.data, f, indent=2, ensure_ascii=False) print(f"πŸ’Ύ Database exported to: {output_file}") except Exception as e: print(f"❌ Failed to export database: {e}") def main(): parser = argparse.ArgumentParser( description="Tracking Management Utility for Karaoke Playlist Downloader", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" Examples: python manage_tracking.py --stats python manage_tracking.py --list-playlists python manage_tracking.py --playlist PLAYLIST_ID python manage_tracking.py --failed python manage_tracking.py --partial python manage_tracking.py --cleanup python manage_tracking.py --export backup.json """, ) parser.add_argument( "--stats", "--statistics", action="store_true", help="Show overall statistics" ) parser.add_argument( "--list-playlists", action="store_true", help="List all playlists in the database", ) parser.add_argument( "--playlist", metavar="PLAYLIST_ID", help="Show detailed information about a specific playlist", ) parser.add_argument("--failed", action="store_true", help="Show all failed songs") parser.add_argument( "--partial", action="store_true", help="Show all partial downloads" ) parser.add_argument( "--cleanup", action="store_true", help="Clean up orphaned tracking entries" ) parser.add_argument( "--export", metavar="FILE", help="Export tracking database to JSON file" ) parser.add_argument( "--tracking-file", default="karaoke_tracking.json", help="Path to tracking database file (default: karaoke_tracking.json)", ) args = parser.parse_args() # Initialize tracking manager tracker = TrackingManager(args.tracking_file) # Process commands if args.stats: show_statistics(tracker) elif args.list_playlists: list_playlists(tracker) elif args.playlist: show_playlist_details(tracker, args.playlist) elif args.failed: show_failed_songs(tracker) elif args.partial: show_partial_downloads(tracker) elif args.cleanup: downloads_dir = Path("downloads") cleanup_orphaned_entries(tracker, downloads_dir) elif args.export: export_database(tracker, args.export) else: parser.print_help() sys.exit(1) if __name__ == "__main__": main()