function loadScript(src) { return new Promise((resolve, reject) => { const script = document.createElement('script') script.src = src script.onload = resolve script.onerror = reject document.body.appendChild(script) }) } function getMimeType(url) { const extension = url.split('?')[0].split('#')[0].split('.').pop().toLowerCase() const mimeTypes = { mp4: 'video/mp4', webm: 'video/webm', ogg: 'video/ogg', ogv: 'video/ogg', mp3: 'audio/mp3', wav: 'audio/wav', flv: 'video/x-flv', mov: 'video/quicktime', avi: 'video/x-msvideo', wmv: 'video/x-ms-wmv', mpd: 'application/dash+xml', m3u8: 'application/x-mpegURL', } return mimeTypes[extension] || 'application/octet-stream' } export default function artplayerPluginChromecast(option) { const DEFAULT_ICON = `` const DEFAULT_SDK = 'https://www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1' let isCastInitialized = false let castSession = null let castState = null const updateCastButton = (state) => { const button = document.querySelector('.art-icon-cast') if (button) { switch (state) { case 'connected': button.style.color = 'red' break case 'connecting': case 'disconnecting': button.style.color = 'orange' break case 'disconnected': default: button.style.color = 'white' break } } } const initializeCastApi = () => { return new Promise((resolve, reject) => { window.__onGCastApiAvailable = (isAvailable) => { if (isAvailable) { const context = window.cast.framework.CastContext.getInstance() context.setOptions({ receiverApplicationId: window.chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID, autoJoinPolicy: window.chrome.cast.AutoJoinPolicy.ORIGIN_SCOPED, }) // Listen for session state changes context.addEventListener( window.cast.framework.CastContextEventType.SESSION_STATE_CHANGED, (event) => { const SessionState = window.cast.framework.SessionState castState = event.sessionState castSession = event.session switch (event.sessionState) { case SessionState.NO_SESSION: option.onStateChange?.('disconnected') updateCastButton('disconnected') break case SessionState.SESSION_STARTING: option.onStateChange?.('connecting') updateCastButton('connecting') break case SessionState.SESSION_STARTED: option.onStateChange?.('connected') updateCastButton('connected') break case SessionState.SESSION_ENDING: option.onStateChange?.('disconnecting') updateCastButton('disconnecting') break case SessionState.SESSION_RESUMED: option.onStateChange?.('connected') updateCastButton('connected') break } }, ) // Listen for cast state changes context.addEventListener(window.cast.framework.CastContextEventType.CAST_STATE_CHANGED, (event) => { const CastState = window.cast.framework.CastState switch (event.castState) { case CastState.NO_DEVICES_AVAILABLE: option.onCastAvailable?.(false) break case CastState.NOT_CONNECTED: option.onCastAvailable?.(true) break case CastState.CONNECTING: case CastState.CONNECTED: option.onCastAvailable?.(true) break } }) isCastInitialized = true resolve() } else { reject(new Error('Cast API is not available')) } } if (!window.chrome || !window.chrome.cast) { loadScript(option.sdk || DEFAULT_SDK).catch(reject) } }) } const castVideo = (art, session) => { const url = option.url || art.option.url const mediaInfo = new window.chrome.cast.media.MediaInfo(url, option.mimeType || getMimeType(url)) const request = new window.chrome.cast.media.LoadRequest(mediaInfo) session .loadMedia(request) .then(() => { art.notice.show = 'Casting started' option.onCastStart?.() }) .catch((error) => { art.notice.show = 'Error casting media' option.onError?.(error) throw error }) } return async (art) => { art.controls.add({ name: 'chromecast', position: 'right', tooltip: 'Chromecast', html: `${option.icon || DEFAULT_ICON}`, click: async () => { if (!isCastInitialized) { try { await initializeCastApi() } catch (error) { art.notice.show = 'Failed to initialize Cast API' option.onError?.(error) throw error } } const context = window.cast.framework.CastContext.getInstance() if (castSession) { castVideo(art, castSession) } else { try { const session = await context.requestSession() castVideo(art, session) } catch (error) { art.notice.show = 'Error connecting to cast session' option.onError?.(error) throw error } } }, }) return { name: 'artplayerPluginChromecast', getCastState: () => castState, isCasting: () => castSession !== null, } } }