fix(cli): address media treatment review findings

This commit is contained in:
ukimsanov
2026-07-26 01:25:42 -07:00
parent 6d5961b802
commit c1dde28980
11 changed files with 526 additions and 124 deletions
@@ -1,66 +0,0 @@
export interface GradeSignalFrame {
ptsTime?: number;
YMIN?: number;
YLOW?: number;
YAVG?: number;
YHIGH?: number;
YMAX?: number;
UAVG?: number;
VAVG?: number;
SATAVG?: number;
}
export interface GradeMediaProbe {
duration: number | null;
colorSpace: string;
transfer: string;
primaries: string;
pixelFormat: string;
}
export interface MediaTreatmentAnalysis {
adjust: Record<string, number>;
measured: {
frames: number;
yMin: number;
yLow: number;
yAvg: number;
yHigh: number;
yMax: number;
uAvg: number;
vAvg: number;
satAvg: number;
shadowClipRisk: number;
highlightClipRisk: number;
};
source: {
colorSpace: string;
transfer: string;
primaries: string;
pixelFormat: string;
hdr: boolean;
log: "unknown";
};
diagnosis: string[];
warnings: string[];
}
export function parseMediaTreatmentSignalStats(raw: string): GradeSignalFrame[];
export function statsToAdjust(
stats: Record<string, number>,
): Pick<MediaTreatmentAnalysis, "adjust" | "measured">;
export function summarizeMediaTreatmentAnalysis(
probe: GradeMediaProbe,
frames: readonly GradeSignalFrame[],
): MediaTreatmentAnalysis;
export function analyzeMediaGrade(
mediaPath: string,
options?: {
ffmpegPath?: string;
ffprobePath?: string;
},
): MediaTreatmentAnalysis;
export function formatMeasuredNote(
mediaPath: string,
measured: MediaTreatmentAnalysis["measured"],
): string;
+28 -18
View File
@@ -1,3 +1,5 @@
// Vendored plain-JS copy of packages/core/src/mediaGradeAnalyzer.ts.
// packages/core/src/mediaGradeAnalyzer.vendoredParity.test.ts guards behavior drift.
import { execFileSync } from "node:child_process";
import { basename, extname } from "node:path";
@@ -114,6 +116,26 @@ function summarizeFrames(frames) {
};
}
function suggestedExposure(normalizedAverage, yLow, yHigh) {
if (normalizedAverage < 0.28 && yHigh / 255 < 0.65) {
return clamp((0.32 - normalizedAverage) * 1.2, "exposure");
}
if (normalizedAverage > 0.72 && yLow / 255 > 0.3) {
return clamp((0.68 - normalizedAverage) * 1.2, "exposure");
}
return 0;
}
function suggestedTemperature(uAverage, vAverage) {
const chromaWarmth = (vAverage - 128 + (128 - uAverage)) / 128;
return Math.abs(chromaWarmth) >= 0.08 ? clamp(-chromaWarmth * 0.25, "temperature") : 0;
}
function suggestedTint(uAverage, vAverage) {
const cast = uAverage + vAverage - 256;
return Math.abs(cast) >= 10 ? clamp(-cast / 512, "tint") : 0;
}
export function statsToAdjust(stats) {
const yMin = Number(stats.yMin);
const yLow = Number(stats.yLow ?? stats.yMin);
@@ -125,29 +147,17 @@ export function statsToAdjust(stats) {
const shadowClipRisk = Number(stats.shadowClipRisk ?? (yLow <= 16 ? 1 : 0));
const highlightClipRisk = Number(stats.highlightClipRisk ?? (yHigh >= 235 ? 1 : 0));
const percentileSpread = (yHigh - yLow) / 255;
const normalizedAvg = yAvg / 255;
const exposure =
normalizedAvg < 0.28 && yHigh / 255 < 0.65
? clamp((0.32 - normalizedAvg) * 1.2, "exposure")
: normalizedAvg > 0.72 && yLow / 255 > 0.3
? clamp((0.68 - normalizedAvg) * 1.2, "exposure")
: 0;
const normalizedAverage = yAvg / 255;
const contrast = percentileSpread < 0.35 ? clamp((0.35 - percentileSpread) * 0.4, "contrast") : 0;
const whites = clamp(-highlightClipRisk * 0.08, "whites");
const blacks = clamp(shadowClipRisk * 0.08, "blacks");
const chromaWarmth = (vAvg - 128 + (128 - uAvg)) / 128;
const temperature =
Math.abs(chromaWarmth) >= 0.08 ? clamp(-chromaWarmth * 0.25, "temperature") : 0;
const tint = Math.abs(uAvg + vAvg - 256) >= 10 ? clamp(-(uAvg + vAvg - 256) / 512, "tint") : 0;
return {
adjust: {
exposure: round(exposure),
exposure: round(suggestedExposure(normalizedAverage, yLow, yHigh)),
contrast: round(contrast),
blacks: round(blacks),
whites: round(whites),
temperature: round(temperature),
tint: round(tint),
blacks: round(clamp(shadowClipRisk * 0.08, "blacks")),
whites: round(clamp(-highlightClipRisk * 0.08, "whites")),
temperature: round(suggestedTemperature(uAvg, vAvg)),
tint: round(suggestedTint(uAvg, vAvg)),
},
measured: {
frames: Number(stats.frames ?? 1),