effective referer

This commit is contained in:
Kyush 2026-06-06 20:22:04 +09:00
commit fbc4d9bad9
4 changed files with 42 additions and 20 deletions

View file

@ -13,15 +13,20 @@ export interface FetchSegmentResult {
bytes: number;
}
function gmFetchBuffer(url: string, referer?: string): Promise<FetchSegmentResult> {
function gmFetchBuffer(url: string, referer?: string, origin?: string): Promise<FetchSegmentResult> {
const fallbackReferer = document.location.href;
const fallbackOrigin = new URL(document.location.href).origin;
const effectiveReferer = referer || fallbackReferer;
const effectiveOrigin = origin || (referer ? new URL(referer).origin : fallbackOrigin);
return new Promise((resolve, reject) => {
(GM_xmlhttpRequest as any)({
method: 'GET',
url: url,
responseType: 'arraybuffer',
referer: referer || document.location.href,
referer: effectiveReferer,
headers: {
'Origin': new URL(document.location.href).origin,
'Origin': effectiveOrigin,
},
onload: (response: any) => {
if (response.status >= 200 && response.status < 300) {
@ -41,11 +46,11 @@ function gmFetchBuffer(url: string, referer?: string): Promise<FetchSegmentResul
});
}
async function fetchWithRetry(url: string, referer?: string, maxRetries = 3): Promise<FetchSegmentResult> {
async function fetchWithRetry(url: string, referer?: string, origin?: string, maxRetries = 3): Promise<FetchSegmentResult> {
let lastError: Error | null = null;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await gmFetchBuffer(url, referer);
return await gmFetchBuffer(url, referer, origin);
} catch (e) {
lastError = e as Error;
log.warn(`fetchWithRetry: attempt ${attempt}/${maxRetries} failed for ${url}: ${e}`);
@ -60,6 +65,7 @@ async function fetchWithRetry(url: string, referer?: string, maxRetries = 3): Pr
export async function fetchSegments(
urls: string[],
referer?: string,
origin?: string,
onProgress?: (progress: DownloadProgress) => void,
concurrency = 5
): Promise<FetchSegmentResult[]> {
@ -79,7 +85,7 @@ export async function fetchSegments(
while (queue.length > 0) {
const idx = queue.shift()!;
try {
const result = await fetchWithRetry(urls[idx], referer);
const result = await fetchWithRetry(urls[idx], referer, origin);
results[idx] = result;
bytesDownloaded += result.bytes;
current++;

View file

@ -89,7 +89,7 @@ async function startDownload(url: string): Promise<void> {
if (!parsed) {
try {
parsed = await parseM3U8(url, entry?.headers?.Referer);
parsed = await parseM3U8(url, entry?.headers?.Referer, entry?.headers?.Origin);
if (entry) entry.parsed = parsed;
} catch (e) {
log.error(`startDownload: parse failed for ${url}: ${e}`);
@ -107,7 +107,7 @@ async function startDownload(url: string): Promise<void> {
}
targetUrl = selected;
try {
parsed = await parseM3U8(targetUrl, entry?.headers?.Referer);
parsed = await parseM3U8(targetUrl, entry?.headers?.Referer, entry?.headers?.Origin);
if (entry) entry.parsed = parsed;
} catch (e) {
log.error(`startDownload: parse selected playlist failed: ${e}`);
@ -123,7 +123,8 @@ async function startDownload(url: string): Promise<void> {
}
const segmentUrls = parsed.segments.map(s => s.uri);
const referrer = entry?.headers?.Referer || document.location.href;
const referrer = entry?.headers?.Referer;
const requestOrigin = entry?.headers?.Origin;
resetProgress();
@ -131,6 +132,7 @@ async function startDownload(url: string): Promise<void> {
const results = await fetchSegments(
segmentUrls,
referrer,
requestOrigin,
(progress: DownloadProgress) => {
if (downloadAborted) return;
showStatus(progress);
@ -167,7 +169,7 @@ function onM3U8Detected(url: string, headers?: Record<string, string>): void {
}
if (isTopFrame()) {
parseM3U8(url, headers?.Referer).then(parsed => {
parseM3U8(url, headers?.Referer, headers?.Origin).then(parsed => {
detectedM3U8s.get(url)!.parsed = parsed;
if (panel) {
addStreamToUI(url, parsed);
@ -191,7 +193,11 @@ if (typeof window === 'undefined') {
window.addEventListener('message', (e: MessageEvent) => {
if (e.data && typeof e.data === 'object' && e.data.__m3u8dl === true && e.data.url) {
const referer = typeof e.data.referer === 'string' ? e.data.referer : '';
onM3U8Detected(e.data.url as string, referer ? { Referer: referer } : undefined);
const origin = typeof e.data.origin === 'string' ? e.data.origin : '';
const headers: Record<string, string> = {};
if (referer) headers['Referer'] = referer;
if (origin) headers['Origin'] = origin;
onM3U8Detected(e.data.url as string, Object.keys(headers).length ? headers : undefined);
}
});

View file

@ -1,16 +1,21 @@
import { log } from '../logger';
export async function fetchKey(keyUri: string, referer?: string): Promise<Uint8Array> {
export async function fetchKey(keyUri: string, referer?: string, origin?: string): Promise<Uint8Array> {
log.info(`fetchKey: ${keyUri}`);
const fallbackReferer = document.location.href;
const fallbackOrigin = new URL(document.location.href).origin;
const effectiveReferer = referer || fallbackReferer;
const effectiveOrigin = origin || (referer ? new URL(referer).origin : fallbackOrigin);
return new Promise((resolve, reject) => {
(GM_xmlhttpRequest as any)({
method: 'GET',
url: keyUri,
responseType: 'arraybuffer',
referer: referer || document.location.href,
referer: effectiveReferer,
headers: {
'Origin': new URL(document.location.href).origin,
'Origin': effectiveOrigin,
},
onload: (response: any) => {
if (response.status >= 200 && response.status < 300) {

View file

@ -28,15 +28,20 @@ export interface MasterPlaylistInfo {
name?: string;
}
function gmFetchText(url: string, referer?: string): Promise<string> {
function gmFetchText(url: string, referer?: string, origin?: string): Promise<string> {
const fallbackReferer = document.location.href;
const fallbackOrigin = new URL(document.location.href).origin;
const effectiveReferer = referer || fallbackReferer;
const effectiveOrigin = origin || (referer ? new URL(referer).origin : fallbackOrigin);
return new Promise((resolve, reject) => {
(GM_xmlhttpRequest as any)({
method: 'GET',
url: url,
responseType: 'text',
referer: referer || document.location.href,
referer: effectiveReferer,
headers: {
'Origin': new URL(document.location.href).origin,
'Origin': effectiveOrigin,
},
onload: (response: any) => {
if (response.status >= 200 && response.status < 300) {
@ -52,10 +57,10 @@ function gmFetchText(url: string, referer?: string): Promise<string> {
});
}
export async function parseM3U8(url: string, referer?: string): Promise<ParsedPlaylist> {
export async function parseM3U8(url: string, referer?: string, origin?: string): Promise<ParsedPlaylist> {
log.info(`parseM3U8: ${url}`);
const content = await gmFetchText(url, referer);
const content = await gmFetchText(url, referer, origin);
if (!content || !content.startsWith('#EXTM3U')) {
throw new Error(`Invalid m3u8 content from ${url}`);
}
@ -106,7 +111,7 @@ export async function parseM3U8(url: string, referer?: string): Promise<ParsedPl
let keyBytes = keyCache.get(keyUri);
if (!keyBytes) {
try {
keyBytes = await fetchKey(keyUri, referer);
keyBytes = await fetchKey(keyUri, referer, origin);
keyCache.set(keyUri, keyBytes);
} catch (e) {
log.error(`parseM3U8: failed to fetch key ${keyUri}: ${e}`);