mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
feat(packages): airplay button (#1531)
This commit is contained in:
@@ -6,6 +6,18 @@ import { isMediaRemotePlaybackCapable } from '../../media/predicate';
|
||||
import { exitFullscreen, isFullscreen } from '../../presentation/fullscreen';
|
||||
import { isRemotePlaybackConnected, requestRemotePlayback } from '../../presentation/remote-playback';
|
||||
|
||||
/** WebKit-only addition to HTMLMediaElement exposing the active AirPlay flag. */
|
||||
interface WebKitAirplayMedia extends HTMLMediaElement {
|
||||
readonly webkitCurrentPlaybackTargetIsWireless: boolean;
|
||||
}
|
||||
|
||||
/** WebKit-specific availability event payload (not in lib.dom). */
|
||||
type WebkitAvailabilityEvent = Event & { availability: 'available' | 'not-available' };
|
||||
|
||||
function isWebKitAirplayCapable(media: EventTarget): media is WebKitAirplayMedia {
|
||||
return 'WebKitPlaybackTargetAvailabilityEvent' in globalThis && 'webkitCurrentPlaybackTargetIsWireless' in media;
|
||||
}
|
||||
|
||||
export const remotePlaybackFeature = definePlayerFeature({
|
||||
name: 'remotePlayback',
|
||||
state: ({ target }): MediaRemotePlaybackState => ({
|
||||
@@ -23,7 +35,7 @@ export const remotePlaybackFeature = definePlayerFeature({
|
||||
await exitFullscreen(media);
|
||||
}
|
||||
|
||||
return requestRemotePlayback(media);
|
||||
return await requestRemotePlayback(media);
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -32,6 +44,31 @@ export const remotePlaybackFeature = definePlayerFeature({
|
||||
|
||||
if (!isMediaRemotePlaybackCapable(media)) return;
|
||||
|
||||
// Safari's W3C `media.remote` events don't fire reliably for AirPlay
|
||||
// session changes. When WebKit's AirPlay APIs are available, drive both
|
||||
// state slices off the WebKit events and skip the W3C listeners entirely
|
||||
// so the two paths can't double-write or conflict.
|
||||
if (isWebKitAirplayCapable(media)) {
|
||||
const syncConnection = () => {
|
||||
set({
|
||||
remotePlaybackState: media.webkitCurrentPlaybackTargetIsWireless ? 'connected' : 'disconnected',
|
||||
});
|
||||
};
|
||||
|
||||
const syncAvailability = (event: Event) => {
|
||||
const { availability } = event as WebkitAvailabilityEvent;
|
||||
set({ remotePlaybackAvailability: availability === 'available' ? 'available' : 'unavailable' });
|
||||
};
|
||||
|
||||
listen(media, 'webkitplaybacktargetavailabilitychanged', syncAvailability, { signal });
|
||||
listen(media, 'webkitcurrentplaybacktargetiswirelesschanged', syncConnection, { signal });
|
||||
|
||||
// Sync initial connection state in case AirPlay was already active.
|
||||
syncConnection();
|
||||
return;
|
||||
}
|
||||
|
||||
// W3C Remote Playback path (Chromium / Edge with the cast extension).
|
||||
const syncState = () => set({ remotePlaybackState: media.remote.state as RemotePlaybackConnectionState });
|
||||
|
||||
syncState();
|
||||
|
||||
Reference in New Issue
Block a user