Skip to content

Commit

Permalink
Video: Auto update Video players and recorder on stream rename
Browse files Browse the repository at this point in the history
Signed-off-by: Arturo Manzoli <[email protected]>
  • Loading branch information
ArturoManzoli committed Sep 30, 2024
1 parent c0742dd commit 6ec7302
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 15 deletions.
57 changes: 43 additions & 14 deletions src/components/mini-widgets/MiniVideoRecorder.vue
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
variant="text"
@click="widgetStore.miniWidgetManagerVars(miniWidget.hash).configMenuOpen = false"
>
Cancel
Close
</v-btn>
<v-btn
class="bg-[#FFFFFF11] hover:bg-[#FFFFFF33]"
Expand Down Expand Up @@ -123,10 +123,9 @@ const timeNow = useTimestamp({ interval: 100 })
const mediaStream = ref<MediaStream | undefined>()
const isProcessingVideo = ref(false)
const numberOfVideosOnDB = ref(0)
const selectedExternalId = ref<string | undefined>()
const externalStreamId = computed(() => {
return nameSelectedStream.value ? videoStore.externalStreamId(nameSelectedStream.value) : undefined
})
const externalStreamId = computed(() => selectedExternalId.value)
const openVideoLibraryModal = (): void => {
interfaceStore.videoLibraryVisibility = true
Expand All @@ -150,13 +149,43 @@ onBeforeMount(async () => {
}
}
nameSelectedStream.value = miniWidget.value.options.internalStreamName
if (nameSelectedStream.value) {
selectedExternalId.value = videoStore.externalStreamId(nameSelectedStream.value)
}
})
watch(nameSelectedStream, () => {
miniWidget.value.options.internalStreamName = nameSelectedStream.value
mediaStream.value = undefined
})
watch(
() => videoStore.streamsCorrespondency,
(newStreamsCorrespondency) => {
if (!selectedExternalId.value) return
const matchingStream = newStreamsCorrespondency.find((stream) => stream.externalId === selectedExternalId.value)
if (matchingStream) {
if (nameSelectedStream.value !== matchingStream.name) {
nameSelectedStream.value = matchingStream.name
}
} else {
// The externalId no longer exists; handle accordingly
nameSelectedStream.value = undefined
selectedExternalId.value = undefined
}
},
{ deep: true }
)
watch(nameSelectedStream, (newName) => {
selectedExternalId.value = newName ? videoStore.externalStreamId(newName) : undefined
miniWidget.value.options.internalStreamName = newName
mediaStream.value = undefined
})
// Fetch number of temporary videos on storage
const fetchNumberOfTempVideos = async (): Promise<void> => {
const nProcessedVideos = (await videoStore.videoStoringDB.keys()).filter((k) => videoStore.isVideoFilename(k)).length
Expand Down Expand Up @@ -184,39 +213,39 @@ function assertStreamIsSelectedAndAvailable(
const toggleRecording = async (): Promise<void> => {
if (isRecording.value) {
if (externalStreamId.value !== undefined) {
videoStore.stopRecording(externalStreamId.value)
if (selectedExternalId.value) {
videoStore.stopRecording(selectedExternalId.value)
}
return
}
// If there's no stream selected, open the configuration dialog so user can choose the stream which will be recorded
if (nameSelectedStream.value === undefined) {
if (!nameSelectedStream.value) {
widgetStore.miniWidgetManagerVars(miniWidget.value.hash).configMenuOpen = true
return
}
// If there's a stream selected already, try to use it without requiring further user interaction
startRecording()
}
const startRecording = (): void => {
if (externalStreamId.value === undefined) {
if (!selectedExternalId.value) {
showDialog({ title: 'Cannot start recording.', message: 'No stream selected.', variant: 'error' })
return
}
if (!videoStore.getStreamData(externalStreamId.value)?.connected) {
if (!videoStore.getStreamData(selectedExternalId.value)?.connected) {
showDialog({ title: 'Cannot start recording.', message: 'Stream is not connected.', variant: 'error' })
return
}
assertStreamIsSelectedAndAvailable(nameSelectedStream.value)
videoStore.startRecording(externalStreamId.value)
videoStore.startRecording(selectedExternalId.value)
widgetStore.miniWidgetManagerVars(miniWidget.value.hash).configMenuOpen = false
}
const isRecording = computed(() => {
if (externalStreamId.value === undefined) return false
return videoStore.isRecording(externalStreamId.value)
if (!selectedExternalId.value) return false
return videoStore.isRecording(selectedExternalId.value)
})
const timePassedString = computed(() => {
Expand Down
27 changes: 26 additions & 1 deletion src/components/widgets/VideoPlayer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,24 @@ const externalStreamId = computed(() => {
watch(
() => videoStore.streamsCorrespondency,
() => (mediaStream.value = undefined),
() => {
mediaStream.value = undefined
if (!nameSelectedStream.value) return
const selectedExternalId = videoStore.externalStreamId(nameSelectedStream.value)
if (!selectedExternalId) return
const newStreamCorr = videoStore.streamsCorrespondency.find((stream) => stream.externalId === selectedExternalId)
if (!newStreamCorr) return
const newInternalName = newStreamCorr.name
if (nameSelectedStream.value !== newInternalName) {
nameSelectedStream.value = newInternalName
widget.value.options.internalStreamName = newInternalName
}
},
{ deep: true }
)
Expand All @@ -169,6 +186,14 @@ const streamConnectionRoutine = setInterval(() => {
streamConnected.value = updatedStreamState
}
}
if (!namesAvailableStreams.value.isEmpty() && !namesAvailableStreams.value.includes(nameSelectedStream.value!)) {
if (videoStore.lastRenamedStreamName !== '') {
nameSelectedStream.value = videoStore.lastRenamedStreamName
return
}
nameSelectedStream.value = namesAvailableStreams.value[0]
}
}, 1000)
onBeforeUnmount(() => clearInterval(streamConnectionRoutine))
Expand Down
30 changes: 30 additions & 0 deletions src/stores/video.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export const useVideoStore = defineStore('video', () => {
const unprocessedVideos = useStorage<{ [key in string]: UnprocessedVideoInfo }>('cockpit-unprocessed-video-info', {})
const timeNow = useTimestamp({ interval: 500 })
const autoProcessVideos = useBlueOsStorage('cockpit-auto-process-videos', true)
const lastRenamedStreamName = ref('')

const namesAvailableStreams = computed(() => mainWebRTCManager.availableStreams.value.map((stream) => stream.name))

Expand Down Expand Up @@ -870,6 +871,33 @@ export const useVideoStore = defineStore('video', () => {
alertStore.pushAlert(new Alert(AlertLevel.Success, `Stopped recording streams: ${streamsThatStopped.join(', ')}.`))
}

const renameStreamInternalNameById = (streamID: string, newInternalName: string): void => {
const streamCorr = streamsCorrespondency.value.find((stream) => stream.externalId === streamID)

if (streamCorr) {
const oldInternalName = streamCorr.name
streamCorr.name = newInternalName

const streamData = activeStreams.value[oldInternalName]
if (streamData) {
activeStreams.value = {
...activeStreams.value,
[newInternalName]: streamData,
}
delete activeStreams.value[oldInternalName]
}
lastRenamedStreamName.value = newInternalName
console.log(`Stream internal name updated from '${oldInternalName}' to '${newInternalName}'.`)
} else {
console.warn(`Stream with ID '${streamID}' not found.`)
showSnackbar({
variant: 'error',
message: `Stream with ID '${streamID}' not found.`,
duration: 3000,
})
}
}

registerActionCallback(
availableCockpitActions.start_recording_all_streams,
useThrottleFn(startRecordingAllStreams, 3000)
Expand Down Expand Up @@ -916,5 +944,7 @@ export const useVideoStore = defineStore('video', () => {
processVideoChunksAndTelemetry,
isVideoFilename,
activeStreams,
renameStreamInternalNameById,
lastRenamedStreamName,
}
})

0 comments on commit 6ec7302

Please sign in to comment.