Update analysis and plan files to enhance duplicate handling and reporting
- Updated `analysis.json` with a new generation timestamp. - Modified `plan.json` to include a new plan ID and created timestamp, and changed operation types from "no-op" to "quarantine" for specific files needing manual review. - Enhanced the README.md to document the new `--analysis` option for generating execution plans, which now includes a human-readable summary and duplicate handling strategies. - Introduced a new `duplicate_resolve.py` module to manage duplicate file resolution strategies. - Improved the execution engine to support quarantine operations and added rollback functionality for quarantined files. These changes improve the functionality of the Video Library Manager by providing better duplicate management and clearer reporting capabilities.
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
"""Duplicate group resolution: choose which file to keep when consuming analysis."""
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Union
|
||||
|
||||
from vlm.models import MovieIdentity, SeriesIdentity
|
||||
|
||||
|
||||
def choose_keep_index(
|
||||
items: list[tuple[Path, Union[MovieIdentity, SeriesIdentity]]],
|
||||
strategy: str,
|
||||
) -> int | None:
|
||||
"""Choose the index of the item to keep in a duplicate group.
|
||||
|
||||
Strategies:
|
||||
- by_reputation: Prefer items with reputation_score; then sort by score desc,
|
||||
then reputation_votes desc, then first_seen (input order). Keep index 0 after sort.
|
||||
- first_seen: Keep the first item (index 0).
|
||||
- manual: Return None; caller should not generate quarantine ops, only record in metadata.
|
||||
|
||||
Args:
|
||||
items: List of (path, identity) for the duplicate group.
|
||||
strategy: One of "by_reputation", "first_seen", "manual".
|
||||
|
||||
Returns:
|
||||
Index in [0, len(items)) to keep, or None for manual.
|
||||
"""
|
||||
if strategy == "manual":
|
||||
return None
|
||||
if strategy == "first_seen":
|
||||
return 0
|
||||
if strategy == "by_reputation":
|
||||
return _by_reputation_index(items)
|
||||
return 0
|
||||
|
||||
|
||||
def _by_reputation_index(
|
||||
items: list[tuple[Path, Union[MovieIdentity, SeriesIdentity]]],
|
||||
) -> int:
|
||||
"""Sort by: has reputation > no reputation; then score desc; then votes desc; then order. Return 0."""
|
||||
def key(idx_reason: tuple[int, tuple[Path, Union[MovieIdentity, SeriesIdentity]]]) -> tuple:
|
||||
idx, (path, identity) = idx_reason
|
||||
has_rep = identity.reputation_score is not None
|
||||
score = identity.reputation_score if identity.reputation_score is not None else -1.0
|
||||
votes = identity.reputation_votes if identity.reputation_votes is not None else -1
|
||||
# Prefer has reputation (True > False), then higher score, then higher votes, then lower index
|
||||
return (not has_rep, -score, -votes, idx)
|
||||
|
||||
indexed = list(enumerate(items))
|
||||
indexed.sort(key=key)
|
||||
return indexed[0][0]
|
||||
Reference in New Issue
Block a user