dasha 4.4.3 → 4.4.5
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/index.d.mts +5 -1
- package/dist/index.mjs +98 -32
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -183,6 +183,7 @@ type Segment = {
|
|
|
183
183
|
};
|
|
184
184
|
type DashSegmentLocation = {
|
|
185
185
|
path: string;
|
|
186
|
+
sourcePath: string;
|
|
186
187
|
offset: number;
|
|
187
188
|
length: number | null;
|
|
188
189
|
};
|
|
@@ -360,7 +361,10 @@ declare class DashDemuxer {
|
|
|
360
361
|
}): void;
|
|
361
362
|
findMatchingTrack(nextTracks: DashParsedTrack[], currentTrack: DashParsedTrack): DashParsedTrack | undefined;
|
|
362
363
|
refreshTracks(tracks: DashParsedTrack[]): Promise<void>;
|
|
363
|
-
|
|
364
|
+
fetchManifestText(url: string): Promise<{
|
|
365
|
+
text: string;
|
|
366
|
+
url: string;
|
|
367
|
+
}>;
|
|
364
368
|
resetManifestUrls(): void;
|
|
365
369
|
}
|
|
366
370
|
declare abstract class DashTrackBackingBase {
|
package/dist/index.mjs
CHANGED
|
@@ -316,6 +316,21 @@ const getDashTrackMatchKey = (track) => JSON.stringify({
|
|
|
316
316
|
const getSourcePath = (source) => {
|
|
317
317
|
if ("rootPath" in source && typeof source.rootPath === "string") return source.rootPath;
|
|
318
318
|
};
|
|
319
|
+
const resolvePathedSourceRequest = async (source, request) => {
|
|
320
|
+
const pathedSource = source;
|
|
321
|
+
if (typeof pathedSource._resolveRequest !== "function") throw new Error("DASH input currently requires a pathed source such as UrlSource.");
|
|
322
|
+
return await pathedSource._resolveRequest(request);
|
|
323
|
+
};
|
|
324
|
+
const resolvePathedSourcePath = async (source, request) => {
|
|
325
|
+
const ref = await resolvePathedSourceRequest(source, request);
|
|
326
|
+
try {
|
|
327
|
+
const path = getSourcePath(ref.source);
|
|
328
|
+
if (!path) throw new Error("DASH segment requests must resolve to a pathed source.");
|
|
329
|
+
return path;
|
|
330
|
+
} finally {
|
|
331
|
+
ref.free();
|
|
332
|
+
}
|
|
333
|
+
};
|
|
319
334
|
const normalizeHeaders$1 = (headers) => {
|
|
320
335
|
if (!headers) return {};
|
|
321
336
|
if (headers instanceof Headers) return Object.fromEntries(headers.entries());
|
|
@@ -333,30 +348,45 @@ const getSourceHeaders$1 = (source) => {
|
|
|
333
348
|
const getSourceFetch = (source) => {
|
|
334
349
|
return ("_options" in source && source._options && typeof source._options === "object" ? source._options : void 0)?.fetchFn ?? fetch;
|
|
335
350
|
};
|
|
351
|
+
const fetchDashManifest = async (source, url) => {
|
|
352
|
+
const response = await getSourceFetch(source)(url, { headers: getSourceHeaders$1(source) });
|
|
353
|
+
if (!response.ok) throw new Error(`Failed to fetch DASH manifest: ${response.status} ${response.statusText} (${response.url})`);
|
|
354
|
+
return response;
|
|
355
|
+
};
|
|
336
356
|
const parseOriginalUrlFromManifest = (text) => text.match(/<!--\s*URL:\s*([^\n]+?)\s*-->/)?.[1]?.trim();
|
|
337
357
|
const loadDashManifest = async (source) => {
|
|
338
358
|
const manifestPath = getSourcePath(source);
|
|
339
359
|
if (!manifestPath) throw new Error("DASH input currently requires a pathed source such as UrlSource.");
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
360
|
+
const manifestRef = await resolvePathedSourceRequest(source, {
|
|
361
|
+
path: manifestPath,
|
|
362
|
+
isRoot: true
|
|
363
|
+
});
|
|
364
|
+
const manifestSource = manifestRef.source;
|
|
365
|
+
const resolvedManifestPath = getSourcePath(manifestSource);
|
|
366
|
+
try {
|
|
367
|
+
if (!resolvedManifestPath) throw new Error("DASH manifest requests must resolve to a pathed source.");
|
|
368
|
+
if (resolvedManifestPath.startsWith("http://") || resolvedManifestPath.startsWith("https://")) {
|
|
369
|
+
const response = await fetchDashManifest(manifestSource, resolvedManifestPath);
|
|
370
|
+
return {
|
|
371
|
+
text: await response.text(),
|
|
372
|
+
url: response.url || resolvedManifestPath
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
if (resolvedManifestPath.startsWith("file:")) {
|
|
376
|
+
const text = await readFile(new URL(resolvedManifestPath), "utf8");
|
|
377
|
+
return {
|
|
378
|
+
text,
|
|
379
|
+
url: parseOriginalUrlFromManifest(text) ?? resolvedManifestPath
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
const text = await readFile(resolvedManifestPath, "utf8");
|
|
350
383
|
return {
|
|
351
384
|
text,
|
|
352
|
-
url: parseOriginalUrlFromManifest(text) ??
|
|
385
|
+
url: parseOriginalUrlFromManifest(text) ?? resolvedManifestPath
|
|
353
386
|
};
|
|
387
|
+
} finally {
|
|
388
|
+
manifestRef.free();
|
|
354
389
|
}
|
|
355
|
-
const text = await readFile(manifestPath, "utf8");
|
|
356
|
-
return {
|
|
357
|
-
text,
|
|
358
|
-
url: parseOriginalUrlFromManifest(text) ?? manifestPath
|
|
359
|
-
};
|
|
360
390
|
};
|
|
361
391
|
const isLikelyDashPath = (source) => {
|
|
362
392
|
const path = getSourcePath(source);
|
|
@@ -1386,6 +1416,7 @@ const BACKING_TYPE_AUDIO = "audio";
|
|
|
1386
1416
|
const BACKING_TYPE_VIDEO = "video";
|
|
1387
1417
|
const BASE_INPUT_PATCHED = Symbol.for("dasha.base-mediabunny-input-patched");
|
|
1388
1418
|
const PRESERVE_SUBTITLE_BACKINGS = Symbol.for("dasha.preserve-subtitle-backings");
|
|
1419
|
+
const resolvedHlsSegmentLocations = /* @__PURE__ */ new WeakSet();
|
|
1389
1420
|
const getDefaultAudioTrackFormats = (source) => {
|
|
1390
1421
|
return isLikelyDashPath(source instanceof SourceRef ? source.source : source) ? [DASH, ...ALL_FORMATS$1] : [...ALL_FORMATS$1, DASH];
|
|
1391
1422
|
};
|
|
@@ -1549,6 +1580,22 @@ const getSegmentedInputForTrack = (track) => {
|
|
|
1549
1580
|
const internalTrack = backing.internalTrack;
|
|
1550
1581
|
return internalTrack.demuxer.getSegmentedInputForPath(internalTrack.fullPath);
|
|
1551
1582
|
};
|
|
1583
|
+
const isHlsSegment = (segment) => !("sourcePath" in segment.location);
|
|
1584
|
+
const resolveHlsSegmentLocation = async (source, location) => {
|
|
1585
|
+
if (resolvedHlsSegmentLocations.has(location)) return;
|
|
1586
|
+
location.path = await resolvePathedSourcePath(source, {
|
|
1587
|
+
path: location.path,
|
|
1588
|
+
isRoot: false
|
|
1589
|
+
});
|
|
1590
|
+
resolvedHlsSegmentLocations.add(location);
|
|
1591
|
+
};
|
|
1592
|
+
const resolveHlsSegments = async (source, segments) => {
|
|
1593
|
+
for (const segment of segments) {
|
|
1594
|
+
if (!isHlsSegment(segment)) continue;
|
|
1595
|
+
if (segment.initSegment) await resolveHlsSegmentLocation(source, segment.initSegment.location);
|
|
1596
|
+
await resolveHlsSegmentLocation(source, segment.location);
|
|
1597
|
+
}
|
|
1598
|
+
};
|
|
1552
1599
|
const getTrackBacking = (track) => track._backing;
|
|
1553
1600
|
const getTrackSource = (track) => {
|
|
1554
1601
|
return getTrackBacking(track).getSource?.() ?? track.input.source;
|
|
@@ -1572,11 +1619,13 @@ const addSegmentAccess = (track) => new Proxy(track, { get(target, prop) {
|
|
|
1572
1619
|
if (prop === "getSegments") return async () => {
|
|
1573
1620
|
const segmentedInput = getSegmentedInputForTrack(target);
|
|
1574
1621
|
if (segmentedInput.segments.length === 0) await segmentedInput.runUpdateSegments();
|
|
1622
|
+
await resolveHlsSegments(getTrackSource(target), segmentedInput.segments);
|
|
1575
1623
|
return segmentedInput.segments;
|
|
1576
1624
|
};
|
|
1577
1625
|
if (prop === "refreshSegments") return async () => {
|
|
1578
1626
|
const segmentedInput = getSegmentedInputForTrack(target);
|
|
1579
1627
|
await segmentedInput.runUpdateSegments();
|
|
1628
|
+
await resolveHlsSegments(getTrackSource(target), segmentedInput.segments);
|
|
1580
1629
|
return segmentedInput.segments;
|
|
1581
1630
|
};
|
|
1582
1631
|
const value = Reflect.get(target, prop, target);
|
|
@@ -1831,23 +1880,27 @@ const getLeastRecentlyUsedIndex = (entries) => {
|
|
|
1831
1880
|
}
|
|
1832
1881
|
return bestIndex;
|
|
1833
1882
|
};
|
|
1834
|
-
const getSegmentLocation = (segment) => ({
|
|
1835
|
-
path:
|
|
1883
|
+
const getSegmentLocation = async (input, segment) => ({
|
|
1884
|
+
path: await resolvePathedSourcePath(input.source, {
|
|
1885
|
+
path: segment.url,
|
|
1886
|
+
isRoot: false
|
|
1887
|
+
}),
|
|
1888
|
+
sourcePath: segment.url,
|
|
1836
1889
|
offset: segment.startRange ?? 0,
|
|
1837
1890
|
length: segment.expectLength ?? null
|
|
1838
1891
|
});
|
|
1839
|
-
const createInitSegment = (segment) => ({
|
|
1892
|
+
const createInitSegment = async (input, segment) => ({
|
|
1840
1893
|
timestamp: 0,
|
|
1841
1894
|
duration: 0,
|
|
1842
1895
|
relativeToUnixEpoch: false,
|
|
1843
1896
|
firstSegment: null,
|
|
1844
1897
|
sequenceNumber: segment.sequenceNumber,
|
|
1845
|
-
location: getSegmentLocation(segment),
|
|
1898
|
+
location: await getSegmentLocation(input, segment),
|
|
1846
1899
|
encryption: segment.encryption,
|
|
1847
1900
|
initSegment: null,
|
|
1848
1901
|
lastProgramDateTimeSeconds: null
|
|
1849
1902
|
});
|
|
1850
|
-
const trackToDashSegments = (internalTrack) => {
|
|
1903
|
+
const trackToDashSegments = async (input, internalTrack) => {
|
|
1851
1904
|
const mediaSegments = internalTrack.track.mediaSegments;
|
|
1852
1905
|
if (mediaSegments.length === 0) return [];
|
|
1853
1906
|
let nextTimestamp = 0;
|
|
@@ -1860,7 +1913,7 @@ const trackToDashSegments = (internalTrack) => {
|
|
|
1860
1913
|
relativeToUnixEpoch: false,
|
|
1861
1914
|
firstSegment: null,
|
|
1862
1915
|
sequenceNumber: mediaSegment.sequenceNumber,
|
|
1863
|
-
location: getSegmentLocation(mediaSegment),
|
|
1916
|
+
location: await getSegmentLocation(input, mediaSegment),
|
|
1864
1917
|
encryption: mediaSegment.encryption,
|
|
1865
1918
|
initSegment: null,
|
|
1866
1919
|
lastProgramDateTimeSeconds: null
|
|
@@ -1869,7 +1922,7 @@ const trackToDashSegments = (internalTrack) => {
|
|
|
1869
1922
|
nextTimestamp = timestamp + mediaSegment.duration;
|
|
1870
1923
|
}
|
|
1871
1924
|
const firstSegment = segments[0] ?? null;
|
|
1872
|
-
const initSegment = internalTrack.track.initSegment ? createInitSegment(internalTrack.track.initSegment) : null;
|
|
1925
|
+
const initSegment = internalTrack.track.initSegment ? await createInitSegment(input, internalTrack.track.initSegment) : null;
|
|
1873
1926
|
for (const segment of segments) {
|
|
1874
1927
|
segment.firstSegment = firstSegment;
|
|
1875
1928
|
segment.initSegment = initSegment;
|
|
@@ -1905,8 +1958,9 @@ var DashSegmentedInput = class {
|
|
|
1905
1958
|
})();
|
|
1906
1959
|
}
|
|
1907
1960
|
async updateSegments() {
|
|
1961
|
+
const input = this.demuxer.input;
|
|
1908
1962
|
await this.demuxer.refreshTrackSegments(this.internalTrack);
|
|
1909
|
-
this.segments = trackToDashSegments(this.internalTrack);
|
|
1963
|
+
this.segments = await trackToDashSegments(input, this.internalTrack);
|
|
1910
1964
|
}
|
|
1911
1965
|
getRemainingWaitTimeMs() {
|
|
1912
1966
|
if (!this.internalTrack.track.isLive) return 0;
|
|
@@ -1970,7 +2024,7 @@ var DashSegmentedInput = class {
|
|
|
1970
2024
|
if (segment.initSegment && segment.initSegment !== segment) initInput = this.getInputForSegment(segment.initSegment);
|
|
1971
2025
|
const formatOptions = { ...input._formatOptions };
|
|
1972
2026
|
const segmentInput = preserveSubtitleBackingsOnInput(new Input$1({
|
|
1973
|
-
source: new CustomPathedSource(segment.location.
|
|
2027
|
+
source: new CustomPathedSource(segment.location.sourcePath, async (request) => {
|
|
1974
2028
|
if (!request.isRoot) throw new Error("Nested requests are not supported for DASH segments.");
|
|
1975
2029
|
const proxiedRequest = {
|
|
1976
2030
|
...request,
|
|
@@ -2764,11 +2818,10 @@ var DashDemuxer = class {
|
|
|
2764
2818
|
}
|
|
2765
2819
|
async refreshTracks(tracks) {
|
|
2766
2820
|
if (!tracks.length) return;
|
|
2767
|
-
const response = await this.
|
|
2768
|
-
const rawText = await response.text();
|
|
2821
|
+
const response = await this.fetchManifestText(this.manifestUrl).catch(() => this.fetchManifestText(this.originalUrl));
|
|
2769
2822
|
this.manifestUrl = response.url;
|
|
2770
2823
|
this.resetManifestUrls();
|
|
2771
|
-
const nextTracks = this.extractTracks(
|
|
2824
|
+
const nextTracks = this.extractTracks(response.text);
|
|
2772
2825
|
for (const track of tracks) {
|
|
2773
2826
|
const nextTrack = this.findMatchingTrack(nextTracks, track);
|
|
2774
2827
|
if (!nextTrack) continue;
|
|
@@ -2781,10 +2834,23 @@ var DashDemuxer = class {
|
|
|
2781
2834
|
track.subtitleGroupId = nextTrack.subtitleGroupId;
|
|
2782
2835
|
}
|
|
2783
2836
|
}
|
|
2784
|
-
async
|
|
2785
|
-
const
|
|
2786
|
-
|
|
2787
|
-
|
|
2837
|
+
async fetchManifestText(url) {
|
|
2838
|
+
const manifestRef = await resolvePathedSourceRequest(this.input.source, {
|
|
2839
|
+
path: url,
|
|
2840
|
+
isRoot: true
|
|
2841
|
+
});
|
|
2842
|
+
const manifestSource = manifestRef.source;
|
|
2843
|
+
try {
|
|
2844
|
+
const resolvedUrl = getSourcePath(manifestSource);
|
|
2845
|
+
if (!resolvedUrl) throw new Error("DASH manifest requests must resolve to a pathed source.");
|
|
2846
|
+
const response = await fetchDashManifest(manifestSource, resolvedUrl);
|
|
2847
|
+
return {
|
|
2848
|
+
text: await response.text(),
|
|
2849
|
+
url: response.url || resolvedUrl
|
|
2850
|
+
};
|
|
2851
|
+
} finally {
|
|
2852
|
+
manifestRef.free();
|
|
2853
|
+
}
|
|
2788
2854
|
}
|
|
2789
2855
|
resetManifestUrls() {
|
|
2790
2856
|
this.mpdUrl = this.manifestUrl;
|