mirror of
https://github.com/arcodange-org/mediabunny.git
synced 2026-09-27 19:03:46 +02:00
Properly handle subrequests for redirected URLs
This commit is contained in:
@@ -77,12 +77,15 @@ export class HlsDemuxer extends Demuxer {
|
|||||||
readMetadata() {
|
readMetadata() {
|
||||||
return this.metadataPromise ??= (async () => {
|
return this.metadataPromise ??= (async () => {
|
||||||
assert(this.input._rootSource instanceof PathedSource);
|
assert(this.input._rootSource instanceof PathedSource);
|
||||||
const { rootPath } = this.input._rootSource;
|
|
||||||
|
|
||||||
const slice = await this.input._reader.requestEntireFile();
|
const slice = await this.input._reader.requestEntireFile();
|
||||||
assert(slice);
|
assert(slice);
|
||||||
const lines = readAllLines(slice, slice.length, { ignore: canIgnoreLine });
|
const lines = readAllLines(slice, slice.length, { ignore: canIgnoreLine });
|
||||||
|
|
||||||
|
// Important: get the root path AFTER reading data to get the final root path, possibly affected by
|
||||||
|
// redirects. Any follow requests should be related to the redirected path, not the original one.
|
||||||
|
const { rootPath } = this.input._rootSource;
|
||||||
|
|
||||||
const variantStreams: {
|
const variantStreams: {
|
||||||
fullPath: string;
|
fullPath: string;
|
||||||
attributes: AttributeList;
|
attributes: AttributeList;
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import {
|
|||||||
base64ToBytes,
|
base64ToBytes,
|
||||||
} from '../misc';
|
} from '../misc';
|
||||||
import { readAllLines, readBytes, Reader } from '../reader';
|
import { readAllLines, readBytes, Reader } from '../reader';
|
||||||
import { CustomPathedSource, ReadableStreamSource, SourceRef, SourceRequest } from '../source';
|
import { CustomPathedSource, PathedSource, ReadableStreamSource, SourceRef, SourceRequest } from '../source';
|
||||||
import { HlsDemuxer } from './hls-demuxer';
|
import { HlsDemuxer } from './hls-demuxer';
|
||||||
import {
|
import {
|
||||||
AttributeList,
|
AttributeList,
|
||||||
@@ -68,6 +68,7 @@ export type HlsSegmentLocation = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export class HlsSegmentedInput extends SegmentedInput {
|
export class HlsSegmentedInput extends SegmentedInput {
|
||||||
|
rootPath: string;
|
||||||
demuxer: HlsDemuxer;
|
demuxer: HlsDemuxer;
|
||||||
segments: HlsSegment[] = [];
|
segments: HlsSegment[] = [];
|
||||||
nextLines: string[] | null = null;
|
nextLines: string[] | null = null;
|
||||||
@@ -84,6 +85,7 @@ export class HlsSegmentedInput extends SegmentedInput {
|
|||||||
) {
|
) {
|
||||||
super(demuxer.input, path, trackDeclarations);
|
super(demuxer.input, path, trackDeclarations);
|
||||||
|
|
||||||
|
this.rootPath = path;
|
||||||
this.demuxer = demuxer;
|
this.demuxer = demuxer;
|
||||||
this.nextLines = lines;
|
this.nextLines = lines;
|
||||||
}
|
}
|
||||||
@@ -126,12 +128,17 @@ export class HlsSegmentedInput extends SegmentedInput {
|
|||||||
this.nextLines = null;
|
this.nextLines = null;
|
||||||
|
|
||||||
if (!lines) {
|
if (!lines) {
|
||||||
using ref = await this.demuxer.input._getSourceUncached({ path: this.path, isRoot: false });
|
using ref = await this.demuxer.input._getSourceUncached({ path: this.rootPath, isRoot: false });
|
||||||
const reader = new Reader(ref.source);
|
const reader = new Reader(ref.source);
|
||||||
|
|
||||||
const slice = await reader.requestEntireFile();
|
const slice = await reader.requestEntireFile();
|
||||||
assert(slice);
|
assert(slice);
|
||||||
lines = readAllLines(slice, slice.length, { ignore: canIgnoreLine });
|
lines = readAllLines(slice, slice.length, { ignore: canIgnoreLine });
|
||||||
|
|
||||||
|
if (ref.source instanceof PathedSource) {
|
||||||
|
// Copy back the source's path to become aware of potential redirects
|
||||||
|
this.rootPath = ref.source.rootPath;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let headerRead = false;
|
let headerRead = false;
|
||||||
@@ -219,7 +226,7 @@ export class HlsSegmentedInput extends SegmentedInput {
|
|||||||
key = { ...key, iv };
|
key = { ...key, iv };
|
||||||
}
|
}
|
||||||
|
|
||||||
const fullPath = joinPaths(this.path, line);
|
const fullPath = joinPaths(this.rootPath, line);
|
||||||
const location: HlsSegmentLocation = {
|
const location: HlsSegmentLocation = {
|
||||||
path: fullPath,
|
path: fullPath,
|
||||||
offset: nextByteRange?.offset ?? 0,
|
offset: nextByteRange?.offset ?? 0,
|
||||||
@@ -299,7 +306,7 @@ export class HlsSegmentedInput extends SegmentedInput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!prevLastSegment) {
|
if (!prevLastSegment) {
|
||||||
const fullPath = joinPaths(this.path, uri);
|
const fullPath = joinPaths(this.rootPath, uri);
|
||||||
const location: HlsSegmentLocation = {
|
const location: HlsSegmentLocation = {
|
||||||
path: fullPath,
|
path: fullPath,
|
||||||
offset: parsedByteRange?.offset ?? 0,
|
offset: parsedByteRange?.offset ?? 0,
|
||||||
@@ -375,7 +382,7 @@ export class HlsSegmentedInput extends SegmentedInput {
|
|||||||
|
|
||||||
currentKey = {
|
currentKey = {
|
||||||
method: 'AES-128',
|
method: 'AES-128',
|
||||||
keyUri: joinPaths(this.path, uri),
|
keyUri: joinPaths(this.rootPath, uri),
|
||||||
iv,
|
iv,
|
||||||
keyFormat,
|
keyFormat,
|
||||||
};
|
};
|
||||||
|
|||||||
+16
-3
@@ -289,10 +289,15 @@ export class SourceRef<S extends Source = Source> implements Disposable {
|
|||||||
*/
|
*/
|
||||||
export abstract class PathedSource extends Source {
|
export abstract class PathedSource extends Source {
|
||||||
constructor(
|
constructor(
|
||||||
/** The path that points to the root file; the entry file of the media. */
|
/**
|
||||||
|
* The path that points to the root file; the entry file of the media.
|
||||||
|
*
|
||||||
|
* This path may be modified by the source to indicate a redirect: an updated path to perform new requests
|
||||||
|
* relative to.
|
||||||
|
*/
|
||||||
public rootPath: FilePath,
|
public rootPath: FilePath,
|
||||||
/** The callback that is called for each requested file; must return a {@link Source} or {@link SourceRef}. */
|
/** The callback that is called for each requested file; must return a {@link Source} or {@link SourceRef}. */
|
||||||
public requestHandler: (request: SourceRequest) => MaybePromise<Source | SourceRef>,
|
public readonly requestHandler: (request: SourceRequest) => MaybePromise<Source | SourceRef>,
|
||||||
) {
|
) {
|
||||||
if (typeof rootPath !== 'string') {
|
if (typeof rootPath !== 'string') {
|
||||||
throw new TypeError('rootPath must be a string.');
|
throw new TypeError('rootPath must be a string.');
|
||||||
@@ -778,7 +783,10 @@ export class UrlSource extends PathedSource {
|
|||||||
? url.href
|
? url.href
|
||||||
: url;
|
: url;
|
||||||
|
|
||||||
super(urlString, request => new UrlSource(request.path, this._options));
|
super(
|
||||||
|
urlString,
|
||||||
|
request => new UrlSource(request.path, this._options),
|
||||||
|
);
|
||||||
|
|
||||||
this._url = url;
|
this._url = url;
|
||||||
this._options = options;
|
this._options = options;
|
||||||
@@ -905,6 +913,11 @@ export class UrlSource extends PathedSource {
|
|||||||
throw new Error(`Error fetching ${String(this._url)}: ${response.status} ${response.statusText}`);
|
throw new Error(`Error fetching ${String(this._url)}: ${response.status} ${response.statusText}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (response.redirected) {
|
||||||
|
// Modify our own root path so that future subrequests get made relative to the redirected URL
|
||||||
|
this.rootPath = response.url;
|
||||||
|
}
|
||||||
|
|
||||||
outer:
|
outer:
|
||||||
if (this._orchestrator.fileSize === null) {
|
if (this._orchestrator.fileSize === null) {
|
||||||
// See if we can deduce the file size from the response
|
// See if we can deduce the file size from the response
|
||||||
|
|||||||
Reference in New Issue
Block a user