mirror of
https://github.com/p-stream/p-stream.git
synced 2026-05-13 16:50:58 +00:00
update airplay and casting
This commit is contained in:
parent
c0029577e2
commit
6f67a7c7fb
3 changed files with 50 additions and 14 deletions
|
|
@ -1,12 +1,15 @@
|
||||||
import { Icons } from "@/components/Icon";
|
import { Icons } from "@/components/Icon";
|
||||||
import { VideoPlayerButton } from "@/components/player/internals/Button";
|
import { VideoPlayerButton } from "@/components/player/internals/Button";
|
||||||
import { usePlayerStore } from "@/stores/player/store";
|
import { usePlayerStore } from "@/stores/player/store";
|
||||||
|
import { isSafari } from "@/utils/detectFeatures";
|
||||||
|
|
||||||
export function Airplay() {
|
export function Airplay() {
|
||||||
const canAirplay = usePlayerStore((s) => s.interface.canAirplay);
|
const canAirplay = usePlayerStore((s) => s.interface.canAirplay);
|
||||||
const display = usePlayerStore((s) => s.display);
|
const display = usePlayerStore((s) => s.display);
|
||||||
|
|
||||||
if (!canAirplay) return null;
|
// Show Airplay button on Safari browsers (which support AirPlay natively)
|
||||||
|
// or when the webkit event has confirmed availability
|
||||||
|
if (!canAirplay && !isSafari) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<VideoPlayerButton
|
<VideoPlayerButton
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,8 @@ export function Chromecast({ className }: ChromecastProps) {
|
||||||
const context = castFramework.CastContext.getInstance();
|
const context = castFramework.CastContext.getInstance();
|
||||||
const updateVisibility = () => {
|
const updateVisibility = () => {
|
||||||
const state = context.getCastState();
|
const state = context.getCastState();
|
||||||
|
// Hide only if we know for sure there are no devices available
|
||||||
|
// Show the button for other states (NOT_CONNECTED, CONNECTING, CONNECTED)
|
||||||
setCastHidden(state === castFramework.CastState.NO_DEVICES_AVAILABLE);
|
setCastHidden(state === castFramework.CastState.NO_DEVICES_AVAILABLE);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -784,19 +784,41 @@ export function makeVideoElementDisplayInterface(): DisplayInterface {
|
||||||
proxiedUrl = source.url; // Already proxied or no headers needed
|
proxiedUrl = source.url; // Already proxied or no headers needed
|
||||||
}
|
}
|
||||||
} else if (source?.type === "mp4") {
|
} else if (source?.type === "mp4") {
|
||||||
// TODO: Implement MP4 proxy for protected streams
|
const allHeaders = {
|
||||||
const hasHeaders =
|
...source.preferredHeaders,
|
||||||
source.headers && Object.keys(source.headers).length > 0;
|
...source.headers,
|
||||||
if (hasHeaders) {
|
};
|
||||||
|
const hasHeaders = Object.keys(allHeaders).length > 0;
|
||||||
|
if (!isUrlAlreadyProxied(source.url) && hasHeaders) {
|
||||||
// Use MP4 proxy for streams with headers
|
// Use MP4 proxy for streams with headers
|
||||||
proxiedUrl = createMP4ProxyUrl(source.url, source.headers || {});
|
proxiedUrl = createMP4ProxyUrl(source.url, allHeaders);
|
||||||
} else {
|
} else {
|
||||||
proxiedUrl = source.url;
|
proxiedUrl = source.url;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Function to restore original URL
|
||||||
|
const restoreOriginalUrl = () => {
|
||||||
|
if (source?.type === "hls") {
|
||||||
|
if (hls && originalUrl) {
|
||||||
|
hls.loadSource(originalUrl);
|
||||||
|
}
|
||||||
|
} else if (originalUrl) {
|
||||||
|
videoPlayer.src = originalUrl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Function to check airplay state and restore if needed
|
||||||
|
const checkAirplayState = () => {
|
||||||
|
const isWireless = videoPlayer.webkitCurrentPlaybackTargetIsWireless;
|
||||||
|
if (!isWireless) {
|
||||||
|
// Airplay didn't start or ended, restore original URL
|
||||||
|
restoreOriginalUrl();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (proxiedUrl && proxiedUrl !== originalUrl) {
|
if (proxiedUrl && proxiedUrl !== originalUrl) {
|
||||||
// Temporarily set the proxied URL for Airplay
|
// Set the proxied URL for Airplay
|
||||||
if (source?.type === "hls") {
|
if (source?.type === "hls") {
|
||||||
if (hls) {
|
if (hls) {
|
||||||
hls.loadSource(proxiedUrl);
|
hls.loadSource(proxiedUrl);
|
||||||
|
|
@ -809,16 +831,25 @@ export function makeVideoElementDisplayInterface(): DisplayInterface {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
videoPlayer.webkitShowPlaybackTargetPicker();
|
videoPlayer.webkitShowPlaybackTargetPicker();
|
||||||
|
|
||||||
// Restore original URL after a short delay
|
// Check airplay state after user interaction
|
||||||
|
// Give user time to select device, then check if airplay started
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (source?.type === "hls") {
|
checkAirplayState();
|
||||||
if (hls && originalUrl) {
|
}, 2000);
|
||||||
hls.loadSource(originalUrl);
|
|
||||||
}
|
// Set up periodic check for airplay state changes
|
||||||
} else if (originalUrl) {
|
const airplayCheckInterval = setInterval(() => {
|
||||||
videoPlayer.src = originalUrl;
|
const isWireless =
|
||||||
|
videoPlayer.webkitCurrentPlaybackTargetIsWireless;
|
||||||
|
if (!isWireless) {
|
||||||
|
// Airplay ended, restore original URL
|
||||||
|
restoreOriginalUrl();
|
||||||
|
clearInterval(airplayCheckInterval);
|
||||||
}
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
|
// Clear interval after 5 minutes as safety measure
|
||||||
|
setTimeout(() => clearInterval(airplayCheckInterval), 300000);
|
||||||
}, 100);
|
}, 100);
|
||||||
} else {
|
} else {
|
||||||
// No proxying needed, just trigger Airplay
|
// No proxying needed, just trigger Airplay
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue