diff --git a/README.md b/README.md index 241f79b..2ca38f7 100644 --- a/README.md +++ b/README.md @@ -54,11 +54,11 @@ the `.env.example` values through Coolify's environment-variable UI. ## Codec policy -Cached video is H.264 High Profile, Level 4.1, 8-bit `yuv420p`, CRF 18, capped -at 15 Mbps. Existing audio, subtitles, chapters, and Matroska attachments are -preserved when the container supports them. Ten-bit SDR is converted with -error-diffusion dithering; HDR10, HLG, and Dolby Vision-labelled sources are -tone-mapped to BT.709 SDR before encoding. +Cached video is H.264 High Profile, 8-bit `yuv420p`, CRF 18, capped at 15 Mbps. +Resolution and frame rate are preserved. Existing audio, subtitles, chapters, +and Matroska attachments are copied. Ten-bit SDR is converted with +error-diffusion dithering; sources carrying explicit HDR10 or HLG transfer +metadata are tone-mapped to BT.709 SDR. The fixed source blacklist is: diff --git a/pretranscode.py b/pretranscode.py index 22df88b..bbb2dbb 100755 --- a/pretranscode.py +++ b/pretranscode.py @@ -39,7 +39,7 @@ H264_CRF = os.environ.get("H264_CRF", "18") H264_PRESET = os.environ.get("H264_PRESET", "medium") H264_MAXRATE = os.environ.get("H264_MAXRATE", "15M") H264_BUFSIZE = os.environ.get("H264_BUFSIZE", "30M") -ENCODE_PROFILE = f"h264-high41-8bit-crf{H264_CRF}-{H264_PRESET}-{H264_MAXRATE}-quality-v2" +ENCODE_PROFILE = f"h264-high-8bit-crf{H264_CRF}-{H264_PRESET}-{H264_MAXRATE}-quality-v3" FFMPEG = os.environ.get("FFMPEG", "/usr/bin/ffmpeg") FFPROBE = os.environ.get("FFPROBE", "/usr/bin/ffprobe") @@ -389,8 +389,10 @@ class Manager: continue old = inventory.get(relative) or {} if old.get("fingerprint") == current_fingerprint: - if relative in catalog and catalog[relative].get("item_id"): - old["item_id"] = catalog[relative]["item_id"] + if relative in catalog: + for key in ("item_id", "color_transfer", "color_primaries", "color_space"): + if catalog[relative].get(key): + old[key] = catalog[relative][key] continue details = catalog.get(relative) view = VIEW_ROOT / relative @@ -469,13 +471,8 @@ class Manager: profile = (info.get("profile") or "").lower() return codec in BLACKLISTED_CODECS or (codec == "h264" and (depth > 8 or "10" in profile)) - def is_hdr(self, info, source): - transfer = (info.get("color_transfer") or "").lower() - name = source.name.lower() - return ( - transfer in {"smpte2084", "arib-std-b67"} - or any(token in name for token in ("hdr10", "hdr.", ".hdr", "dovi", "dolby.vision")) - ) + def is_hdr(self, info): + return (info.get("color_transfer") or "").lower() in {"smpte2084", "arib-std-b67"} def candidate_list(self): now = time.time() @@ -581,15 +578,13 @@ class Manager: self.process = None def ffmpeg_command(self, source, partial, info): - hdr = self.is_hdr(info, source) + hdr = self.is_hdr(info) if hdr: transfer = (info.get("color_transfer") or "").lower() if transfer == "arib-std-b67": input_colors = "tin=arib-std-b67:pin=bt2020:min=bt2020nc:" - elif transfer == "smpte2084": - input_colors = "" else: - input_colors = "tin=smpte2084:pin=bt2020:min=bt2020nc:" + input_colors = "" video_filter = ( f"zscale={input_colors}t=linear:npl=100,format=gbrpf32le," "zscale=p=bt709,tonemap=tonemap=hable:desat=0," @@ -604,7 +599,7 @@ class Manager: "-vf", video_filter, "-c:v", "libx264", "-preset", H264_PRESET, "-crf", H264_CRF, "-maxrate", H264_MAXRATE, "-bufsize", H264_BUFSIZE, - "-pix_fmt", "yuv420p", "-profile:v", "high", "-level:v", "4.1", + "-pix_fmt", "yuv420p", "-profile:v", "high", "-c:a", "copy", "-c:s", "copy", ] if hdr: