attempt to fix airplay and add m3u8 proxy tool

This commit is contained in:
Pas 2025-10-24 19:23:48 -06:00
parent 328f50bbb9
commit e658d13ec5
2 changed files with 132 additions and 1 deletions

View file

@ -12,6 +12,11 @@ import {
} from "@/components/player/display/displayInterface";
import { handleBuffered } from "@/components/player/utils/handleBuffered";
import { getMediaErrorDetails } from "@/components/player/utils/mediaErrorDetails";
import {
createM3U8ProxyUrl,
createMP4ProxyUrl,
isUrlAlreadyProxied,
} from "@/components/player/utils/proxy";
import { useLanguageStore } from "@/stores/language";
import {
LoadableSource,
@ -579,7 +584,75 @@ export function makeVideoElementDisplayInterface(): DisplayInterface {
},
startAirplay() {
const videoPlayer = videoElement as any;
if (videoPlayer && videoPlayer.webkitShowPlaybackTargetPicker) {
if (!videoPlayer || !videoPlayer.webkitShowPlaybackTargetPicker) return;
if (!source) {
// No source loaded, just trigger Airplay
videoPlayer.webkitShowPlaybackTargetPicker();
return;
}
// Store the original URL to restore later
const originalUrl =
source?.type === "hls" ? hls?.url || source.url : videoPlayer.src;
let proxiedUrl: string | null = null;
if (source?.type === "hls") {
// Only proxy HLS streams if they need it:
// 1. Not already proxied AND
// 2. Has headers (either preferredHeaders or headers)
const allHeaders = {
...source.preferredHeaders,
...source.headers,
};
const hasHeaders = Object.keys(allHeaders).length > 0;
// Don't create proxy URL if it's already using the proxy
if (!isUrlAlreadyProxied(source.url) && hasHeaders) {
proxiedUrl = createM3U8ProxyUrl(source.url, allHeaders);
} else {
proxiedUrl = source.url; // Already proxied or no headers needed
}
} else if (source?.type === "mp4") {
// TODO: Implement MP4 proxy for protected streams
const hasHeaders =
source.headers && Object.keys(source.headers).length > 0;
if (hasHeaders) {
// Use MP4 proxy for streams with headers
proxiedUrl = createMP4ProxyUrl(source.url, source.headers || {});
} else {
proxiedUrl = source.url;
}
}
if (proxiedUrl && proxiedUrl !== originalUrl) {
// Temporarily set the proxied URL for Airplay
if (source?.type === "hls") {
if (hls) {
hls.loadSource(proxiedUrl);
}
} else {
videoPlayer.src = proxiedUrl;
}
// Small delay to ensure the URL is set before triggering Airplay
setTimeout(() => {
videoPlayer.webkitShowPlaybackTargetPicker();
// Restore original URL after a short delay
setTimeout(() => {
if (source?.type === "hls") {
if (hls && originalUrl) {
hls.loadSource(originalUrl);
}
} else if (originalUrl) {
videoPlayer.src = originalUrl;
}
}, 1000);
}, 100);
} else {
// No proxying needed, just trigger Airplay
videoPlayer.webkitShowPlaybackTargetPicker();
}
},

View file

@ -0,0 +1,58 @@
import { getLoadbalancedM3U8ProxyUrl } from "@/backend/providers/fetchers";
import { getM3U8ProxyUrls } from "@/utils/proxyUrls";
/**
* Creates a proxied M3U8 URL for HLS streams using a random proxy from config
* @param url - The original M3U8 URL to proxy
* @param headers - Headers to include with the request
* @returns The proxied M3U8 URL
*/
export function createM3U8ProxyUrl(
url: string,
headers: Record<string, string> = {},
): string {
// Get a random M3U8 proxy URL from the configuration
const proxyBaseUrl = getLoadbalancedM3U8ProxyUrl();
if (!proxyBaseUrl) {
console.warn("No M3U8 proxy URLs available in configuration");
return url; // Fallback to original URL
}
const encodedUrl = encodeURIComponent(url);
const encodedHeaders = encodeURIComponent(JSON.stringify(headers));
return `${proxyBaseUrl}/m3u8-proxy?url=${encodedUrl}${headers ? `&headers=${encodedHeaders}` : ""}`;
}
/**
* TODO: Creates a proxied MP4 URL for MP4 streams
* @param url - The original MP4 URL to proxy
* @param headers - Headers to include with the request
* @returns The proxied MP4 URL
*/
export function createMP4ProxyUrl(
url: string,
_headers: Record<string, string> = {},
): string {
// TODO: Implement MP4 proxy for protected streams
// This would need a separate MP4 proxy service that can handle headers
// For now, return the original URL
console.warn("MP4 proxy not yet implemented - using original URL");
return url;
}
/**
* Checks if a URL is already using one of the configured M3U8 proxy services
* @param url - The URL to check
* @returns True if the URL is already proxied, false otherwise
*/
export function isUrlAlreadyProxied(url: string): boolean {
// Check if URL contains the m3u8-proxy pattern
if (url.includes("/m3u8-proxy?url=")) {
return true;
}
// Also check if URL starts with any of the configured proxy URLs
const proxyUrls = getM3U8ProxyUrls();
return proxyUrls.some((proxyUrl) => url.startsWith(proxyUrl));
}