pstream is dead; long live pstream taciturnaxolotl.github.io/pstream-ng/
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

attempt to fix airplay and add m3u8 proxy tool

Pas e658d13e 328f50bb

+132 -1
+74 -1
src/components/player/display/base.ts
··· 12 12 } from "@/components/player/display/displayInterface"; 13 13 import { handleBuffered } from "@/components/player/utils/handleBuffered"; 14 14 import { getMediaErrorDetails } from "@/components/player/utils/mediaErrorDetails"; 15 + import { 16 + createM3U8ProxyUrl, 17 + createMP4ProxyUrl, 18 + isUrlAlreadyProxied, 19 + } from "@/components/player/utils/proxy"; 15 20 import { useLanguageStore } from "@/stores/language"; 16 21 import { 17 22 LoadableSource, ··· 579 584 }, 580 585 startAirplay() { 581 586 const videoPlayer = videoElement as any; 582 - if (videoPlayer && videoPlayer.webkitShowPlaybackTargetPicker) { 587 + if (!videoPlayer || !videoPlayer.webkitShowPlaybackTargetPicker) return; 588 + 589 + if (!source) { 590 + // No source loaded, just trigger Airplay 591 + videoPlayer.webkitShowPlaybackTargetPicker(); 592 + return; 593 + } 594 + 595 + // Store the original URL to restore later 596 + const originalUrl = 597 + source?.type === "hls" ? hls?.url || source.url : videoPlayer.src; 598 + 599 + let proxiedUrl: string | null = null; 600 + 601 + if (source?.type === "hls") { 602 + // Only proxy HLS streams if they need it: 603 + // 1. Not already proxied AND 604 + // 2. Has headers (either preferredHeaders or headers) 605 + const allHeaders = { 606 + ...source.preferredHeaders, 607 + ...source.headers, 608 + }; 609 + const hasHeaders = Object.keys(allHeaders).length > 0; 610 + 611 + // Don't create proxy URL if it's already using the proxy 612 + if (!isUrlAlreadyProxied(source.url) && hasHeaders) { 613 + proxiedUrl = createM3U8ProxyUrl(source.url, allHeaders); 614 + } else { 615 + proxiedUrl = source.url; // Already proxied or no headers needed 616 + } 617 + } else if (source?.type === "mp4") { 618 + // TODO: Implement MP4 proxy for protected streams 619 + const hasHeaders = 620 + source.headers && Object.keys(source.headers).length > 0; 621 + if (hasHeaders) { 622 + // Use MP4 proxy for streams with headers 623 + proxiedUrl = createMP4ProxyUrl(source.url, source.headers || {}); 624 + } else { 625 + proxiedUrl = source.url; 626 + } 627 + } 628 + 629 + if (proxiedUrl && proxiedUrl !== originalUrl) { 630 + // Temporarily set the proxied URL for Airplay 631 + if (source?.type === "hls") { 632 + if (hls) { 633 + hls.loadSource(proxiedUrl); 634 + } 635 + } else { 636 + videoPlayer.src = proxiedUrl; 637 + } 638 + 639 + // Small delay to ensure the URL is set before triggering Airplay 640 + setTimeout(() => { 641 + videoPlayer.webkitShowPlaybackTargetPicker(); 642 + 643 + // Restore original URL after a short delay 644 + setTimeout(() => { 645 + if (source?.type === "hls") { 646 + if (hls && originalUrl) { 647 + hls.loadSource(originalUrl); 648 + } 649 + } else if (originalUrl) { 650 + videoPlayer.src = originalUrl; 651 + } 652 + }, 1000); 653 + }, 100); 654 + } else { 655 + // No proxying needed, just trigger Airplay 583 656 videoPlayer.webkitShowPlaybackTargetPicker(); 584 657 } 585 658 },
+58
src/components/player/utils/proxy.ts
··· 1 + import { getLoadbalancedM3U8ProxyUrl } from "@/backend/providers/fetchers"; 2 + import { getM3U8ProxyUrls } from "@/utils/proxyUrls"; 3 + 4 + /** 5 + * Creates a proxied M3U8 URL for HLS streams using a random proxy from config 6 + * @param url - The original M3U8 URL to proxy 7 + * @param headers - Headers to include with the request 8 + * @returns The proxied M3U8 URL 9 + */ 10 + export function createM3U8ProxyUrl( 11 + url: string, 12 + headers: Record<string, string> = {}, 13 + ): string { 14 + // Get a random M3U8 proxy URL from the configuration 15 + const proxyBaseUrl = getLoadbalancedM3U8ProxyUrl(); 16 + 17 + if (!proxyBaseUrl) { 18 + console.warn("No M3U8 proxy URLs available in configuration"); 19 + return url; // Fallback to original URL 20 + } 21 + 22 + const encodedUrl = encodeURIComponent(url); 23 + const encodedHeaders = encodeURIComponent(JSON.stringify(headers)); 24 + return `${proxyBaseUrl}/m3u8-proxy?url=${encodedUrl}${headers ? `&headers=${encodedHeaders}` : ""}`; 25 + } 26 + 27 + /** 28 + * TODO: Creates a proxied MP4 URL for MP4 streams 29 + * @param url - The original MP4 URL to proxy 30 + * @param headers - Headers to include with the request 31 + * @returns The proxied MP4 URL 32 + */ 33 + export function createMP4ProxyUrl( 34 + url: string, 35 + _headers: Record<string, string> = {}, 36 + ): string { 37 + // TODO: Implement MP4 proxy for protected streams 38 + // This would need a separate MP4 proxy service that can handle headers 39 + // For now, return the original URL 40 + console.warn("MP4 proxy not yet implemented - using original URL"); 41 + return url; 42 + } 43 + 44 + /** 45 + * Checks if a URL is already using one of the configured M3U8 proxy services 46 + * @param url - The URL to check 47 + * @returns True if the URL is already proxied, false otherwise 48 + */ 49 + export function isUrlAlreadyProxied(url: string): boolean { 50 + // Check if URL contains the m3u8-proxy pattern 51 + if (url.includes("/m3u8-proxy?url=")) { 52 + return true; 53 + } 54 + 55 + // Also check if URL starts with any of the configured proxy URLs 56 + const proxyUrls = getM3U8ProxyUrls(); 57 + return proxyUrls.some((proxyUrl) => url.startsWith(proxyUrl)); 58 + }