KaraokeVideoDownloader/karaoke_downloader/tracking_cli.py

259 lines
8.5 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 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()