Files
Aniworld/src/cli/nfo_cli.py
Lukas 1c476003d6 feat: Add 'update' command to NFO CLI tool
- New command: python -m src.cli.nfo_cli update
- Updates all existing NFO files with fresh TMDB data
- Optionally re-downloads media files
- Shows progress with success/error count
- Updates task3_status.md to mark update_tvshow_nfo() as complete
2026-01-11 21:11:49 +01:00

282 lines
9.6 KiB
Python

"""CLI command for NFO management.
This script provides command-line interface for creating, updating,
and checking NFO metadata files.
"""
import asyncio
import sys
from pathlib import Path
# Add src to path
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
from src.config.settings import settings
from src.core.services.series_manager_service import SeriesManagerService
async def scan_and_create_nfo():
"""Scan all series and create missing NFO files."""
print("=" * 70)
print("NFO Auto-Creation Tool")
print("=" * 70)
if not settings.tmdb_api_key:
print("\n❌ Error: TMDB_API_KEY not configured")
print(" Set TMDB_API_KEY in .env file or environment")
print(" Get API key from: https://www.themoviedb.org/settings/api")
return 1
if not settings.anime_directory:
print("\n❌ Error: ANIME_DIRECTORY not configured")
return 1
print(f"\nAnime Directory: {settings.anime_directory}")
print(f"Auto-create NFO: {settings.nfo_auto_create}")
print(f"Update on scan: {settings.nfo_update_on_scan}")
print(f"Download poster: {settings.nfo_download_poster}")
print(f"Download logo: {settings.nfo_download_logo}")
print(f"Download fanart: {settings.nfo_download_fanart}")
if not settings.nfo_auto_create:
print("\n⚠️ Warning: NFO_AUTO_CREATE is set to False")
print(" Enable it in .env to auto-create NFO files")
print("\n Continuing anyway to demonstrate functionality...")
# Override for demonstration
settings.nfo_auto_create = True
print("\nInitializing series manager...")
manager = SeriesManagerService.from_settings()
# Get series list first
serie_list = manager.get_serie_list()
all_series = serie_list.get_all()
print(f"Found {len(all_series)} series in directory")
if not all_series:
print("\n⚠️ No series found. Add some anime series first.")
return 0
# Show series without NFO
series_without_nfo = []
for serie in all_series:
if not serie.has_nfo():
series_without_nfo.append(serie)
if series_without_nfo:
print(f"\nSeries without NFO: {len(series_without_nfo)}")
for serie in series_without_nfo[:5]: # Show first 5
print(f" - {serie.name} ({serie.folder})")
if len(series_without_nfo) > 5:
print(f" ... and {len(series_without_nfo) - 5} more")
else:
print("\n✅ All series already have NFO files!")
if not settings.nfo_update_on_scan:
print("\nNothing to do. Enable NFO_UPDATE_ON_SCAN to update existing NFOs.")
return 0
print("\nProcessing NFO files...")
print("(This may take a while depending on the number of series)")
try:
await manager.scan_and_process_nfo()
print("\n✅ NFO processing complete!")
# Show updated stats
serie_list.load_series() # Reload to get updated stats
all_series = serie_list.get_all()
series_with_nfo = [s for s in all_series if s.has_nfo()]
series_with_poster = [s for s in all_series if s.has_poster()]
series_with_logo = [s for s in all_series if s.has_logo()]
series_with_fanart = [s for s in all_series if s.has_fanart()]
print("\nFinal Statistics:")
print(f" Series with NFO: {len(series_with_nfo)}/{len(all_series)}")
print(f" Series with poster: {len(series_with_poster)}/{len(all_series)}")
print(f" Series with logo: {len(series_with_logo)}/{len(all_series)}")
print(f" Series with fanart: {len(series_with_fanart)}/{len(all_series)}")
except Exception as e:
print(f"\n❌ Error: {e}")
import traceback
traceback.print_exc()
return 1
finally:
await manager.close()
return 0
async def check_nfo_status():
"""Check NFO status for all series."""
print("=" * 70)
print("NFO Status Check")
print("=" * 70)
if not settings.anime_directory:
print("\n❌ Error: ANIME_DIRECTORY not configured")
return 1
print(f"\nAnime Directory: {settings.anime_directory}")
# Create series list (no NFO service needed for status check)
from src.core.entities.SerieList import SerieList
serie_list = SerieList(settings.anime_directory)
all_series = serie_list.get_all()
if not all_series:
print("\n⚠️ No series found")
return 0
print(f"\nTotal series: {len(all_series)}")
# Categorize series
with_nfo = []
without_nfo = []
for serie in all_series:
if serie.has_nfo():
with_nfo.append(serie)
else:
without_nfo.append(serie)
print(f"\nWith NFO: {len(with_nfo)} ({len(with_nfo) * 100 // len(all_series)}%)")
print(f"Without NFO: {len(without_nfo)} ({len(without_nfo) * 100 // len(all_series)}%)")
if without_nfo:
print("\nSeries missing NFO:")
for serie in without_nfo[:10]:
print(f"{serie.name} ({serie.folder})")
if len(without_nfo) > 10:
print(f" ... and {len(without_nfo) - 10} more")
# Media file statistics
with_poster = sum(1 for s in all_series if s.has_poster())
with_logo = sum(1 for s in all_series if s.has_logo())
with_fanart = sum(1 for s in all_series if s.has_fanart())
print("\nMedia Files:")
print(f" Posters: {with_poster}/{len(all_series)} ({with_poster * 100 // len(all_series)}%)")
print(f" Logos: {with_logo}/{len(all_series)} ({with_logo * 100 // len(all_series)}%)")
print(f" Fanart: {with_fanart}/{len(all_series)} ({with_fanart * 100 // len(all_series)}%)")
return 0
async def update_nfo_files():
"""Update existing NFO files with fresh data from TMDB."""
print("=" * 70)
print("NFO Update Tool")
print("=" * 70)
if not settings.tmdb_api_key:
print("\n❌ Error: TMDB_API_KEY not configured")
print(" Set TMDB_API_KEY in .env file or environment")
print(" Get API key from: https://www.themoviedb.org/settings/api")
return 1
if not settings.anime_directory:
print("\n❌ Error: ANIME_DIRECTORY not configured")
return 1
print(f"\nAnime Directory: {settings.anime_directory}")
print(f"Download media: {settings.nfo_download_poster or settings.nfo_download_logo or settings.nfo_download_fanart}")
# Get series with NFO
from src.core.entities.SerieList import SerieList
serie_list = SerieList(settings.anime_directory)
all_series = serie_list.get_all()
series_with_nfo = [s for s in all_series if s.has_nfo()]
if not series_with_nfo:
print("\n⚠️ No series with NFO files found")
print(" Run 'scan' command first to create NFO files")
return 0
print(f"\nFound {len(series_with_nfo)} series with NFO files")
print("Updating NFO files with fresh data from TMDB...")
print("(This may take a while)")
# Initialize NFO service
from src.core.services.nfo_service import NFOService
nfo_service = NFOService(
tmdb_api_key=settings.tmdb_api_key,
anime_directory=settings.anime_directory,
image_size=settings.nfo_image_size
)
success_count = 0
error_count = 0
try:
for i, serie in enumerate(series_with_nfo, 1):
print(f"\n[{i}/{len(series_with_nfo)}] Updating: {serie.name}")
try:
await nfo_service.update_tvshow_nfo(
serie_folder=serie.folder,
download_media=(
settings.nfo_download_poster or
settings.nfo_download_logo or
settings.nfo_download_fanart
)
)
print(f" ✅ Updated successfully")
success_count += 1
# Small delay to respect API rate limits
await asyncio.sleep(0.5)
except Exception as e:
print(f" ❌ Error: {e}")
error_count += 1
print("\n" + "=" * 70)
print(f"✅ Update complete!")
print(f" Success: {success_count}")
print(f" Errors: {error_count}")
except Exception as e:
print(f"\n❌ Fatal error: {e}")
import traceback
traceback.print_exc()
return 1
finally:
await nfo_service.close()
return 0
def main():
"""Main CLI entry point."""
if len(sys.argv) < 2:
print("NFO Management Tool")
print("\nUsage:")
print(" python -m src.cli.nfo_cli scan # Scan and create missing NFO files")
print(" python -m src.cli.nfo_cli status # Check NFO status for all series")
print(" python -m src.cli.nfo_cli update # Update existing NFO files with fresh data")
print("\nConfiguration:")
print(" Set TMDB_API_KEY in .env file")
print(" Set NFO_AUTO_CREATE=true to enable auto-creation")
print(" Set NFO_UPDATE_ON_SCAN=true to update existing NFOs during scan")
return 1
command = sys.argv[1].lower()
if command == "scan":
return asyncio.run(scan_and_create_nfo())
elif command == "status":
return asyncio.run(check_nfo_status())
elif command == "update":
return asyncio.run(update_nfo_files())
else:
print(f"Unknown command: {command}")
print("Use 'scan', 'status', or 'update'")
return 1
if __name__ == "__main__":
sys.exit(main())