@hyperframes/studio-server 0.9.16 → 0.9.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-NOIHVBIQ.js → chunk-66YZA32N.js} +1 -1
- package/dist/{chunk-B356RYG5.js → chunk-F4AQECCT.js} +3 -3
- package/dist/{chunk-GTQ6S4O5.js → chunk-IJMPYPJG.js} +2 -2
- package/dist/{chunk-H4NVSSDJ.js → chunk-NEGQQZNI.js} +30 -31
- package/dist/{chunk-X73QTST2.js → chunk-Y6EZUBYR.js} +13 -3
- package/dist/helpers/loudness.d.ts +4 -4
- package/dist/helpers/loudness.js +2 -2
- package/dist/helpers/mediaCodecMap.js +2 -2
- package/dist/helpers/mediaProxyPreview.js +4 -4
- package/dist/helpers/proxyTranscoder.js +3 -3
- package/dist/index.js +5 -5
- package/package.json +3 -3
|
@@ -2,11 +2,11 @@ import {
|
|
|
2
2
|
PROXY_STILL_EXTENSION,
|
|
3
3
|
PROXY_VARIANT_CONFIG,
|
|
4
4
|
ffmpegHasHdrFilters
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-66YZA32N.js";
|
|
6
6
|
import {
|
|
7
7
|
probeFirstFrameColour,
|
|
8
8
|
probeMediaMetadata
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-Y6EZUBYR.js";
|
|
10
10
|
import {
|
|
11
11
|
mkdirWithinProject,
|
|
12
12
|
realProjectRoot,
|
|
@@ -768,7 +768,7 @@ async function buildVideoFilter(ffmpegPath, sourcePath, metadata, keepsAlpha, bo
|
|
|
768
768
|
const { hdrTransfer } = metadata.color;
|
|
769
769
|
const toneMap = (hdrTransfer === "pq" || hdrTransfer === "hlg") && !keepsAlpha;
|
|
770
770
|
if (toneMap) await ensureHdrFilters(ffmpegPath);
|
|
771
|
-
const firstFrame = toneMap ? await probeFirstFrameColour(sourcePath) : {};
|
|
771
|
+
const firstFrame = toneMap ? await probeFirstFrameColour(sourcePath, metadata.color.codecName) : {};
|
|
772
772
|
const evenScale = proxyScaleFilter(box);
|
|
773
773
|
const pixelFormat = keepsAlpha ? "yuva420p" : "yuv420p";
|
|
774
774
|
return toneMap ? [
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
PROXY_PARAMS_VERSION,
|
|
3
3
|
resolveProxy
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-F4AQECCT.js";
|
|
5
5
|
import {
|
|
6
6
|
createMediaCodecProbeCache,
|
|
7
7
|
proxyVariantFor,
|
|
8
8
|
recordProxyPrewarm,
|
|
9
9
|
scanProjectMedia,
|
|
10
10
|
shouldPrewarmProxy
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-66YZA32N.js";
|
|
12
12
|
|
|
13
13
|
// src/helpers/mediaProxyPreview.ts
|
|
14
14
|
import { resolve } from "path";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
probeAudioTrack
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-Y6EZUBYR.js";
|
|
4
4
|
import {
|
|
5
5
|
resolveWithinProject
|
|
6
6
|
} from "./chunk-QF7MFJJU.js";
|
|
@@ -277,30 +277,24 @@ function audioNormalizationPlan(referenceTrack, targetTrack) {
|
|
|
277
277
|
}
|
|
278
278
|
const referenceLufs = referenceTrack.integratedLufs + gainDb(referenceTrack.volume);
|
|
279
279
|
const targetLufs = targetTrack.integratedLufs + gainDb(targetTrack.volume);
|
|
280
|
-
const
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
const volume = 10 ** (wantedGainDb / 20);
|
|
287
|
-
const projectedTruePeakDbfs = targetTrack.truePeakDbfs + wantedGainDb;
|
|
288
|
-
if (projectedTruePeakDbfs > 0) {
|
|
289
|
-
throw new Error(
|
|
290
|
-
`Matching #${targetTrack.id} would clip at +${projectedTruePeakDbfs.toFixed(1)} dBFS; limit or preprocess the source first`
|
|
291
|
-
);
|
|
292
|
-
}
|
|
280
|
+
const previousGainDb = gainDb(targetTrack.volume);
|
|
281
|
+
const { appliedGainDb, limitedBy } = fittedGain(
|
|
282
|
+
referenceLufs - targetTrack.integratedLufs,
|
|
283
|
+
targetTrack.truePeakDbfs,
|
|
284
|
+
previousGainDb
|
|
285
|
+
);
|
|
293
286
|
return {
|
|
294
287
|
referenceId: referenceTrack.id,
|
|
295
288
|
targetId: targetTrack.id,
|
|
296
289
|
referenceLufs,
|
|
297
290
|
targetLufs,
|
|
298
291
|
previousVolume: targetTrack.volume,
|
|
299
|
-
volume,
|
|
300
|
-
gainDb:
|
|
301
|
-
changeDb:
|
|
302
|
-
projectedLufs: targetTrack.integratedLufs +
|
|
303
|
-
projectedTruePeakDbfs
|
|
292
|
+
volume: 10 ** (appliedGainDb / 20),
|
|
293
|
+
gainDb: appliedGainDb,
|
|
294
|
+
changeDb: appliedGainDb - previousGainDb,
|
|
295
|
+
projectedLufs: targetTrack.integratedLufs + appliedGainDb,
|
|
296
|
+
projectedTruePeakDbfs: targetTrack.truePeakDbfs + appliedGainDb,
|
|
297
|
+
limitedBy
|
|
304
298
|
};
|
|
305
299
|
}
|
|
306
300
|
function updateAudioVolume(html, id, volume) {
|
|
@@ -363,19 +357,12 @@ function absoluteLoudnessPlan(track, targetLufs = DEFAULT_TARGET_LUFS) {
|
|
|
363
357
|
if (track.integratedLufs <= SILENCE_LUFS) {
|
|
364
358
|
throw new Error(`#${track.id} is silent in the played window; there is nothing to normalize`);
|
|
365
359
|
}
|
|
366
|
-
const wantedGainDb = targetLufs - track.integratedLufs;
|
|
367
|
-
const peakRoomDb = NORMALIZE_TRUE_PEAK_CEILING_DBFS - track.truePeakDbfs;
|
|
368
|
-
const limits = [
|
|
369
|
-
[null, wantedGainDb],
|
|
370
|
-
["gain-ceiling", MAX_AUDIO_GAIN_DB],
|
|
371
|
-
["true-peak", peakRoomDb]
|
|
372
|
-
];
|
|
373
|
-
const [limitedBy, limitedGainDb] = limits.reduce(
|
|
374
|
-
(lowest, candidate) => candidate[1] < lowest[1] - 1e-9 ? candidate : lowest
|
|
375
|
-
);
|
|
376
360
|
const previousGainDb = track.volume > 0 ? gainDb(track.volume) : 0;
|
|
377
|
-
const
|
|
378
|
-
|
|
361
|
+
const { appliedGainDb, limitedBy } = fittedGain(
|
|
362
|
+
targetLufs - track.integratedLufs,
|
|
363
|
+
track.truePeakDbfs,
|
|
364
|
+
previousGainDb
|
|
365
|
+
);
|
|
379
366
|
return {
|
|
380
367
|
targetId: track.id,
|
|
381
368
|
targetLufs,
|
|
@@ -389,6 +376,18 @@ function absoluteLoudnessPlan(track, targetLufs = DEFAULT_TARGET_LUFS) {
|
|
|
389
376
|
limitedBy
|
|
390
377
|
};
|
|
391
378
|
}
|
|
379
|
+
function fittedGain(wantedGainDb, truePeakDbfs, previousGainDb) {
|
|
380
|
+
const limits = [
|
|
381
|
+
[null, wantedGainDb],
|
|
382
|
+
["gain-ceiling", MAX_AUDIO_GAIN_DB],
|
|
383
|
+
["true-peak", NORMALIZE_TRUE_PEAK_CEILING_DBFS - truePeakDbfs]
|
|
384
|
+
];
|
|
385
|
+
const [limitedBy, limitedGainDb] = limits.reduce(
|
|
386
|
+
(lowest, candidate) => candidate[1] < lowest[1] - 1e-9 ? candidate : lowest
|
|
387
|
+
);
|
|
388
|
+
const appliedGainDb = wantedGainDb > previousGainDb ? Math.max(limitedGainDb, previousGainDb) : limitedGainDb;
|
|
389
|
+
return { appliedGainDb, limitedBy };
|
|
390
|
+
}
|
|
392
391
|
function absoluteOutcome(plan, tolerance) {
|
|
393
392
|
if (Math.abs(plan.changeDb) > tolerance) return "change";
|
|
394
393
|
return Math.abs(plan.projectedLufs - plan.targetLufs) <= tolerance ? "matched" : "held";
|
|
@@ -3,7 +3,10 @@ import { execFile } from "child_process";
|
|
|
3
3
|
import { extname } from "path";
|
|
4
4
|
import {
|
|
5
5
|
firstFrameColourArgs,
|
|
6
|
-
|
|
6
|
+
keyframeTransferArgs,
|
|
7
|
+
parseFirstFrameColour,
|
|
8
|
+
readsKeyframeTransfers,
|
|
9
|
+
parseKeyframeTransfers
|
|
7
10
|
} from "@hyperframes/core";
|
|
8
11
|
import { findFfBinary } from "@hyperframes/parsers/ff-binaries";
|
|
9
12
|
import { videoStreamHasAlpha } from "@hyperframes/parsers/media-alpha";
|
|
@@ -82,14 +85,21 @@ function classifyMediaColor(stream) {
|
|
|
82
85
|
bitsPerRawSample: stream?.bits_per_raw_sample
|
|
83
86
|
};
|
|
84
87
|
}
|
|
85
|
-
async function probeFirstFrameColour(filePath, runner = execFileRunner) {
|
|
88
|
+
async function probeFirstFrameColour(filePath, codec, runner = execFileRunner) {
|
|
86
89
|
const ffmpegPath = findFfBinary("ffmpeg", { configuredMustExist: true }) ?? (runner === execFileRunner ? void 0 : "ffmpeg");
|
|
87
90
|
if (!ffmpegPath) return {};
|
|
88
91
|
const result = await runner(ffmpegPath, firstFrameColourArgs(filePath), {
|
|
89
92
|
timeout: 15e3,
|
|
90
93
|
maxBuffer: 1024 * 1024
|
|
91
94
|
});
|
|
92
|
-
|
|
95
|
+
const first = result.status === 0 ? parseFirstFrameColour(String(result.stderr)) : {};
|
|
96
|
+
if (result.status !== 0 || !readsKeyframeTransfers(first)) return first;
|
|
97
|
+
const keyframes = await runner(ffmpegPath, keyframeTransferArgs(filePath, codec), {
|
|
98
|
+
timeout: 3e4,
|
|
99
|
+
maxBuffer: 8 * 1024 * 1024
|
|
100
|
+
});
|
|
101
|
+
const keyframeTransfers = keyframes.status === 0 ? parseKeyframeTransfers(String(keyframes.stderr)) : [];
|
|
102
|
+
return { ...first, keyframeTransfers };
|
|
93
103
|
}
|
|
94
104
|
function bitDepthOf(stream) {
|
|
95
105
|
const raw = Number(stream.bits_per_raw_sample);
|
|
@@ -35,6 +35,7 @@ interface AudioNormalizationPlan {
|
|
|
35
35
|
changeDb: number;
|
|
36
36
|
projectedLufs: number;
|
|
37
37
|
projectedTruePeakDbfs: number;
|
|
38
|
+
limitedBy: LoudnessLimit | null;
|
|
38
39
|
}
|
|
39
40
|
/** Read the authored clips with sound — `<audio>` and audible `<video>` — without reserializing. */
|
|
40
41
|
declare function audioTags(html: string): AudioTag[];
|
|
@@ -43,7 +44,7 @@ declare function resolveLocalAudioPath(projectDir: string, src: string): string;
|
|
|
43
44
|
/** Parse FFmpeg's final EBU R128 summary, not its per-window log lines. */
|
|
44
45
|
declare function parseEbur128Summary(stderr: string): LoudnessMeasurement;
|
|
45
46
|
declare function gainDb(volume: number): number;
|
|
46
|
-
/**
|
|
47
|
+
/** The target's gain that matches the reference's played loudness; past a ceiling it stops as fittedGain does. */
|
|
47
48
|
declare function audioNormalizationPlan(referenceTrack: NormalizationTrack, targetTrack: NormalizationTrack): AudioNormalizationPlan;
|
|
48
49
|
/** Patch one authored attribute in place so scripts/styles/comments remain byte-stable. */
|
|
49
50
|
declare function updateAudioVolume(html: string, id: string, volume: number): string;
|
|
@@ -78,9 +79,8 @@ interface AbsoluteLoudnessPlan {
|
|
|
78
79
|
}
|
|
79
80
|
/**
|
|
80
81
|
* The authored gain that brings one clip's played window to an absolute
|
|
81
|
-
* loudness
|
|
82
|
-
*
|
|
83
|
-
* names which limit stopped it.
|
|
82
|
+
* loudness; past the +12 dB authoring ceiling or the true-peak ceiling it
|
|
83
|
+
* applies the most that fits and names which limit stopped it (fittedGain).
|
|
84
84
|
*/
|
|
85
85
|
declare function absoluteLoudnessPlan(track: NormalizationTrack, targetLufs?: number): AbsoluteLoudnessPlan;
|
|
86
86
|
type AbsoluteOutcome = "matched" | "held" | "change";
|
package/dist/helpers/loudness.js
CHANGED
|
@@ -12,8 +12,8 @@ import {
|
|
|
12
12
|
requiredFfmpeg,
|
|
13
13
|
resolveLocalAudioPath,
|
|
14
14
|
updateAudioVolume
|
|
15
|
-
} from "../chunk-
|
|
16
|
-
import "../chunk-
|
|
15
|
+
} from "../chunk-NEGQQZNI.js";
|
|
16
|
+
import "../chunk-Y6EZUBYR.js";
|
|
17
17
|
import "../chunk-QF7MFJJU.js";
|
|
18
18
|
import "../chunk-SVQFE44C.js";
|
|
19
19
|
import "../chunk-L3HJVTQE.js";
|
|
@@ -15,8 +15,8 @@ import {
|
|
|
15
15
|
scanProjectMedia,
|
|
16
16
|
scanProjectMediaCodecMap,
|
|
17
17
|
shouldPrewarmProxy
|
|
18
|
-
} from "../chunk-
|
|
19
|
-
import "../chunk-
|
|
18
|
+
} from "../chunk-66YZA32N.js";
|
|
19
|
+
import "../chunk-Y6EZUBYR.js";
|
|
20
20
|
import "../chunk-QF7MFJJU.js";
|
|
21
21
|
import "../chunk-SVQFE44C.js";
|
|
22
22
|
import "../chunk-L3HJVTQE.js";
|
|
@@ -4,10 +4,10 @@ import {
|
|
|
4
4
|
isAutoProxyEnabled,
|
|
5
5
|
proxyEtagSalt,
|
|
6
6
|
resolvePreviewMediaCodecProbeCache
|
|
7
|
-
} from "../chunk-
|
|
8
|
-
import "../chunk-
|
|
9
|
-
import "../chunk-
|
|
10
|
-
import "../chunk-
|
|
7
|
+
} from "../chunk-IJMPYPJG.js";
|
|
8
|
+
import "../chunk-F4AQECCT.js";
|
|
9
|
+
import "../chunk-66YZA32N.js";
|
|
10
|
+
import "../chunk-Y6EZUBYR.js";
|
|
11
11
|
import "../chunk-QF7MFJJU.js";
|
|
12
12
|
import "../chunk-SVQFE44C.js";
|
|
13
13
|
import "../chunk-L3HJVTQE.js";
|
|
@@ -22,9 +22,9 @@ import {
|
|
|
22
22
|
retryProxyCopy,
|
|
23
23
|
sourceFolderKind,
|
|
24
24
|
waitForProxy
|
|
25
|
-
} from "../chunk-
|
|
26
|
-
import "../chunk-
|
|
27
|
-
import "../chunk-
|
|
25
|
+
} from "../chunk-F4AQECCT.js";
|
|
26
|
+
import "../chunk-66YZA32N.js";
|
|
27
|
+
import "../chunk-Y6EZUBYR.js";
|
|
28
28
|
import "../chunk-QF7MFJJU.js";
|
|
29
29
|
import "../chunk-SVQFE44C.js";
|
|
30
30
|
import "../chunk-L3HJVTQE.js";
|
package/dist/index.js
CHANGED
|
@@ -85,13 +85,13 @@ import {
|
|
|
85
85
|
requestedProjectPath,
|
|
86
86
|
requiredFfmpeg,
|
|
87
87
|
resolveLocalAudioPath
|
|
88
|
-
} from "./chunk-
|
|
88
|
+
} from "./chunk-NEGQQZNI.js";
|
|
89
89
|
import {
|
|
90
90
|
injectMediaCodecMap,
|
|
91
91
|
isAutoProxyEnabled,
|
|
92
92
|
proxyEtagSalt,
|
|
93
93
|
resolvePreviewMediaCodecProbeCache
|
|
94
|
-
} from "./chunk-
|
|
94
|
+
} from "./chunk-IJMPYPJG.js";
|
|
95
95
|
import {
|
|
96
96
|
CACHE_DIR_NAME,
|
|
97
97
|
PROXY_PENDING_RETRY_AFTER_SECONDS,
|
|
@@ -105,7 +105,7 @@ import {
|
|
|
105
105
|
resolveStill,
|
|
106
106
|
retryProxyCopy,
|
|
107
107
|
waitForProxy
|
|
108
|
-
} from "./chunk-
|
|
108
|
+
} from "./chunk-F4AQECCT.js";
|
|
109
109
|
import {
|
|
110
110
|
PROXY_VARIANT_CONFIG,
|
|
111
111
|
decideMediaProxyEligibility,
|
|
@@ -113,11 +113,11 @@ import {
|
|
|
113
113
|
probeAssetCodec,
|
|
114
114
|
recordProxyRequest,
|
|
115
115
|
resolveProxyVariantRequest
|
|
116
|
-
} from "./chunk-
|
|
116
|
+
} from "./chunk-66YZA32N.js";
|
|
117
117
|
import {
|
|
118
118
|
probeAudioTrack,
|
|
119
119
|
probeMediaMetadata
|
|
120
|
-
} from "./chunk-
|
|
120
|
+
} from "./chunk-Y6EZUBYR.js";
|
|
121
121
|
import {
|
|
122
122
|
folderGone,
|
|
123
123
|
isInHiddenOrVendorDir,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperframes/studio-server",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.17",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/heygen-com/hyperframes",
|
|
@@ -88,8 +88,8 @@
|
|
|
88
88
|
"linkedom": "^0.18.12",
|
|
89
89
|
"postcss": "^8.5.8",
|
|
90
90
|
"postcss-selector-parser": "^7.1.2",
|
|
91
|
-
"@hyperframes/
|
|
92
|
-
"@hyperframes/
|
|
91
|
+
"@hyperframes/parsers": "0.9.17",
|
|
92
|
+
"@hyperframes/core": "0.9.17"
|
|
93
93
|
},
|
|
94
94
|
"devDependencies": {
|
|
95
95
|
"@types/node": "^25.0.10",
|