Add basis of webstudio selector server UI.
This commit is contained in:
parent
964977bfdf
commit
8c7df0f5ab
8 changed files with 172 additions and 10 deletions
1
.env
1
.env
|
@ -2,4 +2,5 @@ HOST=local-development.ury.org.uk
|
|||
REACT_APP_VERSION=$npm_package_version
|
||||
REACT_APP_MYRADIO_NONAPI_BASE=https://ury.org.uk/myradio-staging
|
||||
REACT_APP_MYRADIO_BASE=https://ury.org.uk/api-staging/v2
|
||||
REACT_APP_BROADCAST_API_BASE=https://ury.org.uk/webstudio/api/v1
|
||||
REACT_APP_WS_URL=wss://audio.ury.org.uk/webstudio/stream
|
|
@ -1,4 +1,5 @@
|
|||
REACT_APP_VERSION=$npm_package_version
|
||||
REACT_APP_MYRADIO_NONAPI_BASE=https://ury.org.uk/myradio
|
||||
REACT_APP_MYRADIO_BASE=https://ury.org.uk/api/v2
|
||||
REACT_APP_BROADCAST_API_BASE=https://ury.org.uk/webstudio/api/v1
|
||||
REACT_APP_WS_URL=wss://audio.ury.org.uk/webstudio/stream
|
|
@ -76,6 +76,7 @@
|
|||
"react-dnd": "^9.4.0",
|
||||
"react-dnd-html5-backend": "^9.4.0",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-live-clock": "^4.0.5",
|
||||
"react-modal": "^3.11.2",
|
||||
"react-redux": "^7.1.3",
|
||||
"reactstrap": "^8.4.1",
|
||||
|
|
14
src/App.css
14
src/App.css
|
@ -21,6 +21,20 @@
|
|||
color: #09d3ac;
|
||||
}
|
||||
|
||||
#timelord {
|
||||
background: black;
|
||||
border:red 1px solid;
|
||||
padding:0;
|
||||
margin:0;
|
||||
color: white;
|
||||
width: 300px;
|
||||
max-width: 40vw;
|
||||
}
|
||||
#timelord .time {
|
||||
font-weight: bold;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.sp-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
|
30
src/api.ts
30
src/api.ts
|
@ -4,19 +4,22 @@ export const MYRADIO_NON_API_BASE =
|
|||
process.env.REACT_APP_MYRADIO_NONAPI_BASE!;
|
||||
export const MYRADIO_BASE_URL =
|
||||
process.env.REACT_APP_MYRADIO_BASE!;
|
||||
export const BROADCAST_API_BASE_URL =
|
||||
process.env.REACT_APP_BROADCAST_API_BASE!;
|
||||
const MYRADIO_API_KEY = process.env.REACT_APP_MYRADIO_KEY!;
|
||||
|
||||
class ApiException extends Error {}
|
||||
|
||||
export async function myradioRequest(
|
||||
export async function apiRequest(
|
||||
url: string,
|
||||
method: "GET" | "POST" | "PUT",
|
||||
params: any
|
||||
params: any,
|
||||
need_auth: boolean = true
|
||||
): Promise<Response> {
|
||||
let req = null;
|
||||
if (method === "GET") {
|
||||
req = fetch(url + qs.stringify(params, { addQueryPrefix: true }), {
|
||||
credentials: "include"
|
||||
credentials: (need_auth ? "include" : "omit")
|
||||
});
|
||||
} else {
|
||||
const body = JSON.stringify(params);
|
||||
|
@ -27,7 +30,7 @@ export async function myradioRequest(
|
|||
headers: {
|
||||
"Content-Type": "application/json; charset=UTF-8"
|
||||
},
|
||||
credentials: "include"
|
||||
credentials: (need_auth ? "include" : "omit")
|
||||
});
|
||||
}
|
||||
return await req;
|
||||
|
@ -38,7 +41,22 @@ export async function myradioApiRequest(
|
|||
method: "GET" | "POST" | "PUT",
|
||||
params: any
|
||||
): Promise<any> {
|
||||
const res = await myradioRequest(MYRADIO_BASE_URL + endpoint, method, params);
|
||||
const res = await apiRequest(MYRADIO_BASE_URL + endpoint, method, params);
|
||||
const json = await res.json();
|
||||
if (json.status === "OK") {
|
||||
return json.payload;
|
||||
} else {
|
||||
console.error(json.payload);
|
||||
throw new ApiException("Request failed!");
|
||||
}
|
||||
}
|
||||
|
||||
export async function broadcastApiRequest(
|
||||
endpoint: string,
|
||||
method: "GET" | "POST" | "PUT",
|
||||
params: any
|
||||
): Promise<any> {
|
||||
const res = await apiRequest(BROADCAST_API_BASE_URL + endpoint, method, params, false);
|
||||
const json = await res.json();
|
||||
if (json.status === "OK") {
|
||||
return json.payload;
|
||||
|
@ -183,7 +201,7 @@ export function getAuxPlaylists(): Promise<Array<ManagedPlaylist>> {
|
|||
}
|
||||
|
||||
export function loadAuxLibrary(libraryId: string): Promise<AuxItem[]> {
|
||||
return myradioRequest(MYRADIO_NON_API_BASE + "/NIPSWeb/load_aux_lib", "GET", {
|
||||
return apiRequest(MYRADIO_NON_API_BASE + "/NIPSWeb/load_aux_lib", "GET", {
|
||||
libraryid: libraryId
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||
import { AppThunk } from "../store";
|
||||
import { myradioApiRequest } from "../api";
|
||||
import { myradioApiRequest, broadcastApiRequest } from "../api";
|
||||
import { WebRTCStreamer } from "./rtc_streamer";
|
||||
import * as MixerState from "../mixer/state";
|
||||
import * as NavbarState from "../navbar/state";
|
||||
|
@ -9,15 +9,31 @@ import { RecordingStreamer } from "./recording_streamer";
|
|||
|
||||
export let streamer: WebRTCStreamer | null = null;
|
||||
|
||||
export type BroadcastStageEnum =
|
||||
| "NOT_REGISTERED"
|
||||
| "REGISTERED"
|
||||
| "FAILED_REGISTRATION";
|
||||
|
||||
|
||||
interface BroadcastState {
|
||||
stage: BroadcastStageEnum;
|
||||
connID: number | null;
|
||||
tracklisting: boolean;
|
||||
connectionState: ConnectionStateEnum;
|
||||
recordingState: ConnectionStateEnum;
|
||||
}
|
||||
|
||||
/* Overall states:
|
||||
hasn't registered
|
||||
registered
|
||||
on air
|
||||
|
||||
*/
|
||||
const broadcastState = createSlice({
|
||||
name: "Broadcast",
|
||||
initialState: {
|
||||
stage: "NOT_REGISTERED",
|
||||
connID: null,
|
||||
tracklisting: false,
|
||||
connectionState: "NOT_CONNECTED",
|
||||
recordingState: "NOT_CONNECTED"
|
||||
|
@ -26,6 +42,14 @@ const broadcastState = createSlice({
|
|||
toggleTracklisting(state) {
|
||||
state.tracklisting = !state.tracklisting;
|
||||
},
|
||||
setConnID(state, action: PayloadAction<number | null>) {
|
||||
state.connID = action.payload;
|
||||
if (action.payload != null) {
|
||||
state.stage = "REGISTERED"
|
||||
} else {
|
||||
state.stage = "NOT_REGISTERED"
|
||||
}
|
||||
},
|
||||
setConnectionState(state, action: PayloadAction<ConnectionStateEnum>) {
|
||||
state.connectionState = action.payload;
|
||||
},
|
||||
|
@ -41,6 +65,60 @@ export interface TrackListItem {
|
|||
audiologid: number;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export const registerTimeslot = (): AppThunk => async (dispatch, getState) => {
|
||||
if (getState().broadcast.stage === "NOT_REGISTERED") {
|
||||
var state = getState().session;
|
||||
const memberid = state.currentUser?.memberid;
|
||||
const timeslotid = state.currentTimeslot?.timeslot_id;
|
||||
console.log("Attempting to Register for Broadcast.");
|
||||
var sourceid = 4; // TODO: make UI for this.
|
||||
var connID = (await sendBroadcastRegister(timeslotid, memberid, sourceid));
|
||||
if (connID !== undefined) {
|
||||
dispatch(broadcastState.actions.setConnID(connID["connid"]));
|
||||
dispatch(startStreaming());
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
export const cancelTimeslot = (): AppThunk => async (dispatch, getState) => {
|
||||
if (getState().broadcast.stage === "REGISTERED") {
|
||||
console.log("Attempting to Cancel Broadcast.");
|
||||
var response = (await sendBroadcastCancel(getState().broadcast.connID));
|
||||
dispatch(stopStreaming());
|
||||
if (response != null) {
|
||||
dispatch(broadcastState.actions.setConnID(null));
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
export function sendBroadcastRegister(timeslotid: number | undefined, memberid: number | undefined, sourceid: number): Promise<any> {
|
||||
return broadcastApiRequest("/registerTimeslot", "POST", {
|
||||
memberid: memberid,
|
||||
timeslotid: timeslotid,
|
||||
sourceid: sourceid
|
||||
});
|
||||
}
|
||||
export function sendBroadcastCancel(connid: number | null): Promise<string | null> {
|
||||
return broadcastApiRequest("/cancelTimeslot", "POST", {
|
||||
connid: connid
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export const { toggleTracklisting } = broadcastState.actions;
|
||||
|
||||
export const tracklistStart = (
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import React, { useRef, useEffect } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import Clock from 'react-live-clock';
|
||||
|
||||
import { RootState } from "../rootReducer";
|
||||
|
||||
import * as BroadcastState from "../broadcast/state";
|
||||
|
@ -16,7 +18,7 @@ export function NavBar() {
|
|||
const redirect_url = encodeURIComponent(window.location.toString());
|
||||
return (
|
||||
<>
|
||||
<div className="navbar-nav">
|
||||
<div className="navbar-nav navbar-">
|
||||
<a className="navbar-brand" href="/">
|
||||
<img
|
||||
src="//ury.org.uk/myradio/img/URY.svg"
|
||||
|
@ -28,6 +30,26 @@ export function NavBar() {
|
|||
<a className="navbar-brand" href="/">
|
||||
<img src={appLogo} height="28" alt="Web Studio Logo" />
|
||||
</a>
|
||||
<div id="timelord" onClick={() => {
|
||||
switch (broadcastState.stage) {
|
||||
case "NOT_REGISTERED":
|
||||
dispatch(
|
||||
BroadcastState.registerTimeslot()
|
||||
)
|
||||
break;
|
||||
case "REGISTERED":
|
||||
dispatch(
|
||||
BroadcastState.cancelTimeslot()
|
||||
)
|
||||
break;
|
||||
}
|
||||
}}>
|
||||
<div className="time"><Clock format={'HH:mm:ss'} ticking={true} timezone={'EU/London'} /></div>
|
||||
<div className="message">
|
||||
{broadcastState.stage === "NOT_REGISTERED" && "Register for show"}
|
||||
{broadcastState.stage === "REGISTERED" && "Cancel show"}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul className="nav navbar-nav navbar-right">
|
||||
|
@ -193,7 +215,7 @@ export function CombinedNavAlertBar() {
|
|||
<>
|
||||
<AlertBar />
|
||||
<header className="navbar navbar-ury navbar-expand-md p-0 bd-navbar">
|
||||
<nav className="container">
|
||||
<nav className="container-fluid">
|
||||
<button
|
||||
className="navbar-toggler"
|
||||
type="button"
|
||||
|
|
29
yarn.lock
29
yarn.lock
|
@ -6951,6 +6951,18 @@ mkdirp@^0.5.1, mkdirp@~0.5.1:
|
|||
dependencies:
|
||||
minimist "^1.2.5"
|
||||
|
||||
moment-timezone@0.5.27:
|
||||
version "0.5.27"
|
||||
resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.27.tgz#73adec8139b6fe30452e78f210f27b1f346b8877"
|
||||
integrity sha512-EIKQs7h5sAsjhPCqN6ggx6cEbs94GK050254TIJySD1bzoM5JTYDwAU1IoVOeTOL6Gm27kYJ51/uuvq1kIlrbw==
|
||||
dependencies:
|
||||
moment ">= 2.9.0"
|
||||
|
||||
moment@2.24.0, "moment@>= 2.9.0":
|
||||
version "2.24.0"
|
||||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b"
|
||||
integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==
|
||||
|
||||
move-concurrently@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"
|
||||
|
@ -8563,7 +8575,7 @@ prompts@^2.0.1:
|
|||
kleur "^3.0.3"
|
||||
sisteransi "^1.0.4"
|
||||
|
||||
prop-types@^15.5.10, prop-types@^15.5.8, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2:
|
||||
prop-types@15.7.2, prop-types@^15.5.10, prop-types@^15.5.8, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2:
|
||||
version "15.7.2"
|
||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
|
||||
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
|
||||
|
@ -8851,6 +8863,16 @@ react-lifecycles-compat@^3.0.0, react-lifecycles-compat@^3.0.4:
|
|||
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
|
||||
integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
|
||||
|
||||
react-live-clock@^4.0.5:
|
||||
version "4.0.5"
|
||||
resolved "https://registry.yarnpkg.com/react-live-clock/-/react-live-clock-4.0.5.tgz#d53b16be62be7c4fd9b8183db31f58bffa1c9142"
|
||||
integrity sha512-NsXuUAGrUPAnJJc4RWVNE63hbb9gZNO0gN0bMbI/fMT5Iq8Oc/KmnD1yV6W/7EzhlU4jmErng/tVWmp0pDxYHw==
|
||||
dependencies:
|
||||
moment "2.24.0"
|
||||
moment-timezone "0.5.27"
|
||||
prop-types "15.7.2"
|
||||
react-moment "0.9.7"
|
||||
|
||||
react-modal@^3.11.2:
|
||||
version "3.11.2"
|
||||
resolved "https://registry.yarnpkg.com/react-modal/-/react-modal-3.11.2.tgz#bad911976d4add31aa30dba8a41d11e21c4ac8a4"
|
||||
|
@ -8861,6 +8883,11 @@ react-modal@^3.11.2:
|
|||
react-lifecycles-compat "^3.0.0"
|
||||
warning "^4.0.3"
|
||||
|
||||
react-moment@0.9.7:
|
||||
version "0.9.7"
|
||||
resolved "https://registry.yarnpkg.com/react-moment/-/react-moment-0.9.7.tgz#ca570466595b1aa4f7619e62da18b3bb2de8b6f3"
|
||||
integrity sha512-ifzUrUGF6KRsUN2pRG5k56kO0mJBr8kRkWb0wNvtFIsBIxOuPxhUpL1YlXwpbQCbHq23hUu6A0VEk64HsFxk9g==
|
||||
|
||||
react-popper@^1.3.6:
|
||||
version "1.3.7"
|
||||
resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-1.3.7.tgz#f6a3471362ef1f0d10a4963673789de1baca2324"
|
||||
|
|
Loading…
Reference in a new issue