diff --git a/browser2/src/browser.d.ts b/browser2/src/browser.d.ts index b0c9440d..7eedc966 100644 --- a/browser2/src/browser.d.ts +++ b/browser2/src/browser.d.ts @@ -80,6 +80,7 @@ type State = { widescreen: boolean; scroll_stack: Array; now: number; + casting: boolean; // login session_id: string | null; diff --git a/browser2/src/browser.ts b/browser2/src/browser.ts index 9632ab3c..c4e1a046 100644 --- a/browser2/src/browser.ts +++ b/browser2/src/browser.ts @@ -10,6 +10,7 @@ import { import { Root } from "./screens/root"; import { BeLoggedIn, getMQTTListener, ResizeListener } from "./subs"; import { ApiRequest } from "./effects"; +import { CastSender } from "./cc_sender"; declare global { namespace JSX { @@ -44,6 +45,7 @@ let state: State = { widescreen: isWidescreen(), scroll_stack: [], now: Date.now() / 1000, + casting: false, // login session_id: null, @@ -153,6 +155,7 @@ const subscriptions = (state: State): Array => [ return { ...state, now: timestamp_ms / 1000 }; }, }), + state.casting && CastSender(state.room_name, state.room_password), ]; app({ diff --git a/browser2/src/cc_sender.tsx b/browser2/src/cc_sender.tsx new file mode 100644 index 00000000..cf7a4910 --- /dev/null +++ b/browser2/src/cc_sender.tsx @@ -0,0 +1,46 @@ +function _castSender(dispatch, props) { + var applicationID = '05C10F57'; + var session = null; + + function bail(e) { + console.log("cast error", e); + dispatch((state) => ({...state, casting: false})); + } + + var apiConfig = new chrome.cast.ApiConfig( + new chrome.cast.SessionRequest(applicationID), + function (s) { console.log("session", s); }, // session listener + function (r) { console.log("receiver", r); }, // receiver listener + ); + chrome.cast.initialize( + apiConfig, + function () { }, // ok + function (e) { bail(e); }, // err + ); + chrome.cast.requestSession( + function (s) { + session = s; + session.setReceiverMuted(false); + session.setReceiverVolumeLevel(1.00); + session.sendMessage('urn:x-cast:uk.karakara', { + "command": "join", + "room_name": props.room_name, + "room_password": props.room_password, + }); + }, + function (e) {bail(e);} + ); + + return function () { + if (session) { + session.stop( + function () { session = null; }, + function (e) { console.log("session stop err", e); }, + ); + } + } +} + +export function CastSender(name, pass): Subscription { + return [_castSender, { room_name: name, room_password: pass }]; +} diff --git a/browser2/src/index.html b/browser2/src/index.html index 5c4d5fc6..ad72f514 100644 --- a/browser2/src/index.html +++ b/browser2/src/index.html @@ -9,5 +9,6 @@ + diff --git a/browser2/src/screens/control.tsx b/browser2/src/screens/control.tsx index 0386f58d..f4bc288e 100644 --- a/browser2/src/screens/control.tsx +++ b/browser2/src/screens/control.tsx @@ -236,13 +236,20 @@ const ControlButtons = (): VNode => ( ); +export const CastButton = ({ state }: { state: State }): VNode => ( + (window.chrome && chrome.cast && chrome.cast.isAvailable) && + ({...state, casting: !state.casting})}> + + +); + export const Control = ({ state }: { state: State }): VNode => ( } title={"Remote Control"} - // navRight={} + navRight={} footer={} > {current_and_future(state.now, state.queue).length == 0 ? ( diff --git a/browser2/src/static/_brands.css b/browser2/src/static/_brands.css new file mode 100644 index 00000000..676d68ea --- /dev/null +++ b/browser2/src/static/_brands.css @@ -0,0 +1,14 @@ +/*! + * Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + */ +@font-face { + font-family: 'Font Awesome 5 Brands'; + font-style: normal; + font-weight: 400; + font-display: block; + src: url("fa-brands-400.woff2") format("woff2"); } + +.fab { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } diff --git a/browser2/src/static/fa-brands-400.woff2 b/browser2/src/static/fa-brands-400.woff2 new file mode 100644 index 00000000..402f81c0 Binary files /dev/null and b/browser2/src/static/fa-brands-400.woff2 differ diff --git a/player2/src/cc_receiver.tsx b/player2/src/cc_receiver.tsx new file mode 100644 index 00000000..ca70308c --- /dev/null +++ b/player2/src/cc_receiver.tsx @@ -0,0 +1,30 @@ +function _castReceiver(dispatch, props) { + let crm = cast.receiver.CastReceiverManager.getInstance(); + crm.onReady = function (event) { + crm.setApplicationState("Application ready"); + }; + let bus = crm.getCastMessageBus('urn:x-cast:uk.karakara'); + bus.onMessage = function (event) { + var msg = JSON.parse(event.data); + console.groupCollapsed("cast_onmessage(", event.target ,")") + console.log(msg); + console.groupEnd(); + dispatch(function (state) { + return { + ...state, + room_name: msg.room_name, + room_password: msg.room_password + }; + }); + //bus.send(event.senderId, event.data); + } + crm.start({ statusText: "Application starting" }); + + return function () { + crm.stop(); + } +} + +export function CastReceiver(): Subscription { + return [_castReceiver, {}]; +} \ No newline at end of file diff --git a/player2/src/index.html b/player2/src/index.html index 8176cb31..52b35359 100644 --- a/player2/src/index.html +++ b/player2/src/index.html @@ -9,5 +9,6 @@ + diff --git a/player2/src/player.ts b/player2/src/player.ts index 314c4d16..1443dffb 100644 --- a/player2/src/player.ts +++ b/player2/src/player.ts @@ -16,6 +16,7 @@ import { } from "./subs"; import { ApiRequest } from "./effects"; import { current_and_future } from "./utils"; +import { CastReceiver } from "./cc_receiver"; declare global { namespace JSX { @@ -163,6 +164,7 @@ const subscriptions = (state: State): Array => [ }; }, }), + window.location.search.includes("chromecast=true") && CastReceiver(), ]; app({