Skip to content

Commit

Permalink
video: Add button with files count badge to access stored videos modal
Browse files Browse the repository at this point in the history
Also a minor arrangement to the buttons on the video storage interface (modal and config screens)
  • Loading branch information
ArturoManzoli authored and patrickelectric committed Mar 15, 2024
1 parent 0136bad commit 5099a9c
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 23 deletions.
48 changes: 46 additions & 2 deletions src/components/mini-widgets/MiniVideoRecorder.vue
@@ -1,7 +1,8 @@
<template>
<div
ref="recorderWidget"
class="flex justify-around px-2 py-1 text-center rounded-lg h-9 w-28 align-center bg-slate-800/60"
class="flex justify-around px-2 py-1 text-center rounded-lg h-9 align-center bg-slate-800/60"
:class="{ 'w-48': numberOfVideosOnDB > 0, 'w-32': numberOfVideosOnDB <= 0 }"
>
<div
v-if="!isProcessingVideo"
Expand Down Expand Up @@ -31,6 +32,14 @@
<div v-else-if="isProcessingVideo" class="w-16 text-justify text-slate-100">
<div class="text-center text-xs text-white select-none flex-nowrap">Processing video...</div>
</div>
<div v-if="numberOfVideosOnDB > 0" class="flex justify-center w-8">
<v-divider vertical class="h-6" />
<v-badge color="info" :content="numberOfVideosOnDB" :dot="isOutside || isVideoLibraryDialogOpen"
><v-icon class="w-6 h-6 text-slate-100 ml-3" @click="isVideoLibraryDialogOpen = true">
mdi-video-box
</v-icon></v-badge
>
</div>
</div>
<v-dialog v-model="isStreamSelectDialogOpen" width="auto">
<div class="p-6 m-5 bg-white rounded-md">
Expand Down Expand Up @@ -67,19 +76,23 @@
</div>
</div>
</v-dialog>
<v-dialog v-model="isVideoLibraryDialogOpen" width="auto">
<ConfigurationVideoView as-video-library />
</v-dialog>
</template>

<script setup lang="ts">
import { useMouseInElement, useTimestamp } from '@vueuse/core'
import { intervalToDuration } from 'date-fns'
import { storeToRefs } from 'pinia'
import Swal from 'sweetalert2'
import { computed, onBeforeMount, onBeforeUnmount, ref, toRefs, watch } from 'vue'
import { computed, onBeforeMount, onBeforeUnmount, onMounted, ref, toRefs, watch } from 'vue'
import { isEqual } from '@/libs/utils'
import { useVideoStore } from '@/stores/video'
import { useWidgetManagerStore } from '@/stores/widgetManager'
import type { MiniWidget } from '@/types/miniWidgets'
import ConfigurationVideoView from '@/views/ConfigurationVideoView.vue'
const widgetStore = useWidgetManagerStore()
const videoStore = useVideoStore()
Expand All @@ -97,10 +110,16 @@ const { namesAvailableStreams } = storeToRefs(videoStore)
const recorderWidget = ref()
const { isOutside } = useMouseInElement(recorderWidget)
const isStreamSelectDialogOpen = ref(false)
const isVideoLibraryDialogOpen = ref(false)
const isLoadingStream = ref(false)
const timeNow = useTimestamp({ interval: 100 })
const mediaStream = ref<MediaStream | undefined>()
const isProcessingVideo = ref(false)
const numberOfVideosOnDB = ref(0)
onMounted(async () => {
await fetchNumebrOfTempVideos()
})
onBeforeMount(async () => {
// Set initial widget options if they don't exist
Expand All @@ -117,6 +136,12 @@ watch(nameSelectedStream, () => {
mediaStream.value = undefined
})
// Fetch temporary video data from the storage
const fetchNumebrOfTempVideos = async (): Promise<void> => {
const size = await videoStore.videoStoringDB.length()
numberOfVideosOnDB.value = size
}
// eslint-disable-next-line jsdoc/require-jsdoc
function assertStreamIsSelectedAndAvailable(
selectedStream: undefined | string
Expand Down Expand Up @@ -232,6 +257,16 @@ watch(
() => videoStore.areThereVideosProcessing,
(newValue) => {
isProcessingVideo.value = newValue
fetchNumebrOfTempVideos()
}
)
watch(
() => isVideoLibraryDialogOpen.value,
async (newValue) => {
if (newValue === false) {
await fetchNumebrOfTempVideos()
}
}
)
Expand Down Expand Up @@ -295,4 +330,13 @@ watch(isRecording, () => {
background-color: #475569;
box-shadow: 0px 0px 3px 3px #475569;
}
.close-icon {
position: absolute;
top: 5px;
right: 5px;
cursor: pointer;
font-size: 20px;
color: #999;
}
</style>
63 changes: 42 additions & 21 deletions src/views/ConfigurationVideoView.vue
@@ -1,8 +1,9 @@
<template>
<BaseConfigurationView>
<template #title>Video configuration</template>
<template #title>{{ isVideoLibraryOnly ? 'Video Storage' : 'Video configuration' }}</template>
<template #content>
<div
v-if="!isVideoLibraryOnly"
class="flex flex-col items-center px-5 py-3 m-5 font-medium text-center border rounded-md text-grey-darken-1 bg-grey-lighten-5 w-[40%]"
>
<p class="font-bold">
Expand All @@ -18,7 +19,7 @@
</p>
</div>

<div class="flex w-[30rem] flex-wrap">
<div v-if="!isVideoLibraryOnly" class="flex w-[30rem] flex-wrap">
<v-combobox
v-model="allowedIceIps"
multiple
Expand Down Expand Up @@ -60,7 +61,11 @@
</p>
</div>

<div v-if="availableVideosAndLogs?.isEmpty()" class="max-w-[50%] bg-slate-100 rounded-md p-6 border">
<div
v-if="availableVideosAndLogs?.isEmpty()"
:class="{ 'mb-4': isVideoLibraryOnly, 'mb-0': !isVideoLibraryOnly }"
class="max-w-[50%] bg-slate-100 rounded-md p-6 border"
>
<p class="mb-4 text-2xl font-semibold text-center text-slate-500">No videos available.</p>
<p class="text-center text-slate-400">
Use the MiniVideoRecorder widget to record some videos and them come back here to download or discard those.
Expand All @@ -78,7 +83,8 @@
show-select
loading-text="Loading... Please wait"
:loading="availableVideosAndLogs === undefined"
class="max-w-[90%] bg-slate-100/30 rounded-lg p-6 border"
class="max-w-[90%] bg-slate-100/30 rounded-lg p-3 border"
:class="temporaryDbSize === 0 ? 'mb-10' : 'mb-0'"
>
<template #item.size="{ value }">
{{ formatBytes(value) }}
Expand Down Expand Up @@ -110,22 +116,23 @@
</Transition>
</template>
</v-data-table>

<div
v-if="temporaryDbSize > 0"
v-tooltip.bottom="'Remove video files used during the recording. This will not affect already saved videos.'"
class="flex flex-col items-center justify-center p-4 m-4 transition-all rounded-md cursor-pointer bg-slate-600 text-slate-50 hover:bg-slate-500/80"
@click="clearTemporaryVideoFiles()"
>
<span class="text-lg font-medium">Clear temporary video storage</span>
<span class="text-sm text-slate-300/90">Current size: {{ formatBytes(temporaryDbSize) }}</span>
</div>
<div
v-if="temporaryDbSize > 0"
class="flex flex-col items-center justify-center p-4 m-4 transition-all rounded-md cursor-pointer bg-slate-600 text-slate-50 hover:bg-slate-500/80"
@click="videoStore.downloadTempVideoDB()"
>
<span class="text-lg font-medium">Download temporary video chunks</span>
<div class="flex flex-row">
<div
v-if="temporaryDbSize > 0"
v-tooltip.bottom="'Remove video files used during the recording. This will not affect already saved videos.'"
class="flex flex-col items-center justify-center px-4 py-2 mx-4 mb-6 mt-8 transition-all rounded-md cursor-pointer bg-slate-600 text-slate-50 hover:bg-slate-500/80"
@click="clearTemporaryVideoFiles()"
>
<span class="text-md font-medium">Clear temporary video storage</span>
<span class="text-sm text-slate-300/90">Current size: {{ formatBytes(temporaryDbSize) }}</span>
</div>
<div
v-if="temporaryDbSize > 0"
class="flex flex-col items-center justify-center px-4 py-2 mx-4 mb-6 mt-8 transition-all rounded-md cursor-pointer bg-slate-600 text-slate-50 hover:bg-slate-500/80"
@click="videoStore.downloadTempVideoDB()"
>
<span class="text-md font-medium">Download temporary video chunks</span>
</div>
</div>
</template>
</BaseConfigurationView>
Expand All @@ -134,7 +141,7 @@
<script setup lang="ts">
import { storeToRefs } from 'pinia'
import Swal from 'sweetalert2'
import { computed, onMounted, ref, watch } from 'vue'
import { computed, onMounted, ref, watch, watchEffect } from 'vue'
import type { VDataTable } from 'vuetify/components'
import Button from '@/components/Button.vue'
Expand All @@ -146,6 +153,20 @@ import BaseConfigurationView from './BaseConfigurationView.vue'
const videoStore = useVideoStore()
const { allowedIceIps, availableIceIps } = storeToRefs(videoStore)
// Define dialog as video library only
const props = defineProps<{
/**
*
*/
asVideoLibrary?: boolean
}>()
const isVideoLibraryOnly = ref(props.asVideoLibrary)
watchEffect(() => {
isVideoLibraryOnly.value = props.asVideoLibrary ?? false
})
// List available videos and telemetry logs to be downloaded
/* eslint-disable jsdoc/require-jsdoc */
interface VideoStorageFile {
Expand Down

0 comments on commit 5099a9c

Please sign in to comment.