Properly handle subrequests for redirected URLs

This commit is contained in:
Vanilagy
2026-06-16 14:53:27 +02:00
parent b4a6757739
commit 2f0c040fcd
3 changed files with 32 additions and 9 deletions
+4 -1
View File
@@ -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;
+12 -5
View File
@@ -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
View File
@@ -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