Filter filename matching by WCX detection report
This commit is contained in:
@ -42,6 +42,8 @@ Examples:
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import sqlite3
|
||||
import subprocess
|
||||
@ -192,6 +194,15 @@ def parse_arguments() -> argparse.Namespace:
|
||||
help="Show candidates, scores, durations and classification details.",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--detection-report",
|
||||
type=Path,
|
||||
help=(
|
||||
"JSON detection report; only files classified as wcx "
|
||||
"will be matched."
|
||||
),
|
||||
)
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
@ -277,6 +288,116 @@ def parse_video_extensions(
|
||||
return extensions
|
||||
|
||||
|
||||
def normalize_absolute_path(path: Path) -> Path:
|
||||
return Path(os.path.abspath(path))
|
||||
|
||||
|
||||
def load_detection_report(
|
||||
report_file: Path,
|
||||
) -> dict[Path, str]:
|
||||
if not report_file.is_file():
|
||||
raise FileNotFoundError(
|
||||
f"Detection report does not exist: {report_file}"
|
||||
)
|
||||
|
||||
try:
|
||||
with report_file.open(encoding="utf-8") as file:
|
||||
report = json.load(file)
|
||||
except json.JSONDecodeError as error:
|
||||
raise ValueError(
|
||||
f"Detection report is not valid JSON: {report_file}: {error}"
|
||||
) from error
|
||||
|
||||
if not isinstance(report, dict):
|
||||
raise ValueError(
|
||||
"Detection report top level must be a JSON object."
|
||||
)
|
||||
|
||||
schema_version = report.get("schema_version")
|
||||
if type(schema_version) is not int or schema_version != 1:
|
||||
raise ValueError(
|
||||
"Detection report schema_version must be 1."
|
||||
)
|
||||
|
||||
entries = report.get("files")
|
||||
if not isinstance(entries, list):
|
||||
raise ValueError(
|
||||
"Detection report files must be a list."
|
||||
)
|
||||
|
||||
allowed_classifications = {
|
||||
"wcx",
|
||||
"uncertain",
|
||||
"not_wcx",
|
||||
"error",
|
||||
}
|
||||
classifications_by_path = {}
|
||||
|
||||
for index, entry in enumerate(entries):
|
||||
if not isinstance(entry, dict):
|
||||
raise ValueError(
|
||||
f"Detection report files[{index}] must be an object."
|
||||
)
|
||||
|
||||
if "path" not in entry:
|
||||
raise ValueError(
|
||||
f"Detection report files[{index}] is missing path."
|
||||
)
|
||||
|
||||
path_value = entry["path"]
|
||||
if not isinstance(path_value, str):
|
||||
raise ValueError(
|
||||
f"Detection report files[{index}].path must be a string."
|
||||
)
|
||||
|
||||
if "classification" not in entry:
|
||||
raise ValueError(
|
||||
f"Detection report files[{index}] is missing classification."
|
||||
)
|
||||
|
||||
classification = entry["classification"]
|
||||
if not isinstance(classification, str):
|
||||
raise ValueError(
|
||||
f"Detection report files[{index}].classification "
|
||||
f"must be a string."
|
||||
)
|
||||
|
||||
if classification not in allowed_classifications:
|
||||
raise ValueError(
|
||||
f"Detection report files[{index}] has unknown "
|
||||
f"classification: {classification}"
|
||||
)
|
||||
|
||||
normalized_path = normalize_absolute_path(
|
||||
Path(path_value)
|
||||
)
|
||||
if normalized_path in classifications_by_path:
|
||||
raise ValueError(
|
||||
f"Detection report contains duplicate path: "
|
||||
f"{normalized_path}"
|
||||
)
|
||||
|
||||
classifications_by_path[normalized_path] = classification
|
||||
|
||||
return classifications_by_path
|
||||
|
||||
|
||||
def select_wcx_files(
|
||||
inventoried_files: list[Path],
|
||||
report_entries: dict[Path, str],
|
||||
) -> list[Path]:
|
||||
wcx_paths = {
|
||||
path
|
||||
for path, classification in report_entries.items()
|
||||
if classification == "wcx"
|
||||
}
|
||||
return [
|
||||
path
|
||||
for path in inventoried_files
|
||||
if normalize_absolute_path(path) in wcx_paths
|
||||
]
|
||||
|
||||
|
||||
def load_movies(database_file: Path) -> list[Movie]:
|
||||
if not database_file.is_file():
|
||||
raise FileNotFoundError(
|
||||
@ -1267,25 +1388,73 @@ def main() -> None:
|
||||
"--limit must be greater than zero."
|
||||
)
|
||||
|
||||
if shutil.which("ffprobe") is None:
|
||||
raise FileNotFoundError(
|
||||
"ffprobe was not found in PATH."
|
||||
if args.detection_report is None:
|
||||
if shutil.which("ffprobe") is None:
|
||||
raise FileNotFoundError(
|
||||
"ffprobe was not found in PATH."
|
||||
)
|
||||
|
||||
extensions = parse_video_extensions(
|
||||
args.ending
|
||||
)
|
||||
|
||||
extensions = parse_video_extensions(
|
||||
args.ending
|
||||
)
|
||||
movies = load_movies(args.database)
|
||||
|
||||
movies = load_movies(args.database)
|
||||
video_files = find_video_files(
|
||||
directory=args.directory,
|
||||
recursive=args.recursive,
|
||||
extensions=extensions,
|
||||
)
|
||||
|
||||
video_files = find_video_files(
|
||||
directory=args.directory,
|
||||
recursive=args.recursive,
|
||||
extensions=extensions,
|
||||
)
|
||||
if args.limit is not None:
|
||||
video_files = video_files[:args.limit]
|
||||
else:
|
||||
extensions = parse_video_extensions(
|
||||
args.ending
|
||||
)
|
||||
inventoried_files = find_video_files(
|
||||
directory=args.directory,
|
||||
recursive=args.recursive,
|
||||
extensions=extensions,
|
||||
)
|
||||
|
||||
if args.limit is not None:
|
||||
video_files = video_files[:args.limit]
|
||||
if args.limit is not None:
|
||||
inventoried_files = inventoried_files[:args.limit]
|
||||
|
||||
report_entries = load_detection_report(
|
||||
args.detection_report
|
||||
)
|
||||
video_files = select_wcx_files(
|
||||
inventoried_files,
|
||||
report_entries,
|
||||
)
|
||||
report_wcx_files = sum(
|
||||
classification == "wcx"
|
||||
for classification in report_entries.values()
|
||||
)
|
||||
|
||||
print(
|
||||
f"detection_report: "
|
||||
f"{normalize_absolute_path(args.detection_report)}"
|
||||
)
|
||||
print(f"inventoried_files: {len(inventoried_files)}")
|
||||
print(f"report_wcx_files: {report_wcx_files}")
|
||||
print(f"selected_files: {len(video_files)}")
|
||||
print()
|
||||
|
||||
if not video_files:
|
||||
print(
|
||||
"No inventoried files were classified as wcx; "
|
||||
"nothing to match."
|
||||
)
|
||||
return
|
||||
|
||||
if shutil.which("ffprobe") is None:
|
||||
raise FileNotFoundError(
|
||||
"ffprobe was not found in PATH."
|
||||
)
|
||||
|
||||
movies = load_movies(args.database)
|
||||
|
||||
print(
|
||||
f"{'Filename':<55} "
|
||||
|
||||
Reference in New Issue
Block a user