mirror of
https://github.com/arcodange-org/mediabunny.git
synced 2026-09-27 02:43:48 +02:00
Properly handle subrequests for redirected URLs
This commit is contained in:
@@ -77,12 +77,15 @@ export class HlsDemuxer extends Demuxer {
|
||||
readMetadata() {
|
||||
return this.metadataPromise ??= (async () => {
|
||||
assert(this.input._rootSource instanceof PathedSource);
|
||||
const { rootPath } = this.input._rootSource;
|
||||
|
||||
const slice = await this.input._reader.requestEntireFile();
|
||||
assert(slice);
|
||||
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: {
|
||||
fullPath: string;
|
||||
attributes: AttributeList;
|
||||
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
base64ToBytes,
|
||||
} from '../misc';
|
||||
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 {
|
||||
AttributeList,
|
||||
@@ -68,6 +68,7 @@ export type HlsSegmentLocation = {
|
||||
};
|
||||
|
||||
export class HlsSegmentedInput extends SegmentedInput {
|
||||
rootPath: string;
|
||||
demuxer: HlsDemuxer;
|
||||
segments: HlsSegment[] = [];
|
||||
nextLines: string[] | null = null;
|
||||
@@ -84,6 +85,7 @@ export class HlsSegmentedInput extends SegmentedInput {
|
||||
) {
|
||||
super(demuxer.input, path, trackDeclarations);
|
||||
|
||||
this.rootPath = path;
|
||||
this.demuxer = demuxer;
|
||||
this.nextLines = lines;
|
||||
}
|
||||
@@ -126,12 +128,17 @@ export class HlsSegmentedInput extends SegmentedInput {
|
||||
this.nextLines = null;
|
||||
|
||||
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 slice = await reader.requestEntireFile();
|
||||
assert(slice);
|
||||
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;
|
||||
@@ -219,7 +226,7 @@ export class HlsSegmentedInput extends SegmentedInput {
|
||||
key = { ...key, iv };
|
||||
}
|
||||
|
||||
const fullPath = joinPaths(this.path, line);
|
||||
const fullPath = joinPaths(this.rootPath, line);
|
||||
const location: HlsSegmentLocation = {
|
||||
path: fullPath,
|
||||
offset: nextByteRange?.offset ?? 0,
|
||||
@@ -299,7 +306,7 @@ export class HlsSegmentedInput extends SegmentedInput {
|
||||
}
|
||||
|
||||
if (!prevLastSegment) {
|
||||
const fullPath = joinPaths(this.path, uri);
|
||||
const fullPath = joinPaths(this.rootPath, uri);
|
||||
const location: HlsSegmentLocation = {
|
||||
path: fullPath,
|
||||
offset: parsedByteRange?.offset ?? 0,
|
||||
@@ -375,7 +382,7 @@ export class HlsSegmentedInput extends SegmentedInput {
|
||||
|
||||
currentKey = {
|
||||
method: 'AES-128',
|
||||
keyUri: joinPaths(this.path, uri),
|
||||
keyUri: joinPaths(this.rootPath, uri),
|
||||
iv,
|
||||
keyFormat,
|
||||
};
|
||||
|
||||
+16
-3
@@ -289,10 +289,15 @@ export class SourceRef<S extends Source = Source> implements Disposable {
|
||||
*/
|
||||
export abstract class PathedSource extends Source {
|
||||
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,
|
||||
/** 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') {
|
||||
throw new TypeError('rootPath must be a string.');
|
||||
@@ -778,7 +783,10 @@ export class UrlSource extends PathedSource {
|
||||
? url.href
|
||||
: url;
|
||||
|
||||
super(urlString, request => new UrlSource(request.path, this._options));
|
||||
super(
|
||||
urlString,
|
||||
request => new UrlSource(request.path, this._options),
|
||||
);
|
||||
|
||||
this._url = url;
|
||||
this._options = options;
|
||||
@@ -905,6 +913,11 @@ export class UrlSource extends PathedSource {
|
||||
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:
|
||||
if (this._orchestrator.fileSize === null) {
|
||||
// See if we can deduce the file size from the response
|
||||
|
||||
Reference in New Issue
Block a user