After more merge fixing, still no channel lists
This commit is contained in:
parent
ae189d9613
commit
000d85d450
11 changed files with 359 additions and 359 deletions
|
@ -5,7 +5,8 @@
|
|||
"dependencies": {
|
||||
"@babel/core": "7.6.0",
|
||||
"@reduxjs/toolkit": "^1.0.4",
|
||||
"@sentry/react": "^6.5.0",
|
||||
"@sentry/react": "^6.3.1",
|
||||
"@sentry/tracing": "^6.3.1",
|
||||
"@svgr/webpack": "4.3.2",
|
||||
"@types/dom-mediacapture-record": "^1.0.4",
|
||||
"@types/jest": "24.0.22",
|
||||
|
|
|
@ -68,7 +68,7 @@ class Player extends ((PlayerEmitter as unknown) as { new (): EventEmitter }) {
|
|||
}
|
||||
|
||||
stop() {
|
||||
if (process.env.REACT_APP_BAPSICLE_INTERFACE) {
|
||||
if (!process.env.REACT_APP_BAPSICLE_INTERFACE) {
|
||||
return this.wavesurfer.stop();
|
||||
}
|
||||
}
|
||||
|
@ -241,7 +241,7 @@ class Player extends ((PlayerEmitter as unknown) as { new (): EventEmitter }) {
|
|||
if (!process.env.REACT_APP_BAPSICLE_INTERFACE) {
|
||||
customOutput = outputId !== INTERNAL_OUTPUT_ID;
|
||||
}
|
||||
|
||||
|
||||
let waveform = document.getElementById("waveform-" + player.toString());
|
||||
if (waveform == null) {
|
||||
throw new Error();
|
||||
|
|
|
@ -32,15 +32,6 @@ import { sendBAPSicleChannel } from "../bapsicle";
|
|||
import { changeSetting } from "../optionsMenu/settingsState";
|
||||
import { PLAYER_COUNTER_UPDATE_PERIOD_MS } from "../showplanner/Player";
|
||||
|
||||
import { changeSetting } from "../optionsMenu/settingsState";
|
||||
import {
|
||||
DEFAULT_TRIM_DB,
|
||||
OFF_LEVEL_DB,
|
||||
BED_LEVEL_DB,
|
||||
FULL_LEVEL_DB,
|
||||
} from "./audio";
|
||||
import { PLAYER_COUNTER_UPDATE_PERIOD_MS } from "../showplanner/Player";
|
||||
|
||||
const playerGainTweens: Array<{
|
||||
target: VolumePresetEnum;
|
||||
tweens: Between[];
|
||||
|
@ -609,105 +600,107 @@ export const load = (
|
|||
if (state.loadedItem && "outro" in state.loadedItem) {
|
||||
playerInstance.setOutro(state.loadedItem.outro);
|
||||
}
|
||||
});
|
||||
|
||||
if (process.env.REACT_APP_BAPSICLE_INTERFACE) {
|
||||
playerInstance.on("timeChangeSeek", (time) => {
|
||||
if (
|
||||
Math.abs(time - getState().mixer.players[player].timeCurrent) > 0.5
|
||||
) {
|
||||
sendBAPSicleChannel({
|
||||
channel: player,
|
||||
command: "SEEK",
|
||||
time: time,
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
playerInstance.on("play", () => {
|
||||
dispatch(
|
||||
mixerState.actions.setPlayerState({ player, state: "playing" })
|
||||
);
|
||||
|
||||
const state = getState().mixer.players[player];
|
||||
// Don't set played on Preview Channel
|
||||
if (state.loadedItem != null && player !== PLAYER_ID_PREVIEW) {
|
||||
if (process.env.REACT_APP_BAPSICLE_INTERFACE) {
|
||||
playerInstance.on("timeChangeSeek", (time) => {
|
||||
if (
|
||||
Math.abs(time - getState().mixer.players[player].timeCurrent) > 0.5
|
||||
) {
|
||||
sendBAPSicleChannel({
|
||||
channel: player,
|
||||
command: "SEEK",
|
||||
time: time,
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
playerInstance.on("play", () => {
|
||||
dispatch(
|
||||
dispatch(setItemPlayed(itemId(state.loadedItem), true));
|
||||
mixerState.actions.setPlayerState({ player, state: "playing" })
|
||||
);
|
||||
}
|
||||
});
|
||||
playerInstance.on("pause", () => {
|
||||
dispatch(
|
||||
mixerState.actions.setPlayerState({
|
||||
player,
|
||||
state: playerInstance.currentTime === 0 ? "stopped" : "paused",
|
||||
})
|
||||
);
|
||||
});
|
||||
playerInstance.on("timeChange", (time) => {
|
||||
if (
|
||||
Math.abs(time - getState().mixer.players[player].timeCurrent) >
|
||||
PLAYER_COUNTER_UPDATE_PERIOD_MS / 1000
|
||||
) {
|
||||
|
||||
const state = getState().mixer.players[player];
|
||||
// Don't set played on Preview Channel
|
||||
if (state.loadedItem != null && player !== PLAYER_ID_PREVIEW) {
|
||||
dispatch(setItemPlayed(itemId(state.loadedItem), true));
|
||||
}
|
||||
});
|
||||
playerInstance.on("pause", () => {
|
||||
dispatch(
|
||||
mixerState.actions.setTimeCurrent({
|
||||
mixerState.actions.setPlayerState({
|
||||
player,
|
||||
time,
|
||||
state: playerInstance.currentTime === 0 ? "stopped" : "paused",
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
playerInstance.on("finish", () => {
|
||||
// If the Preview Player finishes playing, turn off PFL in the UI.
|
||||
if (player === PLAYER_ID_PREVIEW) {
|
||||
dispatch(setChannelPFL(player, false));
|
||||
}
|
||||
dispatch(mixerState.actions.setPlayerState({ player, state: "stopped" }));
|
||||
const state = getState().mixer.players[player];
|
||||
if (state.tracklistItemID !== -1) {
|
||||
dispatch(BroadcastState.tracklistEnd(state.tracklistItemID));
|
||||
}
|
||||
if (state.repeat === "one") {
|
||||
playerInstance.play();
|
||||
} else if (state.repeat === "all") {
|
||||
if ("channel" in item) {
|
||||
// it's not in the CML/libraries "column"
|
||||
const itsChannel = getState()
|
||||
.showplan.plan!.filter((x) => x.channel === item.channel)
|
||||
.sort((x, y) => x.weight - y.weight);
|
||||
const itsIndex = itsChannel.indexOf(item);
|
||||
if (itsIndex === itsChannel.length - 1) {
|
||||
dispatch(load(player, itsChannel[0]));
|
||||
} else if (state.autoAdvance) {
|
||||
if ("channel" in item) {
|
||||
// it's not in the CML/libraries "column"
|
||||
const itsChannel = getState()
|
||||
.showplan.plan!.filter((x) => x.channel === item.channel)
|
||||
.sort((x, y) => x.weight - y.weight);
|
||||
// Sadly, we can't just do .indexOf() item directly,
|
||||
// since the player's idea of an item may be changed over it's lifecycle (setting played,intro/cue/outro etc.)
|
||||
// Therefore we'll find the updated item from the plan and match that.
|
||||
const itsIndex = itsChannel.findIndex(
|
||||
(x) => itemId(x) === itemId(item)
|
||||
);
|
||||
if (itsIndex > -1 && itsIndex !== itsChannel.length - 1) {
|
||||
dispatch(load(player, itsChannel[itsIndex + 1]));
|
||||
}
|
||||
});
|
||||
playerInstance.on("timeChange", (time) => {
|
||||
if (
|
||||
Math.abs(time - getState().mixer.players[player].timeCurrent) >
|
||||
PLAYER_COUNTER_UPDATE_PERIOD_MS / 1000
|
||||
) {
|
||||
dispatch(
|
||||
mixerState.actions.setTimeCurrent({
|
||||
player,
|
||||
time,
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
playerInstance.on("finish", () => {
|
||||
// If the Preview Player finishes playing, turn off PFL in the UI.
|
||||
if (player === PLAYER_ID_PREVIEW) {
|
||||
dispatch(setChannelPFL(player, false));
|
||||
}
|
||||
dispatch(
|
||||
mixerState.actions.setPlayerState({ player, state: "stopped" })
|
||||
);
|
||||
const state = getState().mixer.players[player];
|
||||
if (state.tracklistItemID !== -1) {
|
||||
dispatch(BroadcastState.tracklistEnd(state.tracklistItemID));
|
||||
}
|
||||
if (state.repeat === "one") {
|
||||
playerInstance.play();
|
||||
} else if (state.repeat === "all") {
|
||||
if ("channel" in item) {
|
||||
// it's not in the CML/libraries "column"
|
||||
const itsChannel = getState()
|
||||
.showplan.plan!.filter((x) => x.channel === item.channel)
|
||||
.sort((x, y) => x.weight - y.weight);
|
||||
const itsIndex = itsChannel.indexOf(item);
|
||||
if (itsIndex === itsChannel.length - 1) {
|
||||
dispatch(load(player, itsChannel[0]));
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (state.autoAdvance) {
|
||||
if ("channel" in item) {
|
||||
// it's not in the CML/libraries "column"
|
||||
const itsChannel = getState()
|
||||
.showplan.plan!.filter((x) => x.channel === item.channel)
|
||||
.sort((x, y) => x.weight - y.weight);
|
||||
// Sadly, we can't just do .indexOf() item directly,
|
||||
// since the player's idea of an item may be changed over it's lifecycle (setting played,intro/cue/outro etc.)
|
||||
// Therefore we'll find the updated item from the plan and match that.
|
||||
const itsIndex = itsChannel.findIndex(
|
||||
(x) => itemId(x) === itemId(item)
|
||||
);
|
||||
if (itsIndex > -1 && itsIndex !== itsChannel.length - 1) {
|
||||
dispatch(load(player, itsChannel[itsIndex + 1]));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Double-check we haven't been aborted since
|
||||
if (signal.aborted) {
|
||||
// noinspection ExceptionCaughtLocallyJS
|
||||
throw new DOMException("abort load", "AbortError");
|
||||
}
|
||||
});
|
||||
|
||||
// Double-check we haven't been aborted since
|
||||
if (signal.aborted) {
|
||||
// noinspection ExceptionCaughtLocallyJS
|
||||
throw new DOMException("abort load", "AbortError");
|
||||
playerInstance.setVolume(getState().mixer.players[player].gain);
|
||||
playerInstance.setTrim(getState().mixer.players[player].trim);
|
||||
delete loadAbortControllers[player];
|
||||
}
|
||||
|
||||
playerInstance.setVolume(getState().mixer.players[player].gain);
|
||||
playerInstance.setTrim(getState().mixer.players[player].trim);
|
||||
delete loadAbortControllers[player];
|
||||
} catch (e) {
|
||||
if ("name" in e && e.name === "AbortError") {
|
||||
// load was aborted, ignore the error
|
||||
|
@ -866,9 +859,6 @@ export const {
|
|||
setRepeat,
|
||||
setTracklistItemID,
|
||||
setMicBaseGain,
|
||||
toggleAutoAdvance,
|
||||
togglePlayOnLoad,
|
||||
toggleRepeat,
|
||||
setPlayerMicAutoDuck,
|
||||
} = mixerState.actions;
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
FaCompactDisc,
|
||||
FaHeadphonesAlt,
|
||||
} from "react-icons/fa";
|
||||
import LiveClock from "react-live-clock";
|
||||
|
||||
import { RootState } from "../rootReducer";
|
||||
|
||||
|
@ -19,7 +20,7 @@ import * as BroadcastState from "../broadcast/state";
|
|||
import appLogo from "../assets/images/webstudio.svg";
|
||||
import myradioLogo from "../assets/images/myradio.svg";
|
||||
import { MYRADIO_NON_API_BASE } from "../api";
|
||||
import logo from "../assets/images/navbarlogo.png";
|
||||
|
||||
import "./navbar.scss";
|
||||
import { closeAlert } from "./state";
|
||||
import { BAPSicleModal } from "./BAPSicleModal";
|
||||
|
@ -165,6 +166,33 @@ export function NavBarMyRadio() {
|
|||
}
|
||||
|
||||
export function NavBarMain() {
|
||||
const [showBAPSicleModal, setShowBAPSicleModal] = useState(true);
|
||||
if (process.env.REACT_APP_BAPSICLE_INTERFACE) {
|
||||
return (
|
||||
<>
|
||||
<ul className="nav navbar-nav navbar-left">
|
||||
<Timelord />
|
||||
</ul>
|
||||
|
||||
<ul className="nav navbar-nav navbar-right mr-0 pr-0">
|
||||
<li
|
||||
className="btn btn-outline-light rounded-0 pt-2 pb-2 nav-item nav-link"
|
||||
style={{ color: "white" }}
|
||||
onClick={() => {
|
||||
setShowBAPSicleModal(true);
|
||||
}}
|
||||
>
|
||||
<FaCompactDisc size={16} className="mr-2" />
|
||||
<b>Menu</b>
|
||||
</li>
|
||||
</ul>
|
||||
<BAPSicleModal
|
||||
close={() => setShowBAPSicleModal(false)}
|
||||
isOpen={showBAPSicleModal}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<ul className="nav navbar-nav navbar-left">
|
||||
|
@ -217,56 +245,6 @@ function RegisterButton() {
|
|||
(state: RootState) => state.showplan
|
||||
);
|
||||
|
||||
const [showBAPSicleModal, setShowBAPSicleModal] = useState(true);
|
||||
|
||||
if (process.env.REACT_APP_BAPSICLE_INTERFACE) {
|
||||
return (
|
||||
<>
|
||||
<ul className="nav navbar-nav navbar-left">
|
||||
<li
|
||||
className="btn rounded-0 py-1 nav-link nav-item"
|
||||
id="timelord"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
window.open(
|
||||
"http://ury.org.uk/timelord/",
|
||||
"URY - Timelord",
|
||||
"resizable,status"
|
||||
);
|
||||
}}
|
||||
>
|
||||
<img src={logo} className="mr-2" height={32} alt="Logo" />
|
||||
<Clock
|
||||
format={"HH:mm:ss"}
|
||||
ticking={true}
|
||||
timezone={"europe/london"}
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div>
|
||||
<ul className="nav navbar-nav navbar-right mr-0 pr-0">
|
||||
<li
|
||||
className="btn btn-outline-light rounded-0 pt-2 pb-2 nav-item nav-link"
|
||||
style={{ color: "white" }}
|
||||
onClick={() => {
|
||||
setShowBAPSicleModal(true);
|
||||
}}
|
||||
>
|
||||
<FaCompactDisc size={16} className="mr-2" />
|
||||
<b>Menu</b>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<BAPSicleModal
|
||||
close={() => setShowBAPSicleModal(false)}
|
||||
isOpen={showBAPSicleModal}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
// Full WebStudio NavBar
|
||||
return (
|
||||
<>
|
||||
<li className="nav-item" style={{ color: "white" }}>
|
||||
|
|
|
@ -5,6 +5,7 @@ import { myradioApiRequest } from "../api";
|
|||
import { useInterval } from "../lib/utils";
|
||||
import { RootState } from "../rootReducer";
|
||||
import "./timelord.scss";
|
||||
import logo from "../assets/images/navbarlogo.png";
|
||||
|
||||
const SILENCE_WARN_SECS = 5;
|
||||
|
||||
|
@ -72,6 +73,10 @@ export function Timelord() {
|
|||
);
|
||||
}}
|
||||
>
|
||||
{process.env.REACT_APP_BAPSICLE_INTERFACE && (
|
||||
<img src={logo} className="mr-2" height={32} alt="Logo" />
|
||||
)}
|
||||
|
||||
<LiveClock
|
||||
format={"HH:mm:ss"}
|
||||
ticking={true}
|
||||
|
|
|
@ -101,7 +101,9 @@ const setTrackIntro = (
|
|||
secs = Math.round(secs);
|
||||
dispatch(MixerState.setLoadedItemIntro(player, secs));
|
||||
if (getState().settings.saveShowPlanChanges) {
|
||||
await api.setTrackIntro(item.trackid, secs);
|
||||
if ("trackid" in item) {
|
||||
await api.setTrackIntro(item.trackid, secs);
|
||||
}
|
||||
}
|
||||
dispatch(ShowPlanState.setItemTimings({ item: item, intro: secs }));
|
||||
} catch (e) {
|
||||
|
@ -135,7 +137,9 @@ const setTrackOutro = (
|
|||
secs = Math.round(secs);
|
||||
dispatch(MixerState.setLoadedItemOutro(player, secs));
|
||||
if (getState().settings.saveShowPlanChanges) {
|
||||
await api.setTrackOutro(item.trackid, secs);
|
||||
if ("trackid" in item) {
|
||||
await api.setTrackOutro(item.trackid, secs);
|
||||
}
|
||||
}
|
||||
dispatch(ShowPlanState.setItemTimings({ item: item, outro: secs }));
|
||||
} catch (e) {
|
||||
|
@ -391,9 +395,7 @@ export function Player({
|
|||
: "btn-outline-secondary") +
|
||||
" btn btn-sm col-4 sp-play-on-load"
|
||||
}
|
||||
onClick={() =>
|
||||
dispatch(MixerState.toggleAutoAdvance({ player: id }))
|
||||
}
|
||||
onClick={() => dispatch(MixerState.toggleAutoAdvance(id))}
|
||||
>
|
||||
<FaLevelDownAlt />
|
||||
Auto Advance
|
||||
|
@ -405,9 +407,7 @@ export function Player({
|
|||
: "btn-outline-secondary") +
|
||||
" btn btn-sm col-4 sp-play-on-load"
|
||||
}
|
||||
onClick={() =>
|
||||
dispatch(MixerState.togglePlayOnLoad({ player: id }))
|
||||
}
|
||||
onClick={() => dispatch(MixerState.togglePlayOnLoad(id))}
|
||||
>
|
||||
<FaPlayCircle />
|
||||
Play on Load
|
||||
|
@ -419,9 +419,7 @@ export function Player({
|
|||
: "btn-outline-secondary") +
|
||||
" btn btn-sm col-4 sp-play-on-load"
|
||||
}
|
||||
onClick={() =>
|
||||
dispatch(MixerState.toggleRepeat({ player: id }))
|
||||
}
|
||||
onClick={() => dispatch(MixerState.toggleRepeat(id))}
|
||||
>
|
||||
<FaRedo />
|
||||
Repeat {playerState.repeat}
|
||||
|
@ -519,56 +517,63 @@ export function Player({
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{!isPreviewChannel && !customOutput && (
|
||||
<div
|
||||
className={
|
||||
"mixer-buttons " +
|
||||
(playerState.state === "playing"
|
||||
? playerState.volume === 0 && !playerState.pfl
|
||||
? "error-animation"
|
||||
: playerState.pfl && "pfl"
|
||||
: "")
|
||||
}
|
||||
>
|
||||
{!process.env.REACT_APP_BAPSICLE_INTERFACE &&
|
||||
!isPreviewChannel &&
|
||||
!customOutput && (
|
||||
<div
|
||||
className="mixer-buttons-backdrop"
|
||||
style={{
|
||||
width:
|
||||
(USE_REAL_GAIN_VALUE ? playerState.gain : playerState.volume) *
|
||||
100 +
|
||||
"%",
|
||||
}}
|
||||
></div>
|
||||
<button onClick={() => dispatch(MixerState.setVolume(id, "off"))}>
|
||||
Off
|
||||
</button>
|
||||
<button onClick={() => dispatch(MixerState.setVolume(id, "bed"))}>
|
||||
Bed
|
||||
</button>
|
||||
<button onClick={() => dispatch(MixerState.setVolume(id, "full"))}>
|
||||
Full
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{!isPreviewChannel && settings.proMode && settings.channelVUs && (
|
||||
<div className="channel-vu">
|
||||
{customOutput ? (
|
||||
<span className="text-muted">
|
||||
Custom audio output disables VU meters.
|
||||
</span>
|
||||
) : playerState.pfl ? (
|
||||
<span className="text-muted">This Player is playing in PFL.</span>
|
||||
) : (
|
||||
<VUMeter
|
||||
width={300}
|
||||
height={40}
|
||||
source={VUsource(id)}
|
||||
range={[-40, 0]}
|
||||
stereo={settings.channelVUsStereo}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
className={
|
||||
"mixer-buttons " +
|
||||
(playerState.state === "playing"
|
||||
? playerState.volume === 0 && !playerState.pfl
|
||||
? "error-animation"
|
||||
: playerState.pfl && "pfl"
|
||||
: "")
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="mixer-buttons-backdrop"
|
||||
style={{
|
||||
width:
|
||||
(USE_REAL_GAIN_VALUE
|
||||
? playerState.gain
|
||||
: playerState.volume) *
|
||||
100 +
|
||||
"%",
|
||||
}}
|
||||
></div>
|
||||
<button onClick={() => dispatch(MixerState.setVolume(id, "off"))}>
|
||||
Off
|
||||
</button>
|
||||
<button onClick={() => dispatch(MixerState.setVolume(id, "bed"))}>
|
||||
Bed
|
||||
</button>
|
||||
<button onClick={() => dispatch(MixerState.setVolume(id, "full"))}>
|
||||
Full
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{!process.env.REACT_APP_BAPSICLE_INTERFACE &&
|
||||
!isPreviewChannel &&
|
||||
settings.proMode &&
|
||||
settings.channelVUs && (
|
||||
<div className="channel-vu">
|
||||
{customOutput ? (
|
||||
<span className="text-muted">
|
||||
Custom audio output disables VU meters.
|
||||
</span>
|
||||
) : playerState.pfl ? (
|
||||
<span className="text-muted">This Player is playing in PFL.</span>
|
||||
) : (
|
||||
<VUMeter
|
||||
width={300}
|
||||
height={40}
|
||||
source={VUsource(id)}
|
||||
range={[-40, 0]}
|
||||
stereo={settings.channelVUsStereo}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -226,7 +226,6 @@ const Showplanner: React.FC<{ timeslotId: number }> = function({ timeslotId }) {
|
|||
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(() => {
|
||||
|
@ -277,18 +276,14 @@ const Showplanner: React.FC<{ timeslotId: number }> = function({ timeslotId }) {
|
|||
</CtxMenuItem>
|
||||
<CtxMenuItem
|
||||
onClick={(args) =>
|
||||
dispatch(
|
||||
setItemPlayed({ itemId: (args.props as any).id, played: false })
|
||||
)
|
||||
dispatch(setItemPlayed((args.props as any).id, false))
|
||||
}
|
||||
>
|
||||
<FaCircleNotch /> Mark Unplayed
|
||||
</CtxMenuItem>
|
||||
<CtxMenuItem
|
||||
onClick={(args) =>
|
||||
dispatch(
|
||||
setItemPlayed({ itemId: (args.props as any).id, played: true })
|
||||
)
|
||||
dispatch(setItemPlayed((args.props as any).id, true))
|
||||
}
|
||||
>
|
||||
<FaCircle /> Mark Played
|
||||
|
@ -347,7 +342,10 @@ const Showplanner: React.FC<{ timeslotId: number }> = function({ timeslotId }) {
|
|||
isOpen={showWelcomeModal}
|
||||
close={() => setShowWelcomeModal(false)}
|
||||
/>
|
||||
<PisModal close={() => setShowPisModal(false)} isOpen={showPisModal} />
|
||||
<PisModal
|
||||
close={() => setShowPisModal(false)}
|
||||
isOpen={showPisModal}
|
||||
/>
|
||||
<MicLiveIndicator />
|
||||
</>
|
||||
)}
|
||||
|
|
|
@ -79,39 +79,41 @@ export function LibraryColumn() {
|
|||
<FaBookOpen className="mx-2" size={25} />
|
||||
Libraries
|
||||
</h2>
|
||||
<div className="row m-0 p-1 card-header hover-menu">
|
||||
<span className="hover-label">Hover for Import & Tools</span>
|
||||
<Button
|
||||
className="mr-1"
|
||||
color="primary"
|
||||
title="Import From Showplan"
|
||||
size="sm"
|
||||
outline={true}
|
||||
onClick={() => setShowImporterModal(true)}
|
||||
>
|
||||
<FaFileImport /> Import
|
||||
</Button>
|
||||
<Button
|
||||
className="mr-1"
|
||||
color="primary"
|
||||
title="Upload to Library"
|
||||
size="sm"
|
||||
outline={true}
|
||||
onClick={() => setShowLibraryModal(true)}
|
||||
>
|
||||
<FaUpload /> Upload
|
||||
</Button>
|
||||
<Button
|
||||
className="mr-1"
|
||||
color="primary"
|
||||
title="Auto Playout"
|
||||
size="sm"
|
||||
outline={true}
|
||||
onClick={() => setAutoPlayoutModal(true)}
|
||||
>
|
||||
<FaPlayCircle /> Auto Playout
|
||||
</Button>
|
||||
</div>
|
||||
{!process.env.REACT_APP_BAPSICLE_INTERFACE && (
|
||||
<div className="row m-0 p-1 card-header hover-menu">
|
||||
<span className="hover-label">Hover for Import & Tools</span>
|
||||
<Button
|
||||
className="mr-1"
|
||||
color="primary"
|
||||
title="Import From Showplan"
|
||||
size="sm"
|
||||
outline={true}
|
||||
onClick={() => setShowImporterModal(true)}
|
||||
>
|
||||
<FaFileImport /> Import
|
||||
</Button>
|
||||
<Button
|
||||
className="mr-1"
|
||||
color="primary"
|
||||
title="Upload to Library"
|
||||
size="sm"
|
||||
outline={true}
|
||||
onClick={() => setShowLibraryModal(true)}
|
||||
>
|
||||
<FaUpload /> Upload
|
||||
</Button>
|
||||
<Button
|
||||
className="mr-1"
|
||||
color="primary"
|
||||
title="Auto Playout"
|
||||
size="sm"
|
||||
outline={true}
|
||||
onClick={() => setAutoPlayoutModal(true)}
|
||||
>
|
||||
<FaPlayCircle /> Auto Playout
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="px-2">
|
||||
<select
|
||||
|
@ -125,12 +127,16 @@ export function LibraryColumn() {
|
|||
Choose a library
|
||||
</option>
|
||||
<option value={"CentralMusicLibrary"}>Central Music Library</option>
|
||||
<option disabled>Personal Resources</option>
|
||||
{userPlaylists.map((playlist) => (
|
||||
<option key={playlist.managedid} value={playlist.managedid}>
|
||||
{playlist.title}
|
||||
</option>
|
||||
))}
|
||||
{!process.env.REACT_APP_BAPSICLE_INTERFACE && (
|
||||
<>
|
||||
<option disabled>Personal Resources</option>
|
||||
{userPlaylists.map((playlist) => (
|
||||
<option key={playlist.managedid} value={playlist.managedid}>
|
||||
{playlist.title}
|
||||
</option>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
<option disabled>Shared Resources</option>
|
||||
{auxPlaylists.map((playlist) => (
|
||||
<option
|
||||
|
|
|
@ -15,9 +15,13 @@ export function Sidebar() {
|
|||
<div id="sidebar">
|
||||
<LibraryColumn />
|
||||
<div className="border-top"></div>
|
||||
<PflPlayer />
|
||||
<div className="border-top"></div>
|
||||
<MicControl />
|
||||
{!process.env.REACT_APP_BAPSICLE_INTERFACE && (
|
||||
<>
|
||||
<PflPlayer />
|
||||
<div className="border-top"></div>
|
||||
<MicControl />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -480,62 +480,64 @@ export const addItem = (
|
|||
if (process.env.REACT_APP_BAPSICLE_INTERFACE) {
|
||||
dispatch(showplan.actions.addItem(newItemData));
|
||||
} else {
|
||||
if (getState().settings.saveShowPlanChanges) {
|
||||
const ghostId = Math.random().toString(10);
|
||||
|
||||
if (getState().settings.saveShowPlanChanges) {
|
||||
const ghostId = Math.random().toString(10);
|
||||
const ghost: ItemGhost = {
|
||||
ghostid: ghostId,
|
||||
channel: newItemData.channel,
|
||||
weight: newItemData.weight,
|
||||
title: newItemData.title,
|
||||
artist: newItemData.type === "central" ? newItemData.artist : "",
|
||||
length: newItemData.length,
|
||||
clean: newItemData.clean,
|
||||
intro: newItemData.type === "central" ? newItemData.intro : 0,
|
||||
outro: newItemData.type === "central" ? newItemData.outro : 0,
|
||||
cue: 0,
|
||||
type: "ghost",
|
||||
};
|
||||
|
||||
const ghost: ItemGhost = {
|
||||
ghostid: ghostId,
|
||||
channel: newItemData.channel,
|
||||
weight: newItemData.weight,
|
||||
title: newItemData.title,
|
||||
artist: newItemData.type === "central" ? newItemData.artist : "",
|
||||
length: newItemData.length,
|
||||
clean: newItemData.clean,
|
||||
intro: newItemData.type === "central" ? newItemData.intro : 0,
|
||||
outro: newItemData.type === "central" ? newItemData.outro : 0,
|
||||
cue: 0,
|
||||
type: "ghost",
|
||||
};
|
||||
const idForServer =
|
||||
newItemData.type === "central"
|
||||
? `${newItemData.album.recordid}-${newItemData.trackid}`
|
||||
: "managedid" in newItemData
|
||||
? `ManagedDB-${newItemData.managedid}`
|
||||
: null;
|
||||
|
||||
const idForServer =
|
||||
newItemData.type === "central"
|
||||
? `${newItemData.album.recordid}-${newItemData.trackid}`
|
||||
: "managedid" in newItemData
|
||||
? `ManagedDB-${newItemData.managedid}`
|
||||
: null;
|
||||
if (!idForServer) return; // Something went very wrong
|
||||
|
||||
if (!idForServer) return; // Something went very wrong
|
||||
|
||||
dispatch(showplan.actions.insertGhost(ghost));
|
||||
ops.push({
|
||||
op: "AddItem",
|
||||
channel: newItemData.channel,
|
||||
weight: newItemData.weight,
|
||||
id: idForServer,
|
||||
});
|
||||
const result = await api.updateShowplan(timeslotId, ops);
|
||||
if (!result.every((x) => x.status)) {
|
||||
Sentry.captureException(new Error("Showplan update failure [addItem]"), {
|
||||
dispatch(showplan.actions.insertGhost(ghost));
|
||||
ops.push({
|
||||
op: "AddItem",
|
||||
channel: newItemData.channel,
|
||||
weight: newItemData.weight,
|
||||
id: idForServer,
|
||||
});
|
||||
const result = await api.updateShowplan(timeslotId, ops);
|
||||
if (!result.every((x) => x.status)) {
|
||||
Sentry.captureException(
|
||||
new Error("Showplan update failure [addItem]"),
|
||||
{
|
||||
contexts: {
|
||||
updateShowplan: {
|
||||
ops,
|
||||
result,
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
});
|
||||
const lastResult = result[result.length - 1]; // this is the add op
|
||||
const newItemId = lastResult.timeslotitemid!;
|
||||
|
||||
const lastResult = result[result.length - 1]; // this is the add op
|
||||
const newItemId = lastResult.timeslotitemid!;
|
||||
|
||||
newItemData.timeslotitemid = newItemId;
|
||||
dispatch(
|
||||
showplan.actions.replaceGhost({
|
||||
ghostId: "G" + ghostId,
|
||||
newItemData,
|
||||
})
|
||||
);
|
||||
newItemData.timeslotitemid = newItemId;
|
||||
dispatch(
|
||||
showplan.actions.replaceGhost({
|
||||
ghostId: "G" + ghostId,
|
||||
newItemData,
|
||||
})
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Just add it straight to the show plan without updating the server.
|
||||
dispatch(showplan.actions.addItem(newItemData));
|
||||
|
@ -577,7 +579,6 @@ export const removeItem = (
|
|||
movingItem.weight -= 1;
|
||||
|
||||
if (!process.env.REACT_APP_BAPSICLE_INTERFACE) {
|
||||
|
||||
if (getState().settings.saveShowPlanChanges) {
|
||||
const result = await api.updateShowplan(timeslotId, ops);
|
||||
if (!result.every((x) => x.status)) {
|
||||
|
@ -592,15 +593,17 @@ 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));
|
||||
dispatch(showplan.actions.applyOps(ops));
|
||||
dispatch(showplan.actions.setPlanSaving(false));
|
||||
};
|
||||
|
||||
export const getShowplan = (timeslotId: number): AppThunk => async (
|
||||
|
@ -663,12 +666,12 @@ export const getPlaylists = (): AppThunk => async (dispatch) => {
|
|||
if (!process.env.REACT_APP_BAPSICLE_INTERFACE) {
|
||||
try {
|
||||
const userPlaylists = await api.getUserPlaylists();
|
||||
|
||||
dispatch(showplan.actions.addUserPlaylists(userPlaylists));
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const managedPlaylists = await api.getManagedPlaylists();
|
||||
dispatch(showplan.actions.addManagedPlaylists(managedPlaylists));
|
||||
|
@ -678,7 +681,6 @@ export const getPlaylists = (): AppThunk => async (dispatch) => {
|
|||
|
||||
try {
|
||||
const auxPlaylists = await api.getAuxPlaylists();
|
||||
|
||||
dispatch(showplan.actions.addAuxPlaylists(auxPlaylists));
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
|
|
101
yarn.lock
101
yarn.lock
|
@ -1425,68 +1425,79 @@
|
|||
estree-walker "^1.0.1"
|
||||
picomatch "^2.2.2"
|
||||
|
||||
"@sentry/browser@6.5.0":
|
||||
version "6.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-6.5.0.tgz#2382493691c3fac5d8b652ae46f09f1b29d288ef"
|
||||
integrity sha512-n1e8hNKwuVP4bLqRK5J0DHFqnnnrbv6h6+Bc1eNRbf32/e6eZ3Cb36PTplqDCxwnMnnIEEowd5F4ZWeTLPPY3A==
|
||||
"@sentry/browser@6.3.1":
|
||||
version "6.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-6.3.1.tgz#6142dd4c72308f4e1a12e585e3300fd54ca058cd"
|
||||
integrity sha512-Ri4tYsyuJIeLQnvQUqbpGzailUYpbjFSYM0+yEM63gPsjiXdg+W8yKHluA6cs6FLWVN3oWfwHW7Kd61echlGuw==
|
||||
dependencies:
|
||||
"@sentry/core" "6.5.0"
|
||||
"@sentry/types" "6.5.0"
|
||||
"@sentry/utils" "6.5.0"
|
||||
"@sentry/core" "6.3.1"
|
||||
"@sentry/types" "6.3.1"
|
||||
"@sentry/utils" "6.3.1"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/core@6.5.0":
|
||||
version "6.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-6.5.0.tgz#03ecbad7845b31f03a84eddf4884877c999bb6be"
|
||||
integrity sha512-Hx/WvhM5bXcXqfIiz+505TjYYfPjQ8mrxby/EWl+L7dYUCyI/W6IZKTc/MoHlLuM+JPUW9c1bw/97TzbgTzaAA==
|
||||
"@sentry/core@6.3.1":
|
||||
version "6.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-6.3.1.tgz#5e32ca919c9be30fec0bb3125a556bc711584bdf"
|
||||
integrity sha512-aVuvVbaehGeN86jZlLDGGkhEtprdOtB6lvYLfGy40Dj1Tkh2mGWE550QsRXAXAqYvQzIYwQR23r6m3o8FujgVg==
|
||||
dependencies:
|
||||
"@sentry/hub" "6.5.0"
|
||||
"@sentry/minimal" "6.5.0"
|
||||
"@sentry/types" "6.5.0"
|
||||
"@sentry/utils" "6.5.0"
|
||||
"@sentry/hub" "6.3.1"
|
||||
"@sentry/minimal" "6.3.1"
|
||||
"@sentry/types" "6.3.1"
|
||||
"@sentry/utils" "6.3.1"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/hub@6.5.0":
|
||||
version "6.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-6.5.0.tgz#ad3c9bcf83050ea217f3c25cc625e6b447f1d9d7"
|
||||
integrity sha512-vEChnLoozOJzEJoTUvaAsK/n7IHoQFx8P1TzQmnR+8XGZJZmGHG6bBXUH0iS2a9hhR1WkoEBeiL+t96R9uyf0A==
|
||||
"@sentry/hub@6.3.1":
|
||||
version "6.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-6.3.1.tgz#dda07888a82d1c48bbefa00205bfa9d035691f07"
|
||||
integrity sha512-2er+OeVlsdVZkhl9kXQAANwgjwoCdM1etK2iFuhzX8xkMaJlAuZLyQInv2U1BbXBlIfWjvzRM8B95hCWvVrR3Q==
|
||||
dependencies:
|
||||
"@sentry/types" "6.5.0"
|
||||
"@sentry/utils" "6.5.0"
|
||||
"@sentry/types" "6.3.1"
|
||||
"@sentry/utils" "6.3.1"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/minimal@6.5.0":
|
||||
version "6.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-6.5.0.tgz#aa89b8e24c88aa85c99ef64e0b460497c90133f9"
|
||||
integrity sha512-MT83ONaBhTCFUlDIQFpsG/lq3ZjGK7jwQ10qxGadSg1KW6EvtQRg+OBwULeQ7C+nNEAhseNrC/qomZMT8brncg==
|
||||
"@sentry/minimal@6.3.1":
|
||||
version "6.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-6.3.1.tgz#38f71c77e8820555effb6e868336d4f5672018cd"
|
||||
integrity sha512-0eN9S7HvXsCQEjX/qXHTMgvSb3mwrnZEWS9Qz/Bz5ig9pEGXKgJ1om5NTTHVHhXqd3wFCjdvIo6slufLHoCtSw==
|
||||
dependencies:
|
||||
"@sentry/hub" "6.5.0"
|
||||
"@sentry/types" "6.5.0"
|
||||
"@sentry/hub" "6.3.1"
|
||||
"@sentry/types" "6.3.1"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/react@^6.5.0":
|
||||
version "6.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/react/-/react-6.5.0.tgz#db2067e080cac24da046af24865b82d3567adbf4"
|
||||
integrity sha512-NyH+v8MwX+nzuhPRGy3+DHSB0es5yaCUNrtAdCtbe8EhERSoYvqAyWIQ+Fp5++PGjfAtYbz0W0IpsjguZbnT2Q==
|
||||
"@sentry/react@^6.3.1":
|
||||
version "6.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/react/-/react-6.3.1.tgz#5082aa145972eec38cc8ceea8e5d8ee3f7f5f86a"
|
||||
integrity sha512-3eFSqdS0QAb4RFNxS0gzVm05q8c5KQp+3TlmqBjoovqWL/FvGvDoqaBmFT+arvPZ88qngveMEk1v6445L0gFTg==
|
||||
dependencies:
|
||||
"@sentry/browser" "6.5.0"
|
||||
"@sentry/minimal" "6.5.0"
|
||||
"@sentry/types" "6.5.0"
|
||||
"@sentry/utils" "6.5.0"
|
||||
"@sentry/browser" "6.3.1"
|
||||
"@sentry/minimal" "6.3.1"
|
||||
"@sentry/types" "6.3.1"
|
||||
"@sentry/utils" "6.3.1"
|
||||
hoist-non-react-statics "^3.3.2"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/types@6.5.0":
|
||||
version "6.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-6.5.0.tgz#2cdb50875bb73d87708b9c0a80d4ca057b3596b5"
|
||||
integrity sha512-yQpTCIYxBsYT0GenqHNNKeXV8CSkkYlAxB1IGV2eac4IKC5ph5GW6TfDGwvlzQSQ297RsRmOSA8o3I5gGPd2yA==
|
||||
|
||||
"@sentry/utils@6.5.0":
|
||||
version "6.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-6.5.0.tgz#8722542b9a901623195cffaab5d18ce176c1e459"
|
||||
integrity sha512-CcHuaQN6vRuAsIC+3sA23NmWLRmUN0x/HNQxk0DHJylvYQdEA0AUNoLXogykaXh6NrCx4DNq9yCQTNTSC3mFxg==
|
||||
"@sentry/tracing@^6.3.1":
|
||||
version "6.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-6.3.1.tgz#3b96aabf4d9cebadfec070c006db79801a68ee24"
|
||||
integrity sha512-qveDmoWsXy9qLEblZJwJ1OU/zZRlEd/q7Jhd0Hnwlob8Ci96huABEbYyGdJs18BKVHEFU3gSdVfvrikUE/W17g==
|
||||
dependencies:
|
||||
"@sentry/types" "6.5.0"
|
||||
"@sentry/hub" "6.3.1"
|
||||
"@sentry/minimal" "6.3.1"
|
||||
"@sentry/types" "6.3.1"
|
||||
"@sentry/utils" "6.3.1"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/types@6.3.1":
|
||||
version "6.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-6.3.1.tgz#af3b54728b29f633f38fbe51b8c10e3834fbc158"
|
||||
integrity sha512-BEBn8JX1yaooCAuonbaMci9z0RjwwMbQ3Eny/eyDdd+rjXprZCZaStZnCvSThbNBqAJ8YaUqY2YBMnEwJxarAw==
|
||||
|
||||
"@sentry/utils@6.3.1":
|
||||
version "6.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-6.3.1.tgz#6d8e691139b5b49d8c655ad1dcaf2cb3ff0d0b03"
|
||||
integrity sha512-cdtl/QWC9FtinAuW3w8QfvSfh/Q9ui5vwvjzVHiS1ga/U38edi2XX+cttY39ZYwz0SQG99cE10GOIhd1p7/mAA==
|
||||
dependencies:
|
||||
"@sentry/types" "6.3.1"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@surma/rollup-plugin-off-main-thread@^1.1.1":
|
||||
|
|
Loading…
Reference in a new issue