#!/usr/bin/env python3 """ Tracking Management Utility for Karaoke Playlist Downloader Provides tools to manage and analyze the tracking database. """ import argparse import json from pathlib import Path from tracking_manager import TrackingManager, SongStatus import sys 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()