diff --git a/src/App.scss b/src/App.scss index 20c33c6e..2033c808 100644 --- a/src/App.scss +++ b/src/App.scss @@ -27,7 +27,6 @@ $number-of-channels: 3; .hover-label { display: none; } - @media (max-height: 900px) { .hover-label { font-size: 10px; @@ -35,19 +34,15 @@ $number-of-channels: 3; display: block; width: 100%; } - &:hover { height: 100%; - .hover-label-hide { display: none; } } - &:not(:hover) { overflow: hidden; height: 15px; - > *:not(.hover-label) { display: none !important; } @@ -70,7 +65,6 @@ $number-of-channels: 3; grid-template-rows: 100%; grid-gap: 10px; padding: 10px; - .channel-vu { text-align: center; background: black; @@ -141,6 +135,12 @@ $number-of-channels: 3; box-sizing: content-box; } +#playoutIframe { + width: 100%; + height: 100%; + box-sizing: content-box; +} + .sp-mic-live { position: fixed; top: 0; @@ -161,16 +161,13 @@ $number-of-channels: 3; width: 100%; display: inline-block; text-align: center; - padding: 0; - .text { margin: 0; float: left; padding: 0 10px; border-right: 3px solid white; } - &.live { border: 3px solid red; .text { diff --git a/src/broadcast/recording_streamer.ts b/src/broadcast/recording_streamer.ts index 30e8bb0f..a184d0b3 100644 --- a/src/broadcast/recording_streamer.ts +++ b/src/broadcast/recording_streamer.ts @@ -3,11 +3,16 @@ import { Streamer } from "./streamer"; export class RecordingStreamer extends Streamer { recorder: MediaRecorder; chunks: Blob[]; + url: string; + modalCallback: () => void; constructor(stream: MediaStream) { super(); this.recorder = new MediaRecorder(stream); this.chunks = []; + this.url = ""; + this.modalCallback = () => {}; + this.recorder.ondataavailable = (e) => { this.chunks.push(e.data); }; @@ -17,14 +22,10 @@ export class RecordingStreamer extends Streamer { this.recorder.onstop = () => { this.onStateChange("NOT_CONNECTED"); const finalData = new Blob(this.chunks, { - type: "audio/ogg; codecs=opus", + type: "audio/mp3; codecs=mpeg", }); - const url = URL.createObjectURL(finalData); - - const a = document.createElement("a"); - a.href = url; - a.download = "recorded.ogg"; - a.click(); + this.url = URL.createObjectURL(finalData); + this.modalCallback(); }; this.recorder.onerror = (e) => { console.error(e.error); @@ -36,7 +37,10 @@ export class RecordingStreamer extends Streamer { this.chunks = []; this.recorder.start(); } - async stop(): Promise { + async stop(callback?: () => void): Promise { + if (callback) { + this.modalCallback = callback; + } this.recorder.stop(); } } diff --git a/src/broadcast/state.ts b/src/broadcast/state.ts index ed86b3c2..cca9f4fe 100644 --- a/src/broadcast/state.ts +++ b/src/broadcast/state.ts @@ -339,10 +339,20 @@ export const startRecording = (): AppThunk => async (dispatch) => { await recorder.start(); }; -export const stopRecording = (): AppThunk => async (dispatch) => { +export const stopRecording = (callback: () => void): AppThunk => async ( + dispatch +) => { if (recorder) { - await recorder.stop(); + await recorder.stop(callback); } else { console.warn("stopRecording called with no recorder!"); } }; + +export const getRecording = () => { + if (recorder) { + return recorder.url; + } + + // There isn't a console.warn here, because it spammed on load for some reason +}; diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 23271c6e..2c7f610c 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -47,6 +47,16 @@ export function secToHHMM(sec: number = 0) { return d.toLocaleString("en-GB").split(" ")[1]; } +export function HHMMTosec(timeString: string = "00:00:00") { + var s: number = parseInt(timeString[7]); + s += 10 * parseInt(timeString[6]); + s += 60 * parseInt(timeString[4]); + s += 600 * parseInt(timeString[3]); + s += 3600 * parseInt(timeString[1]); + s += 36000 * parseInt(timeString[0]); + return s; +} + export function timestampToHHMM(sec: number = 0) { return format(fromUnixTime(sec), "HH:mm:ss"); } diff --git a/src/navbar/FinishRecordingModal.tsx b/src/navbar/FinishRecordingModal.tsx new file mode 100644 index 00000000..2730478b --- /dev/null +++ b/src/navbar/FinishRecordingModal.tsx @@ -0,0 +1,63 @@ +import React from "react"; +import { FaTimes, FaMicrophone } from "react-icons/fa"; +import Modal from "react-modal"; +import { Button } from "reactstrap"; +import * as BroadcastState from "../broadcast/state"; + +interface FinishRecordingModal { + isOpen: boolean; + close: () => any; +} + +export function FinishRecordingModal(props: FinishRecordingModal) { + return ( + +
+

+ + Recording Complete +

+ +
+
+

+ Your recording is now complete. You can listen to it back and then + download it and upload it to your show plan if you want. +

+ + +
+ +
+ + +
+
+ ); +} diff --git a/src/navbar/index.tsx b/src/navbar/index.tsx index 8c712393..8bcf148d 100644 --- a/src/navbar/index.tsx +++ b/src/navbar/index.tsx @@ -24,6 +24,7 @@ import { closeAlert } from "./state"; import { ConnectionStateEnum } from "../broadcast/streamer"; import { VUMeter } from "../optionsMenu/helpers/VUMeter"; import { getShowplan } from "../showplanner/state"; +import { FinishRecordingModal } from "./FinishRecordingModal"; import * as OptionsMenuState from "../optionsMenu/state"; @@ -156,6 +157,8 @@ export function NavBarMain() { const [connectButtonAnimating, setConnectButtonAnimating] = useState(false); + const [finishRecordingModal, setFinishRecordingModal] = useState(false); + const prevRegistrationStage = useRef(broadcastState.stage); useEffect(() => { if (broadcastState.stage !== prevRegistrationStage.current) { @@ -170,6 +173,12 @@ export function NavBarMain() { return ( <> + { + setFinishRecordingModal(false); + }} + />
  • + onClick={() => { dispatch( broadcastState.recordingState === "NOT_CONNECTED" ? BroadcastState.startRecording() - : BroadcastState.stopRecording() - ) - } + : BroadcastState.stopRecording(() => { + setFinishRecordingModal(true); + }) + ); + }} > any; +} + +export function AutoPlayoutModal(props: AutoPlayoutProps) { + return ( + +
    +

    + + URY Automatic Playout +

    + +
    +
    + +
    +
    + ); +} diff --git a/src/showplanner/Player.tsx b/src/showplanner/Player.tsx index d539d60b..bc463e20 100644 --- a/src/showplanner/Player.tsx +++ b/src/showplanner/Player.tsx @@ -13,7 +13,7 @@ import { omit } from "lodash"; import { RootState } from "../rootReducer"; import * as MixerState from "../mixer/state"; import * as ShowPlanState from "../showplanner/state"; -import { secToHHMM, timestampToHHMM } from "../lib/utils"; +import { HHMMTosec, secToHHMM, timestampToHHMM } from "../lib/utils"; import ProModeButtons from "./ProModeButtons"; import { VUMeter } from "../optionsMenu/helpers/VUMeter"; import * as api from "../api"; @@ -221,6 +221,15 @@ export function Player({ id }: { id: number }) { throw new Error("Unknown Player VUMeter source: " + id); } }; + + var duration: number = 0; + const plan = useSelector((state: RootState) => state.showplan.plan); + plan?.forEach((pItem) => { + if (pItem.channel === id) { + duration += HHMMTosec(pItem.length); + } + }); + return (
      Repeat {playerState.repeat} +
    Total Time: {secToHHMM(duration)}
    {proMode && }
    diff --git a/src/showplanner/index.tsx b/src/showplanner/index.tsx index 426c5414..09bf3ac2 100644 --- a/src/showplanner/index.tsx +++ b/src/showplanner/index.tsx @@ -8,6 +8,7 @@ import { FaMicrophone, FaTrash, FaUpload, + FaPlayCircle, } from "react-icons/fa"; import { VUMeter } from "../optionsMenu/helpers/VUMeter"; import Stopwatch from "react-stopwatch"; @@ -50,6 +51,7 @@ import { CombinedNavAlertBar } from "../navbar"; import { OptionsMenu } from "../optionsMenu"; import { WelcomeModal } from "./WelcomeModal"; import { PisModal } from "./PISModal"; +import { AutoPlayoutModal } from "./AutoPlayoutModal"; import { LibraryUploadModal } from "./LibraryUploadModal"; import { ImporterModal } from "./ImporterModal"; import "./channel.scss"; @@ -88,6 +90,7 @@ function LibraryColumn() { (state: RootState) => state.showplan ); + const [autoPlayoutModal, setAutoPlayoutModal] = useState(false); const [showLibraryUploadModal, setShowLibraryModal] = useState(false); const [showImporterModal, setShowImporterModal] = useState(false); @@ -97,6 +100,10 @@ function LibraryColumn() { return ( <> + setAutoPlayoutModal(false)} + /> setShowLibraryModal(false)} @@ -111,6 +118,16 @@ function LibraryColumn() { Libraries +