KaraokeVideoDownloader/karaoke_downloader/tracking_cli.py

263 lines
8.6 KiB
Python

#!/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()