Merge fixes / tidies from BAPS3 to Webstudio

This commit is contained in:
Matthew Stratford 2021-06-13 16:20:51 +01:00
parent 1e53edea2c
commit fede677bb5
13 changed files with 128 additions and 109 deletions

1
.env
View file

@ -4,5 +4,4 @@ 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://ury.org.uk/webstudio/api/stream
REACT_APP_BAPSICLE_INTERFACE=true
REACT_APP_SENTRY_PUBLIC_DSN=https://7bdc2a7a1eb24ae080eb1d3af75e6307@o578586.ingest.sentry.io/5734903

View file

@ -38,6 +38,8 @@
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
TODO: Ideally we shouldn't rely on CDN's during runtime.
-->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">

View file

@ -17,7 +17,7 @@ $number-of-channels: 3;
.logo {
height: 50px;
}
.logo-big {
.logo-big-bapsicle {
height: 30vh;
}
}

View file

@ -205,7 +205,7 @@ export function searchForTracks(
artist,
title,
});
} else {
}
return myradioApiRequest("/track/search", "GET", {
artist,
title,
@ -213,7 +213,6 @@ export function searchForTracks(
digitised: true,
});
}
}
export function getTimeslots(): Promise<Array<Timeslot>> {
return bapsicleApiRequest("/plan/list", "GET", {});

View file

@ -12,7 +12,7 @@ export function ConnectionDialogue({ error }: { error: String | null }) {
return (
<div className="loading-dialogue">
<div className="logo-container">
<img className="logo-big mb-2" src={appLogo} alt="BAPS Logo" />
<img className="logo-big-bapsicle mb-2" src={appLogo} alt="BAPS Logo" />
</div>
<span className="inner">

View file

@ -4,7 +4,6 @@ import "./index.css";
import App from "./App";
import * as serviceWorker from "./serviceWorkerLoader";
import * as Sentry from "@sentry/react";
import { Integrations } from "@sentry/tracing";

View file

@ -289,6 +289,8 @@ class Player extends ((PlayerEmitter as unknown) as { new (): EventEmitter }) {
instance.emit("pause");
});
wavesurfer.on("seek", () => {
// This is used to prevent infinite loops when bapsicle tells wavesurfer to change position,
// since otherwise it would send an change update back to the bapsicle server again (as if the user seeked intentionally).
if (instance.ignore_next_seek) {
instance.ignore_next_seek = false;
} else {

View file

@ -379,6 +379,7 @@ export const setLoadedItemOutro = (
};
export const seek = (player: number, time_s: number): AppThunk => async () => {
// Used only by Bapsicle to update the wavesurfer seek position.
const playerInstance = await audioEngine.getPlayer(player);
if (playerInstance) {
@ -426,6 +427,7 @@ export const load = (
const currentItem = getState().mixer.players[player].loadedItem;
if (currentItem !== null && itemId(currentItem) === itemId(item)) {
if (process.env.REACT_APP_BAPSICLE_INTERFACE) {
// In BAPS, another client could have updated the audio markers etc, so we have to still check and update them.
// The cue/intro/outro point(s) have changed.
if (
"cue" in currentItem &&
@ -567,6 +569,7 @@ export const load = (
dispatch(mixerState.actions.itemLoadComplete({ player }));
if (!process.env.REACT_APP_BAPSICLE_INTERFACE) {
// BAPS server will give these values already on load based on it's own calculation, not wavesurfer's.
dispatch(
mixerState.actions.setTimeLength({
player,
@ -585,6 +588,7 @@ export const load = (
const state = getState().mixer.players[player];
if (!process.env.REACT_APP_BAPSICLE_INTERFACE) {
// Client doesn't do any audio playing in BAPS.
if (state.playOnLoad) {
playerInstance.play();
}
@ -603,16 +607,18 @@ export const load = (
});
if (process.env.REACT_APP_BAPSICLE_INTERFACE) {
// If user manually seeks on the waveform, just direct that to the webserver.
playerInstance.on("timeChangeSeek", (time) => {
if (
Math.abs(time - getState().mixer.players[player].timeCurrent) > 0.5
) {
// Limit
//if (
// Math.abs(time - getState().mixer.players[player].timeCurrent) > 0.5
//) {
sendBAPSicleChannel({
channel: player,
command: "SEEK",
time: time,
});
}
//}
});
} else {
playerInstance.on("play", () => {
@ -1261,10 +1267,40 @@ export const mixerKeyboardShortcutsMiddleware: Middleware<
store.dispatch(handleKeyboardShortcut(store, 2, "STOP"));
});
if (!process.env.REACT_APP_BAPSICLE_INTERFACE) {
Keys("a", () => {
store.dispatch(setVolume(0, "off"));
});
Keys("s", () => {
store.dispatch(setVolume(0, "bed"));
});
Keys("d", () => {
store.dispatch(setVolume(0, "full"));
});
Keys("f", () => {
store.dispatch(setVolume(1, "off"));
});
Keys("g", () => {
store.dispatch(setVolume(1, "bed"));
});
Keys("h", () => {
store.dispatch(setVolume(1, "full"));
});
Keys("j", () => {
store.dispatch(setVolume(2, "off"));
});
Keys("k", () => {
store.dispatch(setVolume(2, "bed"));
});
Keys("l", () => {
store.dispatch(setVolume(2, "full"));
});
Keys("x", () => {
const state = store.getState().mixer.mic;
store.dispatch(setMicVolume(state.volume === 1 ? "off" : "full"));
});
}
return (next) => (action) => next(action);
};

View file

@ -50,8 +50,7 @@ export const Item = memo(function Item({
function triggerClick() {
if (column > -1) {
console.log("Clicking to load:", x);
// TODO: move this into mixer state if we can.
if (process.env.REACT_APP_BAPSICLE_INTERFACE) {
sendBAPSicleChannel({
channel: column,

View file

@ -81,6 +81,7 @@ const setTrackIntro = (
secs: number,
player: number
): AppThunk => async (dispatch, getState) => {
// TODO Move into MixerState
if (process.env.REACT_APP_BAPSICLE_INTERFACE) {
let marker = {
name: "Intro",

View file

@ -50,8 +50,6 @@ import Modal from "react-modal";
import { Sidebar } from "./sidebar";
import { PLAYER_ID_PREVIEW } from "../mixer/audio";
import { sendBAPSicleChannel } from "../bapsicle";
function Channel({ id, data }: { id: number; data: PlanItem[] }) {
return (
<div className="channel" id={"channel-" + id}>
@ -112,6 +110,7 @@ const Showplanner: React.FC<{ timeslotId: number }> = function({ timeslotId }) {
useEffect(() => {
if (!process.env.REACT_APP_BAPSICLE_INTERFACE) {
// In BAPS, we'll load in the show plan from a async message from server.
dispatch(getShowplan(timeslotId));
}
}, [dispatch, timeslotId]);
@ -148,15 +147,7 @@ const Showplanner: React.FC<{ timeslotId: number }> = function({ timeslotId }) {
cue: 0,
...data,
};
if (process.env.REACT_APP_BAPSICLE_INTERFACE) {
sendBAPSicleChannel({
channel: newItem.channel,
command: "ADD",
newItem: newItem,
});
} else {
dispatch(addItem(timeslotId, newItem));
}
increment(null);
} else if (result.draggableId[0] === "A") {
// this is an aux resource
@ -171,15 +162,7 @@ const Showplanner: React.FC<{ timeslotId: number }> = function({ timeslotId }) {
cue: 0,
...data,
} as any;
if (process.env.REACT_APP_BAPSICLE_INTERFACE) {
sendBAPSicleChannel({
channel: newItem.channel,
command: "ADD",
newItem: newItem,
});
} else {
dispatch(addItem(timeslotId, newItem));
}
increment(null);
} else {
// this is a normal move (ghosts aren't draggable)
@ -204,28 +187,6 @@ const Showplanner: React.FC<{ timeslotId: number }> = function({ timeslotId }) {
}
}
async function onCtxRemoveClick(
e: any,
data: { id: string; column: number; index: number }
) {
if (process.env.REACT_APP_BAPSICLE_INTERFACE) {
sendBAPSicleChannel({
channel: data.column,
command: "REMOVE",
weight: data.index,
});
} else {
dispatch(removeItem(timeslotId, data.id));
}
}
async function onCtxUnPlayedClick(
e: any,
data: { id: string; column: number; index: number }
) {
dispatch(setItemPlayed(data.id.toString(), false, data.column));
}
// Add support for reloading the show plan from the iFrames.
// There is a similar listener in showplanner/ImporterModal.tsx to handle closing the iframe.
useEffect(() => {
@ -275,9 +236,9 @@ const Showplanner: React.FC<{ timeslotId: number }> = function({ timeslotId }) {
<FaTrash /> Remove
</CtxMenuItem>
<CtxMenuItem
onClick={(args) =>
dispatch(setItemPlayed((args.props as any).id, false))
}
onClick={(args) => {
dispatch(setItemPlayed((args.props as any).id, false));
}}
>
<FaCircleNotch /> Mark Unplayed
</CtxMenuItem>

View file

@ -99,6 +99,8 @@ const showplan = createSlice({
state,
action: PayloadAction<{ channel: Number; planItems: PlanItem[] }>
) {
// This is used for BAPSicle only to read in individual channels of show plan into the show state from the server.
// TODO: Does this need to be this complicated?
var newItems = state.plan?.filter(
(item) => item.channel !== action.payload.channel
);
@ -263,11 +265,7 @@ export const setItemPlayed = (
played: boolean
): AppThunk => async (dispatch, getState) => {
// The server handles marking things played
// TODO: Add support in BAPSicle for marking things played
if (process.env.REACT_APP_BAPSICLE_INTERFACE) {
if (played) {
return;
}
var weight = -1;
var player = null;
@ -282,13 +280,15 @@ export const setItemPlayed = (
if (player) {
sendBAPSicleChannel({
channel: player,
command: "RESETPLAYED",
command: "SETPLAYED",
weight: weight,
played: played,
});
} else {
sendBAPSicleChannel({
command: "RESETPLAYED",
command: "SETPLAYED",
weight: weight,
played: played,
});
}
return;
@ -448,6 +448,15 @@ export const addItem = (
timeslotId: number,
newItemData: TimeslotItem
): AppThunk => async (dispatch, getState) => {
if (process.env.REACT_APP_BAPSICLE_INTERFACE) {
sendBAPSicleChannel({
channel: newItemData.channel,
command: "ADD",
newItem: newItemData,
});
return;
}
dispatch(showplan.actions.setPlanSaving(true));
console.log("New Weight: " + newItemData.weight);
const plan = cloneDeep(getState().showplan.plan!);
@ -527,7 +536,9 @@ export const addItem = (
},
}
);
dispatch(showplan.actions.planSaveError("Failed to update show plan."));
return;
}
const lastResult = result[result.length - 1]; // this is the add op
const newItemId = lastResult.timeslotitemid!;
@ -538,7 +549,6 @@ export const addItem = (
newItemData,
})
);
}
} else {
// Just add it straight to the show plan without updating the server.
dispatch(showplan.actions.addItem(newItemData));
@ -551,10 +561,24 @@ export const removeItem = (
timeslotId: number,
itemid: string
): AppThunk => async (dispatch, getState) => {
dispatch(showplan.actions.setPlanSaving(true));
// This is a simplified version of the second case of moveItem
const plan = cloneDeep(getState().showplan.plan!);
const item = plan.find((x) => itemId(x) === itemid)!;
if (process.env.REACT_APP_BAPSICLE_INTERFACE) {
// Server handles deletion, short circuit.
if (item) {
sendBAPSicleChannel({
channel: item.channel,
command: "REMOVE",
weight: item.weight,
});
}
return;
}
dispatch(showplan.actions.setPlanSaving(true));
const planColumn = plan
.filter((x) => x.channel === item.channel)
.sort((a, b) => a.weight - b.weight);
@ -578,6 +602,7 @@ export const removeItem = (
weight: movingItem.weight - 1,
});
movingItem.weight -= 1;
}
if (!process.env.REACT_APP_BAPSICLE_INTERFACE) {
if (getState().settings.saveShowPlanChanges) {
@ -594,14 +619,11 @@ export const removeItem = (
},
}
);
dispatch(
showplan.actions.planSaveError("Failed to update show plan.")
);
dispatch(showplan.actions.planSaveError("Failed to update show plan."));
return;
}
}
}
}
dispatch(showplan.actions.applyOps(ops));
dispatch(showplan.actions.setPlanSaving(false));

View file

@ -20,7 +20,6 @@ import { bapsicleMiddleware } from "./bapsicle";
import * as Sentry from "@sentry/react";
// const ACTION_HISTORY_MAX_SIZE = 20;
// const actionHistory: Array<Action> = [];