Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve video download page more #752

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/libs/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,15 @@ export const isValidNetworkAddress = (maybeAddress: string): boolean => {
}
return false
}

export const formatBytes = (bytes: number, decimals = 2): string => {
if (!bytes) return '0 Bytes'

const k = 1024
const dm = decimals < 0 ? 0 : decimals
const sizes = ['Bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']

const i = Math.floor(Math.log(bytes) / Math.log(k))

return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`
}
23 changes: 21 additions & 2 deletions src/stores/video.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,9 +257,26 @@ export const useVideoStore = defineStore('video', () => {

// Used to clear the temporary video database
const clearTemporaryVideoDB = async (): Promise<void> => {
await tempVideoChunksDB.iterate((_, chunkName) => {
tempVideoChunksDB.removeItem(chunkName)
const tempChunks: string[] = []
await videoStoringDB.iterate((_, name) => {
tempChunks.push(name)
})
for (const chunk of tempChunks) {
await videoStoringDB.removeItem(chunk)
}
}

const temporaryVideoDBSize = async (): Promise<number> => {
let totalSizeBytes = 0
await videoStoringDB.iterate((chunk) => {
totalSizeBytes += (chunk as Blob).size
})
return totalSizeBytes
}

const videoStorageFileSize = async (filename: string): Promise<number | undefined> => {
const file = await videoStoringDB.getItem(filename)
return file ? (file as Blob).size : undefined
}

// Used to store chunks of an ongoing recording, that will be merged into a video file when the recording is stopped
Expand Down Expand Up @@ -409,6 +426,8 @@ export const useVideoStore = defineStore('video', () => {
discardFilesFromVideoDB,
downloadFilesFromVideoDB,
clearTemporaryVideoDB,
temporaryVideoDBSize,
videoStorageFileSize,
getMediaStream,
getStreamData,
isRecording,
Expand Down
46 changes: 31 additions & 15 deletions src/views/ConfigurationVideoView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
/>
</div>

<div v-if="namesAvailableVideosAndLogs.isEmpty()" class="max-w-[50%] bg-slate-100 rounded-md p-6 border">
<div v-if="availableVideosAndLogs.isEmpty()" 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 @@ -42,6 +42,7 @@
<fwb-table-head>
<fwb-table-head-cell />
<fwb-table-head-cell>Filename</fwb-table-head-cell>
<fwb-table-head-cell>Size</fwb-table-head-cell>
<fwb-table-head-cell>
<span
v-if="!selectedFilesNames.isEmpty()"
Expand All @@ -58,41 +59,43 @@
</fwb-table-head-cell>
</fwb-table-head>
<fwb-table-body>
<fwb-table-row v-for="filename in namesAvailableVideosAndLogs" :key="filename">
<fwb-table-row v-for="file in availableVideosAndLogs" :key="file.filename">
<fwb-table-cell>
<input
v-model="selectedFilesNames"
:value="filename"
:value="file.filename"
type="checkbox"
class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600"
/>
</fwb-table-cell>
<fwb-table-cell>{{ filename }}</fwb-table-cell>
<fwb-table-cell>{{ file.filename }}</fwb-table-cell>
<fwb-table-cell>{{ formatBytes(file.size) }}</fwb-table-cell>
<fwb-table-cell>
<span
v-if="selectedFilesNames.isEmpty()"
class="rounded-md cursor-pointer hover:text-slate-500/50 mdi mdi-trash-can"
@click="discardAndUpdateDB([filename])"
@click="discardAndUpdateDB([file.filename])"
/>
</fwb-table-cell>
<fwb-table-cell>
<span
v-if="selectedFilesNames.isEmpty()"
class="rounded-md cursor-pointer hover:text-slate-500/50 mdi mdi-download"
@click="downloadAndUpdateDB([filename])"
@click="downloadAndUpdateDB([file.filename])"
/>
</fwb-table-cell>
</fwb-table-row>
</fwb-table-body>
</fwb-table>
<span
<div
v-if="temporaryDbSize > 0"
v-tooltip.bottom="'Remove video files used during the recording. This will not affect already saved videos.'"
class="p-4 m-4 transition-all rounded-md cursor-pointer bg-slate-600 text-slate-50 hover:bg-slate-500/80"
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()"
>
Clear temporary video storage
</span>
<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>
</template>
</BaseConfigurationView>
</template>
Expand All @@ -103,6 +106,7 @@ import { storeToRefs } from 'pinia'
import { ref } from 'vue'
import { onMounted } from 'vue'

import { formatBytes } from '@/libs/utils'
import { useVideoStore } from '@/stores/video'

import BaseConfigurationView from './BaseConfigurationView.vue'
Expand All @@ -111,7 +115,13 @@ const videoStore = useVideoStore()
const { allowedIceIps, availableIceIps } = storeToRefs(videoStore)

// List available videos and telemetry logs to be downloaded
const namesAvailableVideosAndLogs = ref<string[]>([])
/* eslint-disable jsdoc/require-jsdoc */
interface VideoStorageFile {
filename: string
size: number
}
/* eslint-enable jsdoc/require-jsdoc */
const availableVideosAndLogs = ref<VideoStorageFile[]>([])
const temporaryDbSize = ref(0)
const selectedFilesNames = ref<string[]>([])

Expand All @@ -122,16 +132,22 @@ onMounted(async () => {

// Fetch available videos and telemetry logs from the storage
const fetchVideoAndLogsData = async (): Promise<void> => {
const availableData: string[] = []
const availableData: VideoStorageFile[] = []
await videoStore.videoStoringDB.iterate((_, fileName) => {
availableData.push(fileName)
availableData.push({
filename: fileName,
size: 0,
})
})
namesAvailableVideosAndLogs.value = availableData
for (const file of availableData) {
file.size = (await videoStore.videoStorageFileSize(file.filename)) ?? 0
}
availableVideosAndLogs.value = availableData
}

// Fetch temporary video data from the storage
const fetchTemporaryDbSize = async (): Promise<void> => {
const size = await videoStore.tempVideoChunksDB.length()
const size = await videoStore.temporaryVideoDBSize()
temporaryDbSize.value = size
}

Expand Down
Loading