video-fps-probe 0.2.0 → 0.4.0-beta.1
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/CHANGELOG.md +94 -0
- package/README.md +33 -15
- package/dist/api/codes.d.ts +6 -0
- package/dist/api/codes.js +88 -0
- package/dist/api/errors.js +1 -1
- package/dist/api/guards.d.ts +2 -0
- package/dist/api/guards.js +2 -1
- package/dist/check/index.js +12 -2
- package/dist/cjs/analyze.d.cts +9 -0
- package/dist/cjs/api/agreement.d.cts +18 -0
- package/dist/cjs/api/codes.d.cts +6 -0
- package/dist/cjs/api/errors.d.cts +12 -0
- package/dist/cjs/api/guards.d.cts +4 -0
- package/dist/cjs/api/summary.d.cts +22 -0
- package/dist/cjs/check/index.d.cts +60 -0
- package/dist/cjs/check.cjs +1 -0
- package/dist/cjs/chunks/build-CT5g6_bs.cjs +1 -0
- package/dist/cjs/chunks/compare-D7RVqlqf.cjs +1 -0
- package/dist/cjs/chunks/errors-0oBg1Ai6.cjs +1 -0
- package/dist/cjs/chunks/fallback-D9Ak2zFv.cjs +1 -0
- package/dist/cjs/chunks/guards-Dqpjtkdn.cjs +1 -0
- package/dist/cjs/chunks/inline-Bx-TQg8f.cjs +1 -0
- package/dist/cjs/chunks/isobmff-B_bb5rS5.cjs +1 -0
- package/dist/cjs/chunks/matroska-CLoGzfKA.cjs +1 -0
- package/dist/cjs/chunks/probe-DDLUfr23.cjs +1 -0
- package/dist/cjs/chunks/rational-Dq5Vza-Q.cjs +1 -0
- package/dist/cjs/chunks/reader-DNLR-_tm.cjs +1 -0
- package/dist/cjs/chunks/rfps-C-pyKnu8.cjs +1 -0
- package/dist/cjs/chunks/runtime-DtgT0xzU.cjs +1 -0
- package/dist/cjs/chunks/segments-i-hPN7H3.cjs +1 -0
- package/dist/cjs/chunks/sniff-CXwqOGwh.cjs +1 -0
- package/dist/cjs/chunks/stats-D68frkk2.cjs +1 -0
- package/dist/cjs/client/types.d.cts +96 -0
- package/dist/cjs/core/reader.d.cts +47 -0
- package/dist/cjs/core/types.d.cts +149 -0
- package/dist/cjs/engines/types.d.cts +69 -0
- package/dist/cjs/header/index.d.cts +25 -0
- package/dist/cjs/header/limits.d.cts +25 -0
- package/dist/cjs/header.cjs +1 -0
- package/dist/cjs/index.cjs +1 -0
- package/dist/cjs/index.d.cts +13 -0
- package/dist/cjs/plugins/types.d.cts +17 -0
- package/dist/cjs/presets/index.d.cts +19 -0
- package/dist/cjs/presets.cjs +1 -0
- package/dist/cjs/probe.d.cts +3 -0
- package/dist/cjs/url/index.d.cts +47 -0
- package/dist/cjs/url.cjs +1 -0
- package/dist/cjs/worker/protocol.d.cts +47 -0
- package/dist/client/compare.js +2 -2
- package/dist/client/create.d.ts +3 -0
- package/dist/client/create.js +8 -0
- package/dist/client/fallback.js +2 -2
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/inline.js +1 -1
- package/dist/client/lazy.js +1 -1
- package/dist/client/probe-client.js +4 -0
- package/dist/client/probe.js +23 -22
- package/dist/client/route.js +20 -0
- package/dist/client/split.js +9 -0
- package/dist/client/stub.js +2 -2
- package/dist/client/types.d.ts +1 -0
- package/dist/core/analyze.js +4 -78
- package/dist/core/dispatch.js +65 -0
- package/dist/core/isobmff/boxes.js +6 -8
- package/dist/core/isobmff/build.js +398 -0
- package/dist/core/isobmff/header.js +654 -0
- package/dist/core/isobmff/index.js +9 -378
- package/dist/core/isobmff/model.js +1 -1
- package/dist/core/isobmff/moof.js +2 -2
- package/dist/core/isobmff/moov.js +14 -9
- package/dist/core/isobmff/track.js +4 -2
- package/dist/core/matroska/ebml.js +1 -1
- package/dist/core/matroska/header.js +9 -4
- package/dist/core/matroska/index.js +7 -3
- package/dist/core/reader.d.ts +3 -12
- package/dist/core/reader.js +21 -32
- package/dist/core/result.js +1 -0
- package/dist/core/stats.js +3 -0
- package/dist/core/types.d.ts +15 -1
- package/dist/engines/common.js +8 -0
- package/dist/engines/mediabunny.js +17 -4
- package/dist/engines/mp4box.js +2 -1
- package/dist/header/index.d.ts +25 -0
- package/dist/header/index.js +146 -0
- package/dist/header/limits.d.ts +25 -0
- package/dist/header/limits.js +140 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/plugins/own.js +5 -2
- package/dist/presets/index.d.ts +1 -0
- package/dist/presets/sources.json +8 -0
- package/dist/probe.js +2 -1
- package/dist/react/store.js +1 -1
- package/dist/standalone/own-isobmff.worker.js +1 -0
- package/dist/standalone/own-matroska.worker.js +1 -0
- package/dist/standalone/own.worker.js +1 -1
- package/dist/url/index.d.ts +47 -0
- package/dist/url/index.js +335 -0
- package/dist/worker/own-isobmff.worker.js +4 -0
- package/dist/worker/own-matroska.worker.js +4 -0
- package/dist/worker/runtime.d.ts +2 -3
- package/dist/worker/runtime.js +2 -97
- package/dist/worker/serve.d.ts +3 -0
- package/dist/worker/serve.js +97 -0
- package/dist/worker/single.js +14 -0
- package/package.json +85 -2
- package/dist/client/inline.d.ts +0 -19
- package/dist/client/pool.d.ts +0 -29
- package/dist/client/probe.d.ts +0 -29
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import type { BlobReader } from './reader.cjs';
|
|
2
|
+
export type Provenance = 'header' | 'exact' | 'sampled' | 'estimated' | 'computed';
|
|
3
|
+
export interface Rational {
|
|
4
|
+
num: number;
|
|
5
|
+
den: number;
|
|
6
|
+
value: number;
|
|
7
|
+
}
|
|
8
|
+
export type NominalKind = 'exact' | 'snapped' | 'declared';
|
|
9
|
+
export interface NominalFps extends Rational {
|
|
10
|
+
kind: NominalKind;
|
|
11
|
+
}
|
|
12
|
+
export interface DominantFps {
|
|
13
|
+
rate: Rational;
|
|
14
|
+
share: number;
|
|
15
|
+
}
|
|
16
|
+
export type ContainerKind = 'mp4' | 'mov' | 'webm' | 'matroska' | 'unknown';
|
|
17
|
+
export type EngineId = 'own' | 'mediabunny' | 'mp4box' | 'rvfc';
|
|
18
|
+
export type MatroskaMode = 'header' | 'probe' | 'exact' | 'auto';
|
|
19
|
+
export type ScanDepth = 'header' | 'probe' | 'exact';
|
|
20
|
+
export type TrackField = 'codec' | 'dimensions' | 'rotation' | 'frames' | 'presentedFrames' | 'durationSec' | 'startSec' | 'avgFps' | 'nominalFps' | 'dominantFps' | 'minFps' | 'maxFps' | 'isVfr' | 'duplicateTimestamps' | 'medianFrameInterval';
|
|
21
|
+
export type RateSegmentKind = 'steady' | 'variable';
|
|
22
|
+
export interface RateSegment {
|
|
23
|
+
startSec: number;
|
|
24
|
+
endSec: number;
|
|
25
|
+
frames: number;
|
|
26
|
+
fps: number;
|
|
27
|
+
kind: RateSegmentKind;
|
|
28
|
+
}
|
|
29
|
+
export type RateAnomalyKind = 'rate-change' | 'freeze' | 'burst' | 'gap';
|
|
30
|
+
export interface RateAnomaly {
|
|
31
|
+
kind: RateAnomalyKind;
|
|
32
|
+
atSec: number;
|
|
33
|
+
durationSec: number;
|
|
34
|
+
fromFps: number;
|
|
35
|
+
toFps: number;
|
|
36
|
+
}
|
|
37
|
+
export interface RateBuckets {
|
|
38
|
+
bucketSec: number;
|
|
39
|
+
fps: number[];
|
|
40
|
+
}
|
|
41
|
+
export interface RateProfile {
|
|
42
|
+
segments: RateSegment[];
|
|
43
|
+
moreSegments: number;
|
|
44
|
+
anomalies: RateAnomaly[];
|
|
45
|
+
moreAnomalies: number;
|
|
46
|
+
buckets: RateBuckets;
|
|
47
|
+
uneven: boolean;
|
|
48
|
+
dominantShare: number;
|
|
49
|
+
summary: string;
|
|
50
|
+
}
|
|
51
|
+
export interface VideoTrackResult {
|
|
52
|
+
trackId: number;
|
|
53
|
+
fourccOrCodecId: string;
|
|
54
|
+
codecName: string;
|
|
55
|
+
width: number;
|
|
56
|
+
height: number;
|
|
57
|
+
rotation: number;
|
|
58
|
+
timescale: number;
|
|
59
|
+
frames: number | null;
|
|
60
|
+
presentedFrames: number | null;
|
|
61
|
+
durationSec: number | null;
|
|
62
|
+
startSec: number | null;
|
|
63
|
+
avgFps: Rational | null;
|
|
64
|
+
avgFpsFfprobe: string | null;
|
|
65
|
+
rFpsFfprobe: string | null;
|
|
66
|
+
nominalFps: NominalFps | null;
|
|
67
|
+
dominantFps: DominantFps | null;
|
|
68
|
+
minFps: Rational | null;
|
|
69
|
+
maxFps: Rational | null;
|
|
70
|
+
isVfr: boolean | null;
|
|
71
|
+
vfrRatio: number;
|
|
72
|
+
distinctDeltas: number;
|
|
73
|
+
duplicateTimestamps: number;
|
|
74
|
+
timestampsQuantized: boolean;
|
|
75
|
+
isStill: boolean;
|
|
76
|
+
rateProfile?: RateProfile | null;
|
|
77
|
+
medianFrameInterval?: Rational | null;
|
|
78
|
+
enabled?: boolean;
|
|
79
|
+
provenance: Partial<Record<TrackField, Provenance>>;
|
|
80
|
+
warnings: string[];
|
|
81
|
+
}
|
|
82
|
+
export interface IoStats {
|
|
83
|
+
reads: number;
|
|
84
|
+
bytesRead: number;
|
|
85
|
+
ms: number;
|
|
86
|
+
}
|
|
87
|
+
export type FileErrorCode = 'empty-file' | 'unrecognised' | 'unsupported-container' | 'unsupported' | 'too-large' | 'no-header' | 'truncated' | 'corrupt' | 'no-duration' | 'byte-limit' | 'read-limit' | 'time-limit' | 'structure-limit' | 'read-failed' | 'load-failed' | 'engine-failed' | 'internal';
|
|
88
|
+
export type LayoutKind = 'faststart' | 'moov-at-end' | 'fragmented';
|
|
89
|
+
export interface ContainerLayout {
|
|
90
|
+
kind: LayoutKind;
|
|
91
|
+
moovOffset: number | null;
|
|
92
|
+
moovSize: number | null;
|
|
93
|
+
sidx: boolean;
|
|
94
|
+
emptyMoov: boolean;
|
|
95
|
+
}
|
|
96
|
+
export type WarningCode = 'invalid-timescale' | 'truncated-mdat' | 'truncated-box' | 'trailing-data' | 'fragment-index-unread' | 'duration-unresolved' | 'fragments-ignored' | 'structure-limit';
|
|
97
|
+
export interface FileResult {
|
|
98
|
+
name: string;
|
|
99
|
+
size: number;
|
|
100
|
+
container: ContainerKind;
|
|
101
|
+
brand: string | null;
|
|
102
|
+
formatDurationSec: number | null;
|
|
103
|
+
scan: ScanDepth | null;
|
|
104
|
+
videoTracks: VideoTrackResult[];
|
|
105
|
+
audioTrackCount: number;
|
|
106
|
+
primaryTrackId: number | null;
|
|
107
|
+
error: string | null;
|
|
108
|
+
errorCode?: FileErrorCode | null;
|
|
109
|
+
layout?: ContainerLayout | null;
|
|
110
|
+
warnings: string[];
|
|
111
|
+
io: IoStats;
|
|
112
|
+
engine: EngineId;
|
|
113
|
+
}
|
|
114
|
+
export type ContainerAnalysis = Omit<FileResult, 'name' | 'size' | 'io' | 'engine'>;
|
|
115
|
+
export interface ReadAheadSizes {
|
|
116
|
+
head: number;
|
|
117
|
+
fragment: number;
|
|
118
|
+
matroskaScan: number;
|
|
119
|
+
tail: number;
|
|
120
|
+
}
|
|
121
|
+
export interface CostModel {
|
|
122
|
+
perReadMs: number;
|
|
123
|
+
bytesPerMs: number;
|
|
124
|
+
perBlockMs: number;
|
|
125
|
+
}
|
|
126
|
+
export interface AnalyzeProgress {
|
|
127
|
+
phase: string;
|
|
128
|
+
done: number;
|
|
129
|
+
total: number;
|
|
130
|
+
}
|
|
131
|
+
export interface AnalyzeOptions {
|
|
132
|
+
signal?: AbortSignal | null;
|
|
133
|
+
matroskaMode?: MatroskaMode;
|
|
134
|
+
exactBudgetMs?: number;
|
|
135
|
+
readAhead?: Partial<ReadAheadSizes>;
|
|
136
|
+
costModel?: Partial<CostModel>;
|
|
137
|
+
maxExpandedSamples?: number;
|
|
138
|
+
onProgress?: ((progress: AnalyzeProgress) => void) | null;
|
|
139
|
+
}
|
|
140
|
+
export interface ResolvedAnalyzeOptions {
|
|
141
|
+
signal: AbortSignal | null;
|
|
142
|
+
matroskaMode: MatroskaMode;
|
|
143
|
+
exactBudgetMs: number;
|
|
144
|
+
readAhead: ReadAheadSizes;
|
|
145
|
+
costModel: CostModel;
|
|
146
|
+
maxExpandedSamples: number;
|
|
147
|
+
onProgress: ((progress: AnalyzeProgress) => void) | null;
|
|
148
|
+
}
|
|
149
|
+
export type ContainerAnalyzer = (reader: BlobReader, opts: ResolvedAnalyzeOptions) => Promise<ContainerAnalysis>;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { FileResult } from '../core/types.cjs';
|
|
2
|
+
export interface Mp4boxTrackNative {
|
|
3
|
+
trackId: number;
|
|
4
|
+
sampleCount: number;
|
|
5
|
+
sttsLastDelta: number | null;
|
|
6
|
+
lastSampleDuration: number | null;
|
|
7
|
+
lastDurationOverridden: boolean;
|
|
8
|
+
fpsFromSampleDurations: number | null;
|
|
9
|
+
fpsFromMdhdDuration: number | null;
|
|
10
|
+
fpsFromMovieDuration: number | null;
|
|
11
|
+
}
|
|
12
|
+
export interface Mp4boxNative {
|
|
13
|
+
version: string;
|
|
14
|
+
fedBuffers: number;
|
|
15
|
+
fedBytes: number;
|
|
16
|
+
moofsFed: number;
|
|
17
|
+
tracks: Mp4boxTrackNative[];
|
|
18
|
+
}
|
|
19
|
+
export type Mp4boxResult = FileResult & {
|
|
20
|
+
mp4box?: Mp4boxNative;
|
|
21
|
+
};
|
|
22
|
+
export interface MediabunnyTrackNative {
|
|
23
|
+
trackId: number;
|
|
24
|
+
packetCount: number;
|
|
25
|
+
averagePacketRate: number | null;
|
|
26
|
+
nativeCallSkipped: boolean;
|
|
27
|
+
timeResolution: number;
|
|
28
|
+
fpsFromTimestamps: number | null;
|
|
29
|
+
}
|
|
30
|
+
export interface MediabunnyNative {
|
|
31
|
+
version: string;
|
|
32
|
+
formatName: string;
|
|
33
|
+
tracks: MediabunnyTrackNative[];
|
|
34
|
+
nativeStatsMs: number;
|
|
35
|
+
}
|
|
36
|
+
export type MediabunnyResult = FileResult & {
|
|
37
|
+
mediabunny?: MediabunnyNative;
|
|
38
|
+
};
|
|
39
|
+
export interface RvfcPass {
|
|
40
|
+
rate: number;
|
|
41
|
+
callbacks: number;
|
|
42
|
+
fps: number;
|
|
43
|
+
avg: number;
|
|
44
|
+
pairs: number;
|
|
45
|
+
spread: number;
|
|
46
|
+
lo: number;
|
|
47
|
+
hi: number;
|
|
48
|
+
}
|
|
49
|
+
export type RvfcConfidence = 'high' | 'medium' | 'low';
|
|
50
|
+
export interface RvfcDetails {
|
|
51
|
+
displayHz: number;
|
|
52
|
+
rate: number;
|
|
53
|
+
passes: RvfcPass[];
|
|
54
|
+
confidence: RvfcConfidence;
|
|
55
|
+
flags: string[];
|
|
56
|
+
}
|
|
57
|
+
export interface RvfcOptions {
|
|
58
|
+
host: HTMLElement;
|
|
59
|
+
name?: string;
|
|
60
|
+
signal?: AbortSignal;
|
|
61
|
+
onProgress?: (p: {
|
|
62
|
+
phase: string;
|
|
63
|
+
done: number;
|
|
64
|
+
total: number;
|
|
65
|
+
}) => void;
|
|
66
|
+
}
|
|
67
|
+
export type RvfcResult = FileResult & {
|
|
68
|
+
rvfc?: RvfcDetails;
|
|
69
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type BlobLike } from '../core/reader.cjs';
|
|
2
|
+
import type { FileResult, IoStats } from '../core/types.cjs';
|
|
3
|
+
import { type HeaderLimits } from './limits.cjs';
|
|
4
|
+
export { errorClass, errorCodeOf, warningCodeOf, warningCodesOf, type ErrorClass } from '../api/codes.cjs';
|
|
5
|
+
export { isAbortError, isTimeoutError } from '../api/guards.cjs';
|
|
6
|
+
export { HEADER_DEFAULT_LIMITS, type HeaderLimits } from './limits.cjs';
|
|
7
|
+
export type { BlobLike, BlobSlice } from '../core/reader.cjs';
|
|
8
|
+
export type * from '../core/types.cjs';
|
|
9
|
+
export type FragmentedDuration = 'header' | 'index';
|
|
10
|
+
export interface HeaderOptions {
|
|
11
|
+
name?: string;
|
|
12
|
+
signal?: AbortSignal | null;
|
|
13
|
+
limits?: HeaderLimits;
|
|
14
|
+
readAhead?: {
|
|
15
|
+
head?: number;
|
|
16
|
+
tail?: number;
|
|
17
|
+
};
|
|
18
|
+
fragmentedDuration?: FragmentedDuration;
|
|
19
|
+
}
|
|
20
|
+
export type HeaderResult = FileResult & {
|
|
21
|
+
io: IoStats & {
|
|
22
|
+
bytesRequested: number;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
export declare function analyzeHeader(source: Blob | BlobLike, options?: HeaderOptions): Promise<HeaderResult>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { BlobLike, BlobSlice } from '../core/reader.cjs';
|
|
2
|
+
export interface HeaderLimits {
|
|
3
|
+
maxBytes?: number;
|
|
4
|
+
maxReads?: number;
|
|
5
|
+
maxMs?: number;
|
|
6
|
+
maxTracks?: number;
|
|
7
|
+
maxTopLevelBoxes?: number;
|
|
8
|
+
}
|
|
9
|
+
export declare const HEADER_DEFAULT_LIMITS: Readonly<Required<HeaderLimits>>;
|
|
10
|
+
export type LimitCode = 'byte-limit' | 'read-limit' | 'time-limit' | 'structure-limit';
|
|
11
|
+
export declare class LimitError extends Error {
|
|
12
|
+
readonly errorCode: LimitCode;
|
|
13
|
+
constructor(code: LimitCode, message: string);
|
|
14
|
+
}
|
|
15
|
+
export declare class LimitedSource implements BlobLike {
|
|
16
|
+
#private;
|
|
17
|
+
readonly size: number;
|
|
18
|
+
constructor(inner: BlobLike, size: number, limits: Required<Pick<HeaderLimits, 'maxBytes' | 'maxReads' | 'maxMs'>>, caller: AbortSignal | null);
|
|
19
|
+
get bytesLeft(): number;
|
|
20
|
+
get bytesRequested(): number;
|
|
21
|
+
checkTime(): void;
|
|
22
|
+
take(): LimitError | null;
|
|
23
|
+
slice(start?: number, end?: number): BlobSlice;
|
|
24
|
+
dispose(): void;
|
|
25
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./chunks/guards-Dqpjtkdn.cjs"),t=require("./chunks/reader-DNLR-_tm.cjs"),n=require("./chunks/build-CT5g6_bs.cjs"),r=require("./chunks/sniff-CXwqOGwh.cjs"),i=67108864,a=new Set([`sidx`,`styp`,`prft`,`emsg`]);function count$1(e){if(++e.boxes>e.plan.maxTopLevelBoxes)throw new n.C(`structure-limit`,`limit reached: maxTopLevelBoxes ${e.plan.maxTopLevelBoxes}`)}function visit(e){e.budget.checkTime(),count$1(e)}function ahead(e,t,n){return Math.max(0,Math.min(n,e.budget.bytesLeft,e.reader.size-t))}async function peekAt(e,t,n){let r=Math.min(16,e.reader.size-t);return e.reader.peek(t,r)??e.reader.view(t,r,ahead(e,t,n))}function countTracks(e,t){let r;try{r=n.T(e,0,e.byteLength)}catch{return}if(!r)return;let i=0;for(let a=r.start+r.headerSize;a+8<=r.end;){let o;try{o=n.T(e,a,r.end)}catch{return}if(!o)return;if(o.type===`trak`&&++i>t)throw new n.C(`structure-limit`,`limit reached: maxTracks ${t}`);a=o.start+o.size}}function mehdDuration(e){let t;try{t=n.T(e,0,e.byteLength)}catch{return null}if(!t)return null;for(let r of n.w(e,t.start+t.headerSize,t.end))if(r.type===`mvex`)for(let t of n.w(e,r.start+r.headerSize,r.end)){let n=t.start+t.headerSize;if(t.type!==`mehd`||n+8>t.end)continue;if(e.getUint8(n)===1){if(n+12>t.end)return null;let r=e.getBigUint64(n+4);return r===0n||r>BigInt(2**53-1)?null:Number(r)}let r=e.getUint32(n+4);return r===0||r===4294967295?null:r}return null}async function readHeaderMoov(e,t){let r=e.reader.size,i=t.start+t.size>r;if(e.movie.moov=[t.start,t.size],!i&&t.size>268435456)throw new n.C(`too-large`,`unsupported: moov larger than 256 MiB`);let a=await e.reader.view(t.start,i?Math.min(r-t.start,n.r):t.size,i?0:ahead(e,t.start,t.size+32));return countTracks(a,e.plan.maxTracks),await n.f(e.reader,t,e.movie),a}async function walkTop(e){let{reader:r,movie:i}=e,o=r.size,s=0,c=null,l=!1,u=0,d=!1,walked=(e,t=null)=>({moov:c,fragments:i.hasMvex&&(e!==null||t!==null),firstMoof:e,run:e?e.start-u:0,limit:t});try{for(;s+8<=o;){let f=c!==null&&!i.hasMvex,p;if(f){if(p=r.peek(s,Math.min(16,o-s)),!p){if(d)break;d=!0;try{p=await peekAt(e,s,e.plan.head)}catch(t){if(n.c(t,r))throw t;let a=e.budget.take();if(a)throw a;i.warnings.push(`fragments ignored: read at ${s} after a moov without an mvex failed: ${t.message}`);break}}if(++e.boxes>e.plan.maxTopLevelBoxes){i.warnings.push(`too many top-level boxes`);break}}else visit(e),p=await peekAt(e,s,l&&!c?e.plan.tail:e.plan.head);let m;try{m=n.T(p,0,o-s,s)}catch(e){if(!c)throw e;i.warnings.push(`ignored trailing data at ${s}: ${e.message}`);break}if(!m)break;let h={...m,start:s,end:Math.min(s+m.size,o)},g=m.truncated&&c!==null&&h.type!==`mdat`&&h.type!==`moof`;m.truncated&&(i.truncated=!0,i.warnings.push(g?`ignored trailing data at ${s}: not a box`:`'${h.type}' at ${s} is truncated`));let _={type:h.type,start:s,end:h.end,sidx:null};if(i.topLevel.push(_),f&&h.type===`moof`&&i.warnings.push(`fragments ignored: moof at ${s} after a moov without an mvex`),g||f&&h.type===`moof`||c&&h.type===`mfra`)break;if(c&&h.type===`moof`)return walked(h);if(h.type===`ftyp`&&!f){let n=await r.view(s,Math.min(h.size,1024),ahead(e,s,e.plan.head)),a=h.headerSize;if(a+4<=n.byteLength){i.hasFtyp=!0,i.brand=t.n(n,a);for(let e=a+8;e+4<=n.byteLength;e+=4)i.compatibleBrands.push(t.n(n,e))}}else if(h.type===`moov`)c?i.warnings.push(`ignored a second moov at ${s}`):c=await readHeaderMoov(e,h);else if(h.type===`moof`)throw new n.C(`corrupt`,`corrupt file: movie fragment (moof) before moov`);else if(h.type===`sidx`&&!m.truncated){let t=f?r.peek(s,h.size):await r.view(s,h.size,ahead(e,s,e.plan.head)),i=t&&n.T(t,0,t.byteLength);i&&(_.sidx=n.S(t,i,s))}a.has(h.type)||(u=s+m.size),l=h.type===`mdat`,s+=m.size}}catch(t){if(!c||n.c(t,r))throw t;let i=e.budget.take()??(isLimit(t)?t:null);if(!i)throw t;return walked(null,i)}if(!c)throw new n.C(`no-header`,`incomplete file: no moov`);return walked(null)}async function readTailIndex(e){let{reader:r}=e,i=r.size,a=Math.min(i,65536);if(a<16)return null;let o=r.peek(i-a,a)??await r.view(i-a,a,0);if(t.n(o,a-12)!==`mfro`)return null;let s=o.getUint32(a-4);if(s<24||s>Math.min(i,16777216))return null;let c=s<=a?new DataView(o.buffer,o.byteOffset+a-s,s):await r.view(i-s,s,0),l;try{l=n.T(c,0,s)}catch{return null}if(!l||l.type!==`mfra`)return null;let u=new Map;for(let e of n.w(c,l.headerSize,l.end)){let t=e.start+e.headerSize;if(e.type!==`tfra`||t+8>e.end)continue;let r=c.getUint32(t+4),a=u.get(r)??new Set;n.y(c,e.start,e.end,a,i-s),a.size>0&&u.set(r,a)}return{mfraSize:s,offsets:u}}function parseFragment(e,r,i,a){let o=n.T(r,0,r.byteLength);o&&n.x(r,o,i,e.movie.moofCount,e.movie,t.c),e.movie.moofCount++,a.add(i)}async function walkFragments(e,t,r,a){let o=t;for(;o+8<=r;){visit(e);let t=await peekAt(e,o,e.plan.head),s;try{s=n.T(t,0,r-o,o)}catch{return!1}if(!s||s.truncated)return!1;if(s.type===`mfra`)return!0;if(s.type===`moof`&&!a.has(o)){if(s.size>i)throw new n.C(`too-large`,`corrupt file: moof larger than 64 MiB at ${o}`);parseFragment(e,await e.reader.view(o,s.size,ahead(e,o,e.plan.head)),o,a)}else s.type===`sidx`&&e.movie.topLevel.push({type:`sidx`,start:o,end:o+s.size,sidx:n.S(await e.reader.view(o,s.size,ahead(e,o,e.plan.head)),s,o)});o+=s.size}return!0}function runTo(e,t,r){let i=[];try{for(let o=t;o<e.byteLength;){let t=n.T(e,o,e.byteLength);if(!t||t.truncated||!a.has(t.type))return null;i.push({type:t.type,start:r+o,end:r+t.end,sidx:t.type===`sidx`?n.S(e,t,r+o):null}),o=t.end}}catch{return null}return i}async function preRun(e,t,n,r){let i=Math.max(r,t-Math.max(n,4096));if(t-i<8)return t;let a=await e.reader.view(i,t-i,ahead(e,i,e.plan.head));for(let t=0;t+8<=a.byteLength;t++){let n=a.getUint32(t+4)===1936286840?runTo(a,t,i):null;if(n){for(let t of n)count$1(e),e.movie.topLevel.push(t);return i+t}}return t}function lastSidx(e,t,n){let r=null;for(let i of e.topLevel){let e=i.sidx;e&&(!r||i.start>r.start)&&!e.hierarchical&&e.referenceId===t.id&&e.timescale===t.timescale&&(!n||n.includes(e.coverageEnd))&&(r=i)}return r}function timingOf(e,t,r){let i={...e,fragments:r},a=n._(i);return n.v(i,t,a,n.h(i,t.timescale,a))}function hasFragmentData(e,t,n){return t.trex.has(e.id)||(n?.offsets.has(e.id)??!1)||t.topLevel.some(t=>t.sidx?.referenceId===e.id)}function stillFragmented(e,t,r,i,a){if(!a)return!1;let o=n._({...e,fragments:[]}).moovSamples;for(let t of e.fragments)o+=t.count;if(o>1)return!1;for(let t of r?.offsets.get(e.id)??[])if(!i.has(t))return!1;for(let n of t.topLevel)if(n.sidx?.referenceId===e.id&&n.sidx.subsegments.some(([e])=>!i.has(e)))return!1;let s=e.fragments[0],c=t.trex.get(e.id)?.duration||(s?s.durations?.[0]??s.defaultDuration:0);return c>0&&a.ticks*e.timescale<=c*a.timescale}function fragmentedPrimary(e){let t=null,rank=e=>e.auxiliary?.5:e.enabled?2:1,area=e=>e.result.width*e.result.height;for(let n of e)t?rank(n)===rank(t)?n.result.isStill===t.result.isStill?area(n)===area(t)?n.result.trackId<t.result.trackId&&(t=n):t=area(n)>area(t)?n:t:t=t.result.isStill?n:t:t=rank(n)>rank(t)?n:t:t=n;return t?t.result.trackId:null}function headerTrack(e){e.presentedFrames=null,delete e.provenance.presentedFrames}function isLimit(e){let t=e?.errorCode;return typeof t==`string`&&t.endsWith(`-limit`)}function cachedBox(e,t){let r=e.size,i=t+8<=r?e.peek(t,Math.min(16,r-t)):null;try{let e=i&&n.T(i,0,r-t,t);return e&&!e.truncated?e:null}catch{return null}}function onlyFragment(e,t){let n=t.start+t.size,r=cachedBox(e,n);if(n===e.size)return!0;if(r?.type!==`mdat`)return!1;let i=n+r.size,a=cachedBox(e,i);return i===e.size||a?.type===`mfra`&&i+a.size===e.size}function lastOffset(e){let t=null;for(let n of e??[])(t===null||n>t)&&(t=n);return t}async function resolveFragmented(e,r,a,o,s){let{reader:c,plan:l,movie:u}=e,d=c.size,isMain=e=>a.includes(e)||e.handler===`soun`,f=u.tracks.filter(e=>isMain(e)||e.hasMdhd),p=new Map,m=new Map,h=new Set,g=new Set,_=new Map,v=r.firstMoof,y=null,b=r.limit,x=!1,S=d,open=()=>f.filter(e=>!p.has(e)&&(a.includes(e)||hasFragmentData(e,u,y))),settle=(e,t,n,r=p)=>{let i=lastSidx(u,e,t)?.sidx,a=i?i.earliestPts+i.durationSum:0,o=timingOf(e,u,n).durationTs;(t?!i:!x&&i&&i.start<S&&i.coverageEnd<=S&&a<o)||r.set(e,{ticks:Math.max(o,a),timescale:e.timescale,provenance:`header`})},bySidx=e=>{let t=open(),n=t.filter(t=>lastSidx(u,t,e)!==null);h.clear();for(let r of t)n.includes(r)?settle(r,e,r.fragments,n.length===t.length?p:m):h.add(r)};try{let t=v?c.peek(v.start,v.size)===null:!1;if(v&&!b&&(l.index||!t||open().some(e=>lastSidx(u,e,[d])!==null))){if(v.size>i)throw new n.C(`too-large`,`corrupt file: moof larger than 64 MiB at ${v.start}`);if(parseFragment(e,c.peek(v.start,v.size)??await c.view(v.start,v.size,ahead(e,v.start,l.head)),v.start,g),x=onlyFragment(c,v),x)for(let e of open())settle(e,null,e.fragments);else bySidx([d])}for(let e of f)_.set(e,e.fragments.length);if(l.index&&!b&&open().length>0&&(y=await readTailIndex(e),y)){let t=d-y.mfraSize;bySidx([d,t]),S=t;let n=!0;for(let t of[!0,!1]){let i=null;for(let e of open()){let n=isMain(e)===t?lastOffset(y.offsets.get(e.id)):null;n!==null&&n<S&&(i===null||n<i)&&(i=n)}if(i===null)continue;let a=f.map(e=>e.fragments.length),o=t&&v?await preRun(e,i,r.run,v.start+v.size):i;n=await walkFragments(e,i,S,g)&&n,f.forEach((e,t)=>{let n=e.fragments,r=_.get(e)??0;e.fragments=[...n.slice(0,r),...n.slice(a[t]),...n.slice(r,a[t])]}),S=o,x=n&&v!==null&&i<=v.start;for(let e of open()){let t=e.fragments.slice(_.get(e));(x||t.length>0&&t[0].baseDts!==null)&&settle(e,null,e.fragments)}}}}catch(t){if(n.c(t,c)||(b=e.budget.take()??(isLimit(t)?t:null),!b))throw t;for(let e of f)e.fragments.length=_.get(e)??0}let C=r.moov?mehdDuration(r.moov):null,w=open().length>0?b:null,T=[...m.keys()].filter(e=>!p.has(e));for(let e of T)p.set(e,m.get(e));for(let e of open())C!==null&&p.set(e,{ticks:C,timescale:u.timescale,provenance:`estimated`});for(let e of f)!p.has(e)&&!a.includes(e)&&!hasFragmentData(e,u,y)&&p.set(e,{ticks:timingOf(e,u,[]).durationTs,timescale:e.timescale,provenance:`header`});a.forEach((e,t)=>{let n=o[t].result,r=p.get(e)??null,i=n.provenance;n.frames=null,n.avgFps=null,n.avgFpsFfprobe=null,n.startSec=null,n.durationSec=r?r.ticks/r.timescale:null,n.isStill=stillFragmented(e,u,y,g,r),delete i.frames,delete i.avgFps,delete i.startSec,r?i.durationSec=r.provenance:delete i.durationSec});let E=open(),D=a.length>0?!a.some(e=>p.has(e)):![...p.keys()].some(isMain)&&E.some(isMain);w&&!D&&s.warnings.push(`fragment index not read: ${w.message}`);let O=[...p].some(([e,t])=>isMain(e)&&e.hasMdhd&&t.provenance===`estimated`),loose=e=>!O&&!isMain(e)&&f.some(e=>isMain(e)&&e.hasMdhd),k=E.filter(loose),A=E.filter(e=>!loose(e)),j=[],M=2**53-1,N={start:M,end:-M,dur:-M},P={...N},F=!0,I=0;for(let[e,t]of p){if(!e.hasMdhd)continue;let r=timingOf(e,u,x?e.fragments:e.fragments.slice(0,_.get(e)??0)).startPts,i=t.provenance!==`estimated`,a=r!==null||x||!hasFragmentData(e,u,y);if(loose(e)&&!(i&&a)){k.push(e);continue}I++;let o=isMain(e)?N:P,s=n.g(t.ticks,1e6,t.timescale);if(o.dur=Math.max(o.dur,s),!i)F=!1;else if(r!==null){let t=n.g(r,1e6,e.timescale);o.start=Math.min(o.start,t),o.end=Math.max(o.end,t+s)}else a||j.push(e)}(N.start===M||N.start>P.start&&N.start-P.start<1e6)&&(N.start=P.start),(N.end===-M||N.end<P.end&&P.end-N.end<1e6)&&(N.end=P.end);let L=N.dur===-M||N.dur<P.dur&&P.dur-N.dur<1e6?P.dur:N.dur;F&&N.start!==M&&N.end!==-M&&N.end>=N.start&&(L=Math.max(L,N.end-N.start)),(!F||I<2)&&(j.length=0);let R=T.length>0?[...h].filter(e=>p.get(e)?.provenance!==`header`):[];T.length>0&&R.length===0&&R.push(...T);let z=new Set([...A,...j,...k,...R]);if(!D)for(let e of f)z.has(e)&&s.warnings.push(`format duration unresolved: track ${e.id}`);if(s.formatDurationSec=A.length+j.length+R.length===0&&L>0?L/1e6:null,s.primaryTrackId=fragmentedPrimary(o),D){let e=b??new n.C(`no-duration`,`incomplete header: fragmented file without a sidx, mfra or mehd duration`);s.error=e.message,s.errorCode=t.i(e)}}function collapse(e){let t=new Map;for(let n of e)t.set(n,(t.get(n)??0)+1);if(t.size!==e.length){e.length=0;for(let[n,r]of t)e.push(r>1?`${n} (${r} times)`:n)}}async function analyzeIsoBmffHeader(r,i,a){let o=n.b(r.size),s={reader:r,budget:i,plan:a,movie:o,boxes:0},c=e.c(`mp4`);c.scan=`header`,c.warnings=o.warnings,c.errorCode=null;try{let e=await walkTop(s);c.container=n.a(o),c.brand=o.brand;let r=o.ffprobeReject,i=r!==null&&r.moof<o.ffprobeMoofLimit;i&&o.warnings.push(`ffprobe 8.1.1 ${r.reason}`);let a=n.p(o.tracks);[...a].some(([e,t])=>e.id!==t)&&o.warnings.push(`duplicate track_ID values`);let l=o.tracks.filter(e=>n.l(e,o)),u=l.map(e=>n.i(e,o,{maxExpandedSamples:0},{reportedId:a.get(e)??e.id,ffprobeFails:i}));for(let e of u)headerTrack(e.result);c.videoTracks=u.map(e=>e.result),c.audioTrackCount=o.tracks.filter(e=>e.handler===`soun`).length,e.fragments?await resolveFragmented(s,e,l,u,c):(c.primaryTrackId=n.d(u),c.formatDurationSec=n.s(o,new Set(l)),e.limit&&(c.error=e.limit.message,c.errorCode=t.i(e.limit)))}catch(e){if(n.c(e,r))throw e;let a=i.take(),s=a?a.message:e instanceof Error?e.message:String(e);c.container=n.a(o),c.brand=o.brand,c.error=a||e?.errorCode?s:`corrupt file: ${s}`,c.errorCode=t.i(a??e),c.videoTracks=[],c.audioTrackCount=0,c.primaryTrackId=null,c.formatDurationSec=null}c.layout=n.u(o),collapse(c.warnings);for(let e of c.videoTracks)collapse(e.warnings);return c}const o=Object.freeze({maxBytes:16777216,maxReads:8,maxMs:1e4,maxTracks:64,maxTopLevelBoxes:1024});var LimitError=class extends Error{constructor(e,t){super(t),this.name=`LimitError`,this.errorCode=e}};const noop=()=>{};function sourceError(e){return Object.assign(Error(e),{name:``})}function bufferOf(e){if(ArrayBuffer.isView(e)){let t=new Uint8Array(e.buffer,e.byteOffset,e.byteLength);return t.byteOffset===0&&t.byteLength===t.buffer.byteLength&&t.buffer instanceof ArrayBuffer?t.buffer:t.slice().buffer}if(typeof e?.byteLength!=`number`)throw sourceError(`the source returned ${e===null?`null`:typeof e} instead of bytes`);return e}var LimitedSource=class{size;#e;#t;#n;#r;#i;#a=new AbortController;#o;#s;#c=()=>this.#a.abort(this.#s?.reason);#l=null;#u=!1;#d=0;#f=0;#p=null;constructor(e,t,n,r){this.size=t,this.#e=e,this.#t=n.maxBytes,this.#n=n.maxReads,this.#r=n.maxMs,this.#i=performance.now()+n.maxMs,this.#s=r;let i=this.#a.signal;this.#o=new Promise((e,t)=>i.addEventListener(`abort`,()=>t(i.reason),{once:!0})),this.#o.catch(noop),r&&(r.aborted?this.#c():r.addEventListener(`abort`,this.#c,{once:!0})),Number.isFinite(n.maxMs)&&!i.aborted&&this.#m(n.maxMs)}#m(e){let t=Math.min(e,2147483647);this.#l=setTimeout(()=>{if(e>t)return this.#m(e-t);this.#l=null,this.#u=!0,this.#a.abort(new LimitError(`time-limit`,`limit reached: maxMs ${this.#r}`))},t)}get bytesLeft(){return this.#t-this.#f}get bytesRequested(){return this.#f}checkTime(){if(this.#u||performance.now()>=this.#i)throw this.#h(`time-limit`,`limit reached: maxMs ${this.#r}`)}take(){let e=this.#p;return this.#p=null,e}slice(e=0,t=this.size){return{arrayBuffer:()=>this.#_(e,t)}}dispose(){this.#l!==null&&clearTimeout(this.#l),this.#l=null,this.#s?.removeEventListener(`abort`,this.#c)}#h(e,t){return this.#p=new LimitError(e,t),this.#p}#g(e,t){if(this.#d+1>this.#n)return this.#h(`read-limit`,`limit reached: maxReads ${this.#n}`);if(this.#f+e>this.#t)return this.#h(`byte-limit`,`limit reached: maxBytes ${this.#t} (${this.#f+e-this.#t} more bytes needed at offset ${t})`);try{this.checkTime()}catch(e){return e}return null}#_(e,t){let n=Math.max(0,t-e),r=this.#g(n,e);if(r)return Promise.reject(r);this.#d++,this.#f+=n;let i;try{let n=this.#e;i=Promise.resolve(typeof n.read==`function`?n.read(e,t,this.#a.signal):n.slice(e,t).arrayBuffer()).then(bufferOf)}catch(e){i=Promise.reject(e)}return i.catch(noop),Promise.race([i,this.#o]).catch(e=>{if(e instanceof LimitError)this.#p=e;else if(!this.#a.signal.aborted&&e?.name===`AbortError`)throw sourceError(String(e));throw e})}};function shown(e){return typeof e==`string`?JSON.stringify(e):typeof e==`object`&&e?Array.isArray(e)?`an array`:`an object`:String(e)}function count(e,t,n=0,r=!1){if(r&&e===1/0)return e;if(typeof e!=`number`||!Number.isSafeInteger(e)||e<n)throw TypeError(`${t} must be a non-negative safe integer${n>0?` of at least ${n}`:``}${r?` or Infinity`:``}, got ${shown(e)}`);return e}function record(e,t){if(e===void 0)return{};if(typeof e!=`object`||!e||Array.isArray(e))throw TypeError(`${t} must be an object, got ${shown(e)}`);return e}function resolveHeaderOptions(e,n){let r=e;if(typeof r!=`object`||!r||typeof r.slice!=`function`)throw TypeError(`source must be a Blob or a BlobLike with a size and a slice method, got ${shown(e)}`);let i=count(r.size,`source.size`),a=record(n,`options`);if(a.name!==void 0&&typeof a.name!=`string`)throw TypeError(`name must be a string, got ${shown(a.name)}`);let s=a.signal??null;if(s!==null){let e=s;if(typeof e!=`object`||typeof e.aborted!=`boolean`||typeof e.addEventListener!=`function`||typeof e.removeEventListener!=`function`)throw TypeError(`signal must be an AbortSignal, got ${shown(s)}`)}let c=record(a.limits,`limits`),l={...o};for(let e of Object.keys(o))c[e]!==void 0&&(l[e]=count(c[e],`limits.${e}`,0,!0));let u=record(a.readAhead,`readAhead`);for(let e of Object.keys(u))if(e!==`head`&&e!==`tail`&&u[e]!==void 0)throw TypeError(`readAhead takes only head and tail, got ${e}`);let d=u.head===void 0?t.s.head:count(u.head,`readAhead.head`,1024),f=u.tail===void 0?t.s.tail:count(u.tail,`readAhead.tail`,16),p=a.fragmentedDuration??`index`;if(p!==`index`&&p!==`header`)throw TypeError(`fragmentedDuration must be 'header' or 'index', got ${shown(p)}`);return{name:a.name??(typeof File<`u`&&e instanceof File?e.name:``),size:i,signal:s,limits:l,head:d,tail:f,index:p===`index`}}function docTypeOf(e){let t=4,vint=n=>{if(t>=e.length)return null;let r=e[t],i=1;for(;i<=8&&!(r&128>>i-1);)i++;if(i>8||t+i>e.length)return null;let a=n?r:r&255>>i;for(let n=1;n<i;n++)a=a*256+e[t+n];return t+=i,a},n=vint(!1);if(n===null)return null;let r=Math.min(e.length,t+n);for(;t<r;){let n=vint(!0),i=vint(!1);if(n===null||i===null||t+i>r)return null;if(n===17026)return String.fromCharCode(...e.subarray(t,t+i)).replace(/\0+$/,``);t+=i}return null}function failed(e,t,n){return e.error=t,e.errorCode=n,e}async function analyzeSource(t,n,i){let a={...e.c(`unknown`),scan:`header`,errorCode:null,layout:null},o=t.size;if(o===0)return failed(a,`not a recognised container: empty file`,`empty-file`);let s=await t.bytes(0,Math.min(1024,o),Math.min(o,i.head,n.bytesLeft)),c=r.n(s);if(c.kind===`unknown`)return failed(a,`not a recognised container: ${c.reason}`,`unrecognised`);if(c.kind===`matroska`)return a.container=docTypeOf(s)===`webm`?`webm`:`matroska`,failed(a,`container not read by the header entry: ${a.container}`,`unsupported-container`);let{maxTracks:l,maxTopLevelBoxes:u}=i.limits;return analyzeIsoBmffHeader(t,n,{head:i.head,tail:i.tail,maxTracks:l,maxTopLevelBoxes:u,index:i.index})}async function analyzeHeader(n,r={}){let i=performance.now(),a=resolveHeaderOptions(n,r);t.d(a.signal);let o=new LimitedSource(n,a.size,a.limits,a.signal),s=new t.t(o,{headWindow:a.head,signal:a.signal}),c;try{c=await analyzeSource(s,o,a)}catch(n){t.d(a.signal);let r=o.take();c={...e.c(`unknown`),scan:`header`,errorCode:null,layout:null},failed(c,r?r.message:n instanceof Error?n.message||n.name:String(n),t.i(r??n))}finally{o.dispose()}return{name:a.name,size:a.size,...c,io:{reads:s.reads,bytesRead:s.bytesFetched,ms:performance.now()-i,bytesRequested:o.bytesRequested},engine:`own`}}exports.HEADER_DEFAULT_LIMITS=o,exports.analyzeHeader=analyzeHeader,exports.errorClass=e.i,exports.errorCodeOf=e.a,exports.isAbortError=e.t,exports.isTimeoutError=e.r,exports.warningCodeOf=e.o,exports.warningCodesOf=e.s;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./chunks/guards-Dqpjtkdn.cjs"),t=require("./chunks/probe-DDLUfr23.cjs"),n=require("./chunks/errors-0oBg1Ai6.cjs");async function analyze(e,t={}){let{name:n=typeof File<`u`&&e instanceof File?e.name:``,engine:r,...i}=t;return(r?await r.load():(await Promise.resolve().then(()=>require("./chunks/runtime-DtgT0xzU.cjs"))).ownEngine).run(e,n,i)}const r=new Set([`estimated`,`sampled`]);function summarize(e){let t=e.primaryTrackId===null?null:e.videoTracks.find(t=>t.trackId===e.primaryTrackId)??null,n=e.error??(t?t.avgFps?null:`no frame rate`:`no video track`);return{ok:n===null,name:e.name,container:e.container,engine:e.engine,fps:t?.avgFps?.value??null,fpsRational:t?.avgFps?`${t.avgFps.num}/${t.avgFps.den}`:null,nominalFps:t?.nominalFps?.value??null,frames:t?t.presentedFrames??t.frames:null,durationSec:t?.durationSec??e.formatDurationSec,width:t?.width??null,height:t?.height??null,rotation:t?.rotation??null,isVfr:t?.isVfr??null,minFps:t?.minFps?.value??null,maxFps:t?.maxFps?.value??null,approximate:!!t&&[`avgFps`,`nominalFps`,`frames`].some(e=>r.has(t.provenance[e])),error:n,warnings:[...e.warnings,...t?.warnings??[]]}}const i=[`isobmff`,`matroska`],splitBy=e=>(t,n,r,a,o,s)=>{let pool=e=>{let i=n[e];return i?r(`${t}-${e}`,i):r(t,s)},c=e(pool,a,o);return{kind:t,pool:()=>c,warmPools:e=>i.filter(t=>e.includes(t)).map(e=>[pool(e),[e]])}},a=[26,69,223,163],familyOf=(e,t)=>new Promise(n=>{let iso=()=>n(`isobmff`);if(t?.aborted)return iso();t?.addEventListener(`abort`,iso),Promise.resolve().then(()=>e.slice(0,4).arrayBuffer()).then(e=>{let t=new Uint8Array(e);return a.every((e,n)=>t[n]===e)?`matroska`:`isobmff`}).then(n,iso).finally(()=>t?.removeEventListener(`abort`,iso))}),runByHead=(e,t,n)=>({run:(r,i)=>t(familyOf(r.file,i.signal),r.engine).then(a=>n.aborted?t(Promise.reject(),r.engine):e(a).run(r,i))}),o=splitBy(runByHead),s={id:`own`,thread:`worker`,createWorkers:{isobmff:()=>new Worker(new URL(`../worker/own-isobmff.worker.js`,require("url").pathToFileURL(__filename).href),{type:`module`}),matroska:()=>new Worker(new URL(`../worker/own-matroska.worker.js`,require("url").pathToFileURL(__filename).href),{type:`module`})},load:()=>Promise.resolve().then(()=>require("./chunks/runtime-DtgT0xzU.cjs")).then(e=>e.ownEngine)};function createProbe(e={}){let{engines:n=[],worker:r,...i}=e;return t.t({...i,engines:[s,...n],worker:r===`inline`?`inline`:void 0,split:o})}exports.ProbeError=n.t,exports.analyze=analyze,exports.compareResults=t.r,exports.createProbe=createProbe,exports.errorClass=e.i,exports.errorCodeOf=e.a,exports.isAbortError=e.t,exports.isTimeoutError=e.r,exports.summarize=summarize,exports.warningCodeOf=e.o,exports.warningCodesOf=e.s;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { analyze, type InThreadOptions } from './analyze.cjs';
|
|
2
|
+
export { compareResults, type Agreement, type ComparedField, type ComparedFieldName, type ComparedValue } from './api/agreement.cjs';
|
|
3
|
+
export { errorClass, errorCodeOf, warningCodeOf, warningCodesOf, type ErrorClass } from './api/codes.cjs';
|
|
4
|
+
export { ProbeError, type ProbeErrorCode } from './api/errors.cjs';
|
|
5
|
+
export { isAbortError, isTimeoutError } from './api/guards.cjs';
|
|
6
|
+
export { summarize, type VideoFpsSummary } from './api/summary.cjs';
|
|
7
|
+
export type * from './client/types.cjs';
|
|
8
|
+
export type { BlobLike, BlobSlice } from './core/reader.cjs';
|
|
9
|
+
export type * from './core/types.cjs';
|
|
10
|
+
export type { MediabunnyNative, MediabunnyTrackNative, Mp4boxNative, Mp4boxTrackNative, RvfcConfidence, RvfcDetails, RvfcPass } from './engines/types.cjs';
|
|
11
|
+
export type { ContainerFamily, EngineDescriptor, EngineModule, WorkerEngineId } from './plugins/types.cjs';
|
|
12
|
+
export type { WireOptions } from './worker/protocol.cjs';
|
|
13
|
+
export { createProbe } from './probe.cjs';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { BlobLike } from '../core/reader.cjs';
|
|
2
|
+
import type { AnalyzeOptions, EngineId, FileResult } from '../core/types.cjs';
|
|
3
|
+
export type { EngineId };
|
|
4
|
+
export type ContainerFamily = 'isobmff' | 'matroska';
|
|
5
|
+
export type WorkerEngineId = 'own' | 'mp4box' | 'mediabunny';
|
|
6
|
+
export interface EngineModule<R extends FileResult = FileResult> {
|
|
7
|
+
readonly run: (blob: BlobLike, name: string, opts: AnalyzeOptions) => Promise<R>;
|
|
8
|
+
readonly containers: readonly ContainerFamily[] | 'any';
|
|
9
|
+
readonly warm?: (containers: readonly ContainerFamily[]) => Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
export interface EngineDescriptor<Id extends EngineId = EngineId> {
|
|
12
|
+
readonly id: Id;
|
|
13
|
+
readonly thread: 'worker' | 'main';
|
|
14
|
+
readonly createWorker?: () => Worker;
|
|
15
|
+
readonly load: () => Promise<EngineModule>;
|
|
16
|
+
readonly loadOwn?: () => Promise<EngineModule>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { CheckRules } from '../check/index.cjs';
|
|
2
|
+
export type PresetSourceKind = 'official' | 'gateway';
|
|
3
|
+
export interface ProviderPreset {
|
|
4
|
+
readonly name: string;
|
|
5
|
+
/** @deprecated Use video-fps-probe/presets/sources.json; sourceUrl will be removed in the next major. */
|
|
6
|
+
readonly sourceUrl: string;
|
|
7
|
+
readonly sourceKind: PresetSourceKind;
|
|
8
|
+
readonly verifiedAt: string;
|
|
9
|
+
readonly note: string;
|
|
10
|
+
readonly rules: {
|
|
11
|
+
readonly [K in keyof CheckRules]: Readonly<CheckRules[K]>;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export declare const klingO1ReferenceVideo: ProviderPreset;
|
|
15
|
+
export declare const klingMotionControl: ProviderPreset;
|
|
16
|
+
export declare const runwayAleph2: ProviderPreset;
|
|
17
|
+
export declare const veoExtend: ProviderPreset;
|
|
18
|
+
export declare const wan26ReferenceToVideoFal: ProviderPreset;
|
|
19
|
+
export declare const seedance2ReferenceVideo: ProviderPreset;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const frozen=e=>{for(let t of Object.values(e))t&&typeof t==`object`&&frozen(t);return Object.freeze(e)},e=frozen({name:`Kling O1 reference video`,sourceUrl:`https://kling.ai/document-api/api/video/o1/video-omni.md`,sourceKind:`official`,verifiedAt:`2026-10-25`,note:`The feature or base video of the Kling O1 omni video API, one per request. No codec, aspect ratio or audio rule is published for it. The page does not say whether 200 MB is decimal or binary; the larger reading, 209,715,200 bytes, is checked.`,rules:{fps:{min:24,max:60},duration:{min:3,max:10},frameSize:{minShortSide:700,maxLongSide:2160},fileSize:{max:209715200},containers:[`mp4`,`mov`]}}),t=frozen({name:`Kling Motion Control 3.0 reference video`,sourceUrl:`https://kling.ai/document-api/api/video/motion-control.md`,sourceKind:`official`,verifiedAt:`2026-10-25`,note:`The 30 s maximum applies when the character orientation follows the video; it is 10 s when it follows the image. No frame rate rule is published. The page does not say whether 100 MB is decimal or binary; the larger reading, 104,857,600 bytes, is checked.`,rules:{duration:{min:3,max:30},frameSize:{minShortSide:340,maxLongSide:3850},fileSize:{max:104857600},containers:[`mp4`,`mov`]}}),n=frozen({name:`Runway Aleph 2.0 input video`,sourceUrl:`https://docs.dev.runwayml.com/assets/inputs`,sourceKind:`official`,verifiedAt:`2026-10-25`,note:`The file size is the ephemeral upload limit; a URL is limited to 32 MB and a data URI to 16 MB. The codecs join what the four containers accept, including the discouraged ones, without pairing codec and container. Aleph keeps the input resolution up to 1080p, and the page does not say whether a larger input is refused, so no size rule. The page does not say whether 200 MB is decimal or binary; the larger reading, 209,715,200 bytes, is checked.`,rules:{fps:{max:30},duration:{min:2,max:30},fileSize:{max:209715200},containers:[`mp4`,`mov`,`matroska`,`webm`],codecs:[`h264`,`hevc`,`av1`,`prores`,`vp8`,`vp9`,`mjpeg`,`mpeg2video`]}}),r=frozen({name:`Google Veo 3.1 video to extend`,sourceUrl:`https://docs.cloud.google.com/gemini-enterprise-agent-platform/models/video/extend-a-veo-video`,sourceKind:`official`,verifiedAt:`2026-10-25`,note:`The frame rate must be 24, so 23.976 fails. The resolution must be 720p, 1080p or 4k; 4k is not given in pixels, so only the 720 px short side is checked. No codec or file size limit is published.`,rules:{fps:{exact:24},duration:{min:1,max:30},frameSize:{minShortSide:720},aspectRatio:{oneOf:[`16/9`,`9/16`]},containers:[`mp4`]}}),i=frozen({name:`Wan 2.6 reference to video on fal`,sourceUrl:`https://fal.ai/models/wan/v2.6/reference-to-video/api`,sourceKind:`gateway`,verifiedAt:`2026-10-25`,note:`As the fal endpoint documents it, for each of 1 to 3 reference videos; fal serves a model it does not make, and no first-party page was verified. No resolution, aspect ratio or codec limit is published for the input. The page does not say whether 30 MB is decimal or binary; the larger reading, 31,457,280 bytes, is checked.`,rules:{fps:{min:16},duration:{min:2,max:30},fileSize:{max:31457280},containers:[`mp4`,`mov`],severity:`warn`}}),a=frozen({name:`Seedance 2.0 reference video`,sourceUrl:`https://evolink.ai/docs/en/api-manual/video-series/seedance2.0/seedance-2.0-reference-to-video`,sourceKind:`gateway`,verifiedAt:`2026-10-25`,note:`As the EvoLink gateway documents it; no first-party page was verified, and the Runware model page gives only the count. Up to 3 videos of at most 15 s together. The 480p, 720p or 1080p list is checked only through the pixel range. The page does not say whether 50 MB is decimal or binary; the larger reading, 52,428,800 bytes, is checked.`,rules:{fps:{min:24,max:60},duration:{min:2,max:15},frameSize:{minShortSide:300,maxLongSide:6e3},pixels:{min:409600,max:2086876},aspectRatio:{min:.4,max:2.5},fileSize:{max:52428800},containers:[`mp4`,`mov`],severity:`warn`}});exports.klingMotionControl=t,exports.klingO1ReferenceVideo=e,exports.runwayAleph2=n,exports.seedance2ReferenceVideo=a,exports.veoExtend=r,exports.wan26ReferenceToVideoFal=i;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { IdOf, Probe, ProbeOptions } from './client/types.cjs';
|
|
2
|
+
import type { EngineDescriptor } from './plugins/types.cjs';
|
|
3
|
+
export declare function createProbe<const E extends readonly EngineDescriptor[] = []>(options?: ProbeOptions<E>): Probe<'own' | IdOf<E[number]>>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { BlobLike } from '../core/reader.cjs';
|
|
2
|
+
export type { BlobLike, BlobSlice } from '../core/reader.cjs';
|
|
3
|
+
export interface RangeRequest {
|
|
4
|
+
readonly start: number;
|
|
5
|
+
readonly end: number;
|
|
6
|
+
readonly range: string;
|
|
7
|
+
readonly signal: AbortSignal | null;
|
|
8
|
+
}
|
|
9
|
+
export interface RangeReply {
|
|
10
|
+
readonly status?: number | null;
|
|
11
|
+
readonly contentRange?: string | null;
|
|
12
|
+
readonly contentLength?: number | null;
|
|
13
|
+
readonly bytes?: ArrayBuffer | ArrayBufferView | null;
|
|
14
|
+
readonly body?: (expectedLength: number) => Promise<ArrayBuffer | ArrayBufferView>;
|
|
15
|
+
readonly discard?: () => void;
|
|
16
|
+
}
|
|
17
|
+
export type RangeFetcher = (request: RangeRequest) => Promise<RangeReply>;
|
|
18
|
+
export interface RangeSource extends BlobLike {
|
|
19
|
+
read(start: number, end: number, signal?: AbortSignal | null): Promise<Uint8Array>;
|
|
20
|
+
}
|
|
21
|
+
export interface RangeSourceInit {
|
|
22
|
+
size: number;
|
|
23
|
+
signal?: AbortSignal | null;
|
|
24
|
+
}
|
|
25
|
+
export interface OpenRangeSourceInit {
|
|
26
|
+
size?: number;
|
|
27
|
+
headBytes?: number;
|
|
28
|
+
signal?: AbortSignal | null;
|
|
29
|
+
}
|
|
30
|
+
export interface HttpRangeInit {
|
|
31
|
+
headers?: Readonly<Record<string, string>>;
|
|
32
|
+
fetch?: typeof globalThis.fetch;
|
|
33
|
+
}
|
|
34
|
+
export type RangeSourceErrorCode = 'unverifiable-reply' | 'unexpected-status' | 'range-mismatch' | 'size-changed' | 'length-mismatch' | 'body-too-long' | 'body-too-short' | 'invalid-reply';
|
|
35
|
+
export interface RangeSourceErrorOptions {
|
|
36
|
+
status?: number | null;
|
|
37
|
+
cause?: unknown;
|
|
38
|
+
}
|
|
39
|
+
export declare class RangeSourceError extends Error {
|
|
40
|
+
readonly code: RangeSourceErrorCode;
|
|
41
|
+
readonly status: number | null;
|
|
42
|
+
constructor(code: RangeSourceErrorCode, message: string, options?: RangeSourceErrorOptions);
|
|
43
|
+
}
|
|
44
|
+
export declare function rangeSource(fetcher: RangeFetcher, init: RangeSourceInit): RangeSource;
|
|
45
|
+
export declare function openRangeSource(fetcher: RangeFetcher, init?: OpenRangeSourceInit): Promise<RangeSource>;
|
|
46
|
+
export declare function httpRange(url: string | URL, init?: HttpRangeInit): RangeFetcher;
|
|
47
|
+
export declare function readCapped(body: ReadableStream<Uint8Array> | AsyncIterable<Uint8Array>, expectedLength: number, signal?: AbortSignal | null): Promise<Uint8Array>;
|
package/dist/cjs/url.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var RangeSourceError=class extends Error{code;status;constructor(e,t,n={}){super(t,n.cause===void 0?void 0:{cause:n.cause}),this.name=`RangeSourceError`,this.code=e,this.status=typeof n.status==`number`?n.status:null}};const e=/^\s*bytes\s+(\d+)-(\d+)\/(\d+|\*)\s*$/i,noop=()=>{},tag=e=>Object.prototype.toString.call(e);function shown(e){return typeof e==`string`?JSON.stringify(e):typeof e==`object`&&e?Array.isArray(e)?`an array`:`an object`:String(e)}function count(e,t,n=0){if(typeof e!=`number`||!Number.isSafeInteger(e)||e<n)throw TypeError(`${t} must be a non-negative safe integer${n>0?` of at least ${n}`:``}, got ${shown(e)}`);return e}function signalOf(e,t){if(e==null)return null;let n=e;if(typeof n!=`object`||typeof n.aborted!=`boolean`||typeof n.addEventListener!=`function`||typeof n.removeEventListener!=`function`)throw TypeError(`${t} must be an AbortSignal, got ${shown(e)}`);return e}function objectOf(e,t){if(e===void 0)return{};if(typeof e!=`object`||!e||Array.isArray(e))throw TypeError(`${t} must be an object, got ${shown(e)}`);return e}function fetcherOf(e){if(typeof e!=`function`)throw TypeError(`fetcher must be a function, got ${shown(e)}`);return e}const reasonOf=e=>e.reason??new DOMException(`This operation was aborted`,`AbortError`);function throwIfAborted(e){if(e?.aborted)throw reasonOf(e)}function linked(e,t){if(!e||!t||e===t||e.aborted||t.aborted)return[e?.aborted||!t?e:t,noop];let n=new AbortController,fromA=()=>n.abort(reasonOf(e)),fromB=()=>n.abort(reasonOf(t));return e.addEventListener(`abort`,fromA,{once:!0}),t.addEventListener(`abort`,fromB,{once:!0}),[n.signal,()=>(e.removeEventListener(`abort`,fromA),t.removeEventListener(`abort`,fromB))]}function untilAborted(e,t,n=noop){return new Promise((r,i)=>{let stop=()=>i(reasonOf(t));t?.aborted?stop():t?.addEventListener(`abort`,stop,{once:!0}),e.then(e=>{t?.removeEventListener(`abort`,stop),t?.aborted?n(e):r(e)},e=>{t?.removeEventListener(`abort`,stop),i(!t?.aborted&&e?.name===`AbortError`?Object.assign(Error(String(e),{cause:e}),{name:``}):e)})})}function attempt(e){try{return Promise.resolve(e())}catch(e){return Promise.reject(e)}}function discard(e){attempt(()=>e?.discard?.()).catch(noop)}function parseRange(t){let n=e.exec(t);if(!n)return null;let r=Number(n[1]),i=Number(n[2]),a=n[3]===`*`?null:Number(n[3]);return!Number.isSafeInteger(r)||!Number.isSafeInteger(i)||r>i||a!==null&&!Number.isSafeInteger(a)?null:{first:r,last:i,total:a}}const present=e=>e!=null,contentRangeOf=({contentRange:e})=>typeof e==`string`?e.trim()===``?null:e:present(e)?String(e):null;function replyOf(e,t){if(typeof e!=`object`||!e)throw new RangeSourceError(`invalid-reply`,`the reply to ${t} is ${shown(e)}, not an object`);return e}function refusal(e,t,n,r,i){let a=present(e.status)?e.status:null,o=contentRangeOf(e);if(a===null&&o===null)return new RangeSourceError(`unverifiable-reply`,`the reply to ${i} has neither a status nor a Content-Range, so it cannot be checked`);if(a!==null&&a!==206&&(a!==200||t!==0||n!==r))return new RangeSourceError(`unexpected-status`,`status ${shown(a)} to ${i} of an object of ${r} bytes, expected 206${t===0&&n===r?` or 200`:``}`,{status:a});if(o!==null){let e=parseRange(o);if(e&&e.total!==null&&e.total!==r)return new RangeSourceError(`size-changed`,`Content-Range ${shown(o)} gives an object of ${e.total} bytes, the source was opened at ${r} bytes`,{status:a});if(!e||e.first!==t||e.last!==n-1)return new RangeSourceError(`range-mismatch`,`Content-Range ${shown(o)} does not match ${i}`,{status:a})}return present(e.contentLength)&&e.contentLength!==n-t?new RangeSourceError(`length-mismatch`,`Content-Length ${shown(e.contentLength)} does not match ${i} (${n-t} bytes)`,{status:a}):null}function exactly(e,t,n){let r;if(ArrayBuffer.isView(e))r=new Uint8Array(e.buffer,e.byteOffset,e.byteLength);else if(tag(e)===`[object ArrayBuffer]`)r=new Uint8Array(e);else throw new RangeSourceError(`invalid-reply`,`${n} is ${e===null?`null`:typeof e}, not bytes`);if(r.byteLength>t)throw new RangeSourceError(`body-too-long`,`${n} holds ${r.byteLength} bytes, more than the ${t} requested`);if(r.byteLength<t)throw new RangeSourceError(`body-too-short`,`${n} holds ${r.byteLength} of the ${t} bytes requested`);return r.byteOffset===0&&r.byteLength===r.buffer.byteLength&&tag(r.buffer)===`[object ArrayBuffer]`?r:r.slice()}async function bodyOf(e,t,n,r){let i=`the body of ${n}`;try{if(present(e.bytes))return exactly(e.bytes,t,i);let a=e.body;if(typeof a!=`function`)throw new RangeSourceError(`invalid-reply`,`the reply to ${n} has neither bytes nor a body function`);return exactly(await untilAborted(attempt(()=>a.call(e,t)),r),t,i)}catch(t){throw discard(e),t}}function request(e,t,n,r){let i=`bytes=${t}-${n-1}`;return[i,untilAborted(attempt(()=>e({start:t,end:n,range:i,signal:r})),r,discard).then(e=>replyOf(e,i))]}function sourceOf(e,t,n,r){let read=async(i,a,o)=>{if(count(i,`start`),count(a,`end`),i>a||a>t)throw RangeError(`read needs start <= end <= size (${t}), got ${i} and ${a}`);let[s,c]=linked(n,signalOf(o,`signal`));try{if(throwIfAborted(s),i===a)return new Uint8Array;if(r&&a<=r.byteLength)return r.slice(i,a);let[n,o]=request(e,i,a,s),c=await o,l=refusal(c,i,a,t,n);if(l)throw discard(c),l;return await bodyOf(c,a-i,n,s)}finally{c()}},clamp=(e,n)=>{if(e===void 0)return n;let r=Math.round(Number(e))||0;return r<0?Math.max(t+r,0):Math.min(r,t)};return{size:t,read,slice(e,r){let i=clamp(e,0),a=Math.max(i,clamp(r,t));return{arrayBuffer:()=>read(i,a,n).then(e=>e.buffer)}}}}function rangeSource(e,t){let n=fetcherOf(e),r=objectOf(t,`init`);return sourceOf(n,count(r.size,`init.size`),signalOf(r.signal,`init.signal`),null)}async function openRangeSource(e,t={}){let n=fetcherOf(e),r=objectOf(t,`init`),i=signalOf(r.signal,`init.signal`);if(r.size!==void 0)return sourceOf(n,count(r.size,`init.size`),i,null);let a=r.headBytes===void 0?65536:count(r.headBytes,`init.headBytes`,1);throwIfAborted(i);let[o,s]=request(n,0,a,i),c=await s,l=present(c.status)?c.status:null,u=contentRangeOf(c),d=u===null?null:parseRange(u),f=present(c.contentLength)?c.contentLength:null,refuse=(e,t)=>{throw discard(c),new RangeSourceError(e,t,{status:l})};if((l===416||l===null)&&u!==null&&/^\s*bytes\s+\*\/0\s*$/i.test(u))return discard(c),sourceOf(n,0,i,new Uint8Array);let p;if(l===200)(f===null||!Number.isSafeInteger(f)||f<0||f>a)&&refuse(`unexpected-status`,`status 200 to ${o} with ${f===null?`no Content-Length`:`Content-Length ${shown(f)}`}: the server sent the whole object instead of the range, and only an object of at most ${a} bytes is accepted that way`),u!==null&&(!d||d.first!==0||d.last!==f-1||d.total!==f)&&refuse(`range-mismatch`,`Content-Range ${shown(u)} does not match the whole object of ${f} bytes`),p=f;else if(l===206||l===null&&u!==null)u===null&&refuse(`unverifiable-reply`,`the 206 reply to ${o} has no Content-Range, or does not expose it to a cross-origin page, so the object size is unknown: pass init.size`),(!d||d.total===null||d.first!==0||d.last!==Math.min(a,d.total)-1)&&refuse(`range-mismatch`,`Content-Range ${shown(u)} does not match ${o} of an object of known size`),f!==null&&f!==d.last+1&&refuse(`length-mismatch`,`Content-Length ${shown(f)} does not match Content-Range ${shown(u)}`),p=d.total;else if(l===null)return refuse(`unverifiable-reply`,`the reply to ${o} has neither a status nor a Content-Range, so it cannot be checked`);else return refuse(`unexpected-status`,`status ${shown(l)} to ${o}, expected 206`);return sourceOf(n,p,i,await bodyOf(c,Math.min(p,a),o,i))}function named(e,t){let n=e?.cause,r=n?.code??n?.message;return t?.aborted||e?.name!==`TypeError`||typeof r!=`string`?e:TypeError(`${e.message}: ${r}`,{cause:e})}function httpRange(e,t={}){let n=typeof e==`string`?e:typeof e?.href==`string`?e.href:null;if(!n)throw TypeError(`url must be a non-empty string or a URL, got ${shown(e)}`);let r=objectOf(t,`init`),i=objectOf(r.headers,`init.headers`),a=r.fetch??globalThis.fetch;if(typeof a!=`function`)throw TypeError(`httpRange needs a fetch function: pass init.fetch where globalThis.fetch does not exist`);return async({range:e,signal:t})=>{let r=new Headers(i);r.set(`range`,e),r.set(`accept-encoding`,`identity`);let fail=e=>{throw named(e,t)},o=await a(n,{headers:r,signal:t}).catch(fail),s=o.headers.get(`content-length`);return{status:o.status,contentRange:o.headers.get(`content-range`),contentLength:s===null?null:/^\s*\d+\s*$/.test(s)?Number(s):NaN,body:e=>o.body?readCapped(o.body,e,t).catch(fail):Promise.resolve(new Uint8Array),discard:()=>void o.body?.cancel().catch(noop)}}}async function readCapped(e,t,n=null){count(t,`expectedLength`);let r=signalOf(n,`signal`),next,stop,i=e;if(typeof i?.getReader==`function`){let t=e.getReader();next=()=>t.read(),stop=()=>void t.cancel().catch(noop)}else if(typeof i?.[Symbol.asyncIterator]==`function`){let t=e[Symbol.asyncIterator]();next=()=>t.next(),stop=()=>{attempt(()=>e.destroy?.()).catch(noop),attempt(()=>t.return?.()).catch(noop)}}else throw TypeError(`body must be a ReadableStream or an async iterable, got ${shown(e)}`);let a=new Uint8Array(t),o=0;try{for(;;){throwIfAborted(r);let e=await untilAborted(attempt(next),r);if(e.done)break;let n=e.value,i=ArrayBuffer.isView(n)?new Uint8Array(n.buffer,n.byteOffset,n.byteLength):tag(n)===`[object ArrayBuffer]`?new Uint8Array(n):null;if(!i)throw new RangeSourceError(`invalid-reply`,`the body gave a ${n===null?`null`:typeof n} chunk, not bytes`);if(o+i.byteLength>t)throw new RangeSourceError(`body-too-long`,`the body passed the ${t} bytes requested`);a.set(i,o),o+=i.byteLength}}catch(e){throw stop(),e}if(o<t)throw new RangeSourceError(`body-too-short`,`the body ended after ${o} of the ${t} bytes requested`);return a}exports.RangeSourceError=RangeSourceError,exports.httpRange=httpRange,exports.openRangeSource=openRangeSource,exports.rangeSource=rangeSource,exports.readCapped=readCapped;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { AnalyzeOptions, AnalyzeProgress, FileResult } from '../core/types.cjs';
|
|
2
|
+
import type { ContainerFamily, WorkerEngineId } from '../plugins/types.cjs';
|
|
3
|
+
export declare const PROTOCOL_VERSION = 1;
|
|
4
|
+
export type WireOptions = Omit<AnalyzeOptions, 'signal' | 'onProgress'>;
|
|
5
|
+
export type SkipReason = 'unsupported-container';
|
|
6
|
+
export type WorkerErrorCode = 'unknown-engine' | 'protocol';
|
|
7
|
+
export type AnalyzeRequest = {
|
|
8
|
+
type: 'analyze';
|
|
9
|
+
file: Blob;
|
|
10
|
+
name: string;
|
|
11
|
+
engine: WorkerEngineId;
|
|
12
|
+
options: WireOptions;
|
|
13
|
+
};
|
|
14
|
+
export type WarmRequest = {
|
|
15
|
+
type: 'warm';
|
|
16
|
+
engines: readonly WorkerEngineId[];
|
|
17
|
+
containers: readonly ContainerFamily[];
|
|
18
|
+
};
|
|
19
|
+
export type JobRequest = AnalyzeRequest | WarmRequest;
|
|
20
|
+
export type WorkerRequest = {
|
|
21
|
+
v: typeof PROTOCOL_VERSION;
|
|
22
|
+
id: number;
|
|
23
|
+
} & (JobRequest | {
|
|
24
|
+
type: 'cancel';
|
|
25
|
+
});
|
|
26
|
+
export type WorkerResponse = {
|
|
27
|
+
v: typeof PROTOCOL_VERSION;
|
|
28
|
+
id: number;
|
|
29
|
+
} & ({
|
|
30
|
+
type: 'progress';
|
|
31
|
+
progress: AnalyzeProgress;
|
|
32
|
+
} | {
|
|
33
|
+
type: 'result';
|
|
34
|
+
result: FileResult;
|
|
35
|
+
} | {
|
|
36
|
+
type: 'skipped';
|
|
37
|
+
reason: SkipReason;
|
|
38
|
+
family: ContainerFamily | null;
|
|
39
|
+
} | {
|
|
40
|
+
type: 'warmed';
|
|
41
|
+
} | {
|
|
42
|
+
type: 'error';
|
|
43
|
+
message: string;
|
|
44
|
+
aborted: boolean;
|
|
45
|
+
code?: WorkerErrorCode;
|
|
46
|
+
reason?: 'load';
|
|
47
|
+
});
|
package/dist/client/compare.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { compareResults } from "../api/agreement.js";
|
|
2
2
|
import { ProbeError } from "../api/errors.js";
|
|
3
|
-
import { isAbortError } from "../api/guards.js";
|
|
3
|
+
import { isAbortError, isProbeError } from "../api/guards.js";
|
|
4
4
|
import { failureOf } from "./fallback.js";
|
|
5
5
|
import { nameOf } from "./probe.js";
|
|
6
6
|
export async function compare(ctx, file, o = {}) {
|
|
@@ -25,7 +25,7 @@ export async function compare(ctx, file, o = {}) {
|
|
|
25
25
|
catch (e) {
|
|
26
26
|
if (isAbortError(e))
|
|
27
27
|
throw e;
|
|
28
|
-
const error = e
|
|
28
|
+
const error = isProbeError(e) ? e : Object.assign(new ProbeError('worker-failed', String(e), { engine: id, cause: e }), { reason: 'crash' });
|
|
29
29
|
if (error.code === 'unsupported-environment')
|
|
30
30
|
return { status: 'skipped', reason: 'unsupported-environment' };
|
|
31
31
|
return { status: error.code === 'timeout' ? 'timeout' : 'crashed', error };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { EngineDescriptor, WorkerEngineId } from '../plugins/types.js';
|
|
2
|
+
import type { ClientOptions, IdOf, Probe } from './types.js';
|
|
3
|
+
export declare function createProbeClient<const E extends readonly EngineDescriptor[] = [], const W extends WorkerEngineId = never>(options: ClientOptions<E, W>): Probe<W | IdOf<E[number]> | 'own'>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { createProbeCore } from "./probe.js";
|
|
2
|
+
import { splitBy } from "./split.js";
|
|
3
|
+
const splitOnFirstRun = splitBy((pool, lazy, life) => ({
|
|
4
|
+
run: (request, options) => lazy(import("./route.js"), request.engine).then((m) => m.runByHead(pool, lazy, life).run(request, options)),
|
|
5
|
+
}));
|
|
6
|
+
export function createProbeClient(options) {
|
|
7
|
+
return createProbeCore({ ...options, split: splitOnFirstRun });
|
|
8
|
+
}
|
package/dist/client/fallback.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ProbeError } from "../api/errors.js";
|
|
2
|
-
import { isAbortError } from "../api/guards.js";
|
|
2
|
+
import { isAbortError, isProbeError } from "../api/guards.js";
|
|
3
3
|
import { emptyAnalysis, primaryTrack } from "../core/result.js";
|
|
4
4
|
export const failureOf = (r) => {
|
|
5
5
|
const t = primaryTrack(r);
|
|
@@ -25,7 +25,7 @@ export async function runChain(run, chain, file, name, wire, call, first) {
|
|
|
25
25
|
catch (e) {
|
|
26
26
|
if (isAbortError(e))
|
|
27
27
|
throw e;
|
|
28
|
-
last = e
|
|
28
|
+
last = isProbeError(e) ? e : Object.assign(new ProbeError('worker-failed', String(e), { engine: id, cause: e }), { reason: 'crash' });
|
|
29
29
|
failures.push({ engine: id, error: last.message });
|
|
30
30
|
continue;
|
|
31
31
|
}
|
package/dist/client/index.d.ts
CHANGED
|
@@ -7,5 +7,5 @@ export type * from '../core/types.js';
|
|
|
7
7
|
export type { MediabunnyNative, MediabunnyTrackNative, Mp4boxNative, Mp4boxTrackNative, RvfcConfidence, RvfcDetails, RvfcPass } from '../engines/types.js';
|
|
8
8
|
export type { ContainerFamily, EngineDescriptor, EngineModule, WorkerEngineId } from '../plugins/types.js';
|
|
9
9
|
export type { WireOptions } from '../worker/protocol.js';
|
|
10
|
-
export { createProbeClient } from './
|
|
10
|
+
export { createProbeClient } from './create.js';
|
|
11
11
|
export type * from './types.js';
|
package/dist/client/index.js
CHANGED
|
@@ -2,4 +2,4 @@ export { compareResults } from "../api/agreement.js";
|
|
|
2
2
|
export { ProbeError } from "../api/errors.js";
|
|
3
3
|
export { isAbortError, isTimeoutError } from "../api/guards.js";
|
|
4
4
|
export { summarize } from "../api/summary.js";
|
|
5
|
-
export { createProbeClient } from "./
|
|
5
|
+
export { createProbeClient } from "./create.js";
|
package/dist/client/inline.js
CHANGED
|
@@ -38,6 +38,6 @@ export function runInline(load, file, name, options, run) {
|
|
|
38
38
|
}
|
|
39
39
|
const opts = { ...options, signal: controller.signal, onProgress, host };
|
|
40
40
|
return { type: 'result', result: await module.run(file, name, opts) };
|
|
41
|
-
})().then((reply) => finish(() => resolve(reply)), (e) => (e
|
|
41
|
+
})().then((reply) => finish(() => resolve(reply)), (e) => (e?.name === 'ProbeError' && typeof e.code === 'string' ? finish(() => reject(e)) : fail(Object.assign(new ProbeError('worker-failed', e instanceof Error ? e.message || e.name : String(e), { engine, cause: e }), { reason: loading ? 'load' : 'crash' }))));
|
|
42
42
|
});
|
|
43
43
|
}
|
package/dist/client/lazy.js
CHANGED
|
@@ -3,5 +3,5 @@ export function lazyProbeFrom(factory) {
|
|
|
3
3
|
return lazyStub(factory, []);
|
|
4
4
|
}
|
|
5
5
|
export function lazyProbeClient(options) {
|
|
6
|
-
return lazyStub(() => import("./probe.js").then((m) => m.
|
|
6
|
+
return lazyStub(() => import("./probe-client.js").then((m) => m.loadProbeClient(options)), knownEngines(options.worker, options.engines));
|
|
7
7
|
}
|