Prefer ffmpeg-derived resolution and normalize to SD/HD/FHD/4K/8K
This commit is contained in:
@@ -67,7 +67,7 @@ async def _build_torrent_info(
|
||||
return Torrent(
|
||||
title=item.title,
|
||||
playable_file=make_relative_path(playable_file, media_root),
|
||||
resolution=item.resolution or (probe_info.resolution if probe_info else None),
|
||||
resolution=(probe_info.resolution if probe_info else None) or item.resolution,
|
||||
quality=item.quality,
|
||||
codec=item.codec,
|
||||
audio=item.audio,
|
||||
@@ -252,7 +252,7 @@ def _build_episodes_data(
|
||||
torrents[relpath] = Torrent(
|
||||
title=f["torrent_title"],
|
||||
playable_file=make_relative_path(f["path"], media_root),
|
||||
resolution=f.get("resolution") or f.get("probed_resolution"),
|
||||
resolution=f.get("probed_resolution") or f.get("resolution"),
|
||||
quality=f.get("quality"),
|
||||
codec=f.get("codec"),
|
||||
audio=f.get("audio"),
|
||||
|
||||
@@ -8,6 +8,7 @@ import PTN
|
||||
from aiopathlib import AsyncPath
|
||||
|
||||
from mediahive.hivescan.models import ContentHash, ContentType, ParsedContent
|
||||
from mediahive.hivescan.utils import normalize_resolution_label
|
||||
|
||||
|
||||
def determine_content_type(parsed: dict) -> ContentType:
|
||||
@@ -36,7 +37,7 @@ async def parse_download(path: Path) -> ParsedContent:
|
||||
content_type=content_type,
|
||||
title=parsed.get("title", name),
|
||||
year=parsed.get("year"),
|
||||
resolution=parsed.get("resolution"),
|
||||
resolution=normalize_resolution_label(parsed.get("resolution")),
|
||||
quality=parsed.get("quality"),
|
||||
codec=parsed.get("codec"),
|
||||
audio=parsed.get("audio"),
|
||||
|
||||
@@ -19,6 +19,8 @@ from typing import Optional
|
||||
|
||||
from aiopathlib import AsyncPath
|
||||
|
||||
from mediahive.hivescan.utils import classify_resolution_from_dimensions
|
||||
|
||||
logger = logging.getLogger("hivescan.showreel")
|
||||
|
||||
|
||||
@@ -388,7 +390,9 @@ async def probe_media_info(video_path: str) -> MediaProbeInfo:
|
||||
if dim_match:
|
||||
info.width = int(dim_match.group(1))
|
||||
info.height = int(dim_match.group(2))
|
||||
info.resolution = f"{info.width}x{info.height}"
|
||||
info.resolution = classify_resolution_from_dimensions(
|
||||
info.width, info.height
|
||||
)
|
||||
|
||||
info.is_hdr = (
|
||||
"smpte2084" in lower_text
|
||||
|
||||
@@ -2,11 +2,10 @@
|
||||
|
||||
import time
|
||||
from pathlib import Path
|
||||
from typing import Optional, List
|
||||
from typing import List, Optional
|
||||
|
||||
from aiopathlib import AsyncPath
|
||||
|
||||
|
||||
# Default output folder name (created at common root of scanned paths)
|
||||
DEFAULT_OUTPUT_FOLDER = ".mediahive"
|
||||
|
||||
@@ -15,15 +14,76 @@ _ATIME_FRESHNESS_THRESHOLD = 3600
|
||||
|
||||
# Resolution priority for quality sorting (higher = better)
|
||||
RESOLUTION_PRIORITY = {
|
||||
"2160p": 4,
|
||||
"8K": 5,
|
||||
"4K": 4,
|
||||
"FHD": 3,
|
||||
"HD": 2,
|
||||
"SD": 1,
|
||||
# Backward compatibility for existing snapshot data
|
||||
"4320p": 5,
|
||||
"2160p": 4,
|
||||
"UHD": 4,
|
||||
"1080p": 3,
|
||||
"1080i": 3,
|
||||
"720p": 2,
|
||||
"576p": 1,
|
||||
"480p": 1,
|
||||
}
|
||||
|
||||
|
||||
def classify_resolution_from_dimensions(
|
||||
width: int | None, height: int | None
|
||||
) -> str | None:
|
||||
"""Map raw frame dimensions to SD/HD/FHD/4K/8K buckets.
|
||||
|
||||
Uses the smallest standard frame bucket that can contain the source frame,
|
||||
which keeps cropped cinematic encodes in their expected class.
|
||||
"""
|
||||
if not width or not height or width <= 0 or height <= 0:
|
||||
return None
|
||||
|
||||
long_edge = max(width, height)
|
||||
short_edge = min(width, height)
|
||||
|
||||
buckets = [
|
||||
(1024, 576, "SD"),
|
||||
(1280, 720, "HD"),
|
||||
(1920, 1080, "FHD"),
|
||||
(4096, 2160, "4K"),
|
||||
(8192, 4320, "8K"),
|
||||
]
|
||||
|
||||
for max_w, max_h, label in buckets:
|
||||
if long_edge <= max_w and short_edge <= max_h:
|
||||
return label
|
||||
|
||||
return "8K"
|
||||
|
||||
|
||||
def normalize_resolution_label(value: str | None) -> str | None:
|
||||
"""Normalize PTN/legacy resolution text into SD/HD/FHD/4K/8K labels."""
|
||||
if not value:
|
||||
return None
|
||||
|
||||
normalized = str(value).strip().upper()
|
||||
mapping = {
|
||||
"SD": "SD",
|
||||
"HD": "HD",
|
||||
"FHD": "FHD",
|
||||
"4K": "4K",
|
||||
"8K": "8K",
|
||||
"4320P": "8K",
|
||||
"2160P": "4K",
|
||||
"UHD": "4K",
|
||||
"1080P": "FHD",
|
||||
"1080I": "FHD",
|
||||
"720P": "HD",
|
||||
"576P": "SD",
|
||||
"480P": "SD",
|
||||
}
|
||||
return mapping.get(normalized)
|
||||
|
||||
|
||||
async def get_added_timestamp(path: Path) -> Optional[int]:
|
||||
"""
|
||||
Get the timestamp when a torrent was added to the collection.
|
||||
|
||||
Reference in New Issue
Block a user