Files
dl-organizer/src/vlm/commands/parse.py
T

166 lines
5.3 KiB
Python
Raw Normal View History

"""Parse command implementation."""
from __future__ import annotations
from pathlib import Path
from typing import Optional
import click
from vlm.context import CLIContext
from vlm.io import load_inventory_csv, save_identities_json
from vlm.parser import parse_movie, parse_series
from vlm.utils import utc_now
def parse_cmd(ctx: CLIContext, input: Path, output: Path, inventory: Optional[Path]) -> None:
"""Parse identities from scanned inventory."""
config = ctx.config
logger = ctx.logger
click.echo(f"Parsing identities from: {input}")
path_to_metadata: dict[str, object] = {}
if inventory:
click.echo(f"Loading video metadata from: {inventory}")
inventory_files = load_inventory_csv(inventory)
path_to_metadata = {str(vf.path): vf for vf in inventory_files}
click.echo(f"Loaded metadata for {len(path_to_metadata)} files")
click.echo()
inventory_files = load_inventory_csv(input)
video_files = [
{
"path": str(vf.path),
"filename": vf.filename,
"category": vf.category,
}
for vf in inventory_files
]
click.echo(f"Loaded {len(video_files)} files from inventory")
click.echo()
movie_identities: list[dict] = []
series_identities: list[dict] = []
anime_files: list[dict] = []
other_files: list[dict] = []
def get_video_metadata(file_path: str) -> dict:
"""Extract video metadata from inventory if available."""
if not path_to_metadata:
return {}
vf = path_to_metadata.get(file_path)
if not vf:
return {}
return {
"size_bytes": vf.size_bytes,
"modified_timestamp": vf.modified_timestamp.isoformat(),
"resolution": vf.resolution,
"codec": vf.codec,
"duration_seconds": vf.duration_seconds,
"bitrate_kbps": vf.bitrate_kbps,
}
for vf in video_files:
filename = vf["filename"]
category = vf["category"]
file_path = vf["path"]
video_metadata = get_video_metadata(file_path)
if category == "movie":
identity = parse_movie(filename, extensions=config.video_extensions)
record = {
"path": file_path,
"filename": filename,
"category": category,
"title": identity.title,
"year": identity.year,
"confidence": identity.confidence,
"needs_review": identity.needs_review,
}
if video_metadata:
record["video_metadata"] = video_metadata
movie_identities.append(record)
elif category == "series":
identity = parse_series(filename, extensions=config.video_extensions)
record = {
"path": file_path,
"filename": filename,
"category": category,
"title": identity.title,
"season": identity.season,
"episodes": identity.episodes,
"confidence": identity.confidence,
"needs_review": identity.needs_review,
}
if video_metadata:
record["video_metadata"] = video_metadata
series_identities.append(record)
elif category == "anime":
anime_files.append(
{
"path": vf["path"],
"filename": filename,
"category": category,
"note": "Anime parsing deferred in v1",
}
)
else:
other_files.append(
{
"path": vf["path"],
"filename": filename,
"category": category,
"note": "Not categorized for parsing",
}
)
click.echo("Parsing complete!")
click.echo()
click.echo("Results by category:")
click.echo(f" Movies: {len(movie_identities)}")
movies_need_review = sum(1 for m in movie_identities if m["needs_review"])
if movies_need_review > 0:
click.echo(f" - Need review: {movies_need_review}")
click.echo(f" Series: {len(series_identities)}")
series_need_review = sum(1 for s in series_identities if s["needs_review"])
if series_need_review > 0:
click.echo(f" - Need review: {series_need_review}")
click.echo(f" Anime: {len(anime_files)} (not parsed in v1)")
click.echo(f" Other: {len(other_files)} (not parsed)")
click.echo()
click.echo(f"Saving parsed identities to: {output}")
generation_timestamp = utc_now().strftime("%Y-%m-%dT%H:%M:%S")
schema_version = "2.0" if path_to_metadata else "1.0"
identities_data = {
"vlm_schema_version": schema_version,
"metadata": {
"generated": generation_timestamp,
"source_inventory": str(input),
"total_files": len(video_files),
},
"movies": movie_identities,
"series": series_identities,
"anime": anime_files,
"other": other_files,
}
save_identities_json(identities_data, output)
click.echo("Parsed identities saved successfully!")
logger.info(
"Parse completed: %s movies, %s series, saved to %s",
len(movie_identities),
len(series_identities),
output,
)