feat: enable anime parsing and organization with dedicated templates

Anime files with SxxEyy format are now parsed and organized into
anime-specific directories using configurable templates. Files with
absolute episode numbering (no season info) are marked needs_review
for manual handling. Also removes unused imports flagged by ruff.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
windyboy
2026-09-27 11:08:15 +08:00
co-authored by Claude Sonnet 4.5
parent 8832f5da3f
commit 9814b2b917
8 changed files with 248 additions and 19 deletions
+25 -13
View File
@@ -17,7 +17,7 @@ from vlm.models import (
SeriesIdentityRecord,
VideoFile,
)
from vlm.parser import parse_movie, parse_series
from vlm.parser import parse_anime, parse_movie, parse_series
from vlm.utils import utc_now
@@ -52,7 +52,7 @@ def parse_cmd(ctx: CLIContext, input: Path, output: Path, inventory: Optional[Pa
movie_identities: list[MovieIdentityRecord] = []
series_identities: list[SeriesIdentityRecord] = []
anime_files: list[IdentityRecord] = []
anime_identities: list[SeriesIdentityRecord] = []
other_files: list[IdentityRecord] = []
def get_video_metadata(file_path: str) -> dict:
@@ -107,14 +107,20 @@ def parse_cmd(ctx: CLIContext, input: Path, output: Path, inventory: Optional[Pa
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",
}
)
identity = parse_anime(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
anime_identities.append(record)
else:
other_files.append(
{
@@ -140,7 +146,12 @@ def parse_cmd(ctx: CLIContext, input: Path, output: Path, inventory: Optional[Pa
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" Anime: {len(anime_identities)}")
anime_need_review = sum(1 for a in anime_identities if a["needs_review"])
if anime_need_review > 0:
click.echo(f" - Need review: {anime_need_review}")
click.echo(f" Other: {len(other_files)} (not parsed)")
click.echo()
@@ -158,7 +169,7 @@ def parse_cmd(ctx: CLIContext, input: Path, output: Path, inventory: Optional[Pa
},
"movies": movie_identities,
"series": series_identities,
"anime": anime_files,
"anime": anime_identities,
"other": other_files,
}
@@ -166,9 +177,10 @@ def parse_cmd(ctx: CLIContext, input: Path, output: Path, inventory: Optional[Pa
click.echo("Parsed identities saved successfully!")
logger.info(
"Parse completed: %s movies, %s series, saved to %s",
"Parse completed: %s movies, %s series, %s anime, saved to %s",
len(movie_identities),
len(series_identities),
len(anime_identities),
output,
)