Skip to content

Commit ca5a1b3

Browse files
video: Adapt video-related components for the new recording and processing pipeline
1 parent e7b52b8 commit ca5a1b3

8 files changed

Lines changed: 1742 additions & 683 deletions

File tree

src/components/VideoLibraryModal.vue

Lines changed: 688 additions & 630 deletions
Large diffs are not rendered by default.

src/components/mini-widgets/MiniVideoRecorder.vue

Lines changed: 58 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,14 @@
44
class="flex justify-around px-2 py-1 text-center rounded-lg w-40 h-9 align-center bg-slate-800/60"
55
>
66
<div
7-
v-if="!isProcessingVideo"
87
:class="{
98
'blob red w-5 opacity-100 rounded-sm': isRecording,
109
'opacity-30 bg-red-400': isOutside && !isRecording,
1110
}"
1211
class="w-6 transition-all duration-500 rounded-full aspect-square bg-red-lighten-1 hover:cursor-pointer opacity-70 hover:opacity-90"
1312
@click="toggleRecording()"
1413
/>
15-
<div v-else>
16-
<v-icon class="w-6 h-6 animate-spin" color="white">mdi-loading</v-icon>
17-
</div>
18-
<template v-if="!isRecording && !isProcessingVideo">
14+
<template v-if="!isRecording">
1915
<div
2016
v-if="nameSelectedStream"
2117
class="flex flex-col max-w-[50%] scroll-container transition-all border-blur cursor-pointer"
@@ -25,12 +21,9 @@
2521
</div>
2622
<FontAwesomeIcon v-else icon="fa-solid fa-video" class="h-6 text-slate-100" />
2723
</template>
28-
<div v-if="isRecording && !isProcessingVideo" class="w-16 text-justify text-slate-100">
24+
<div v-if="isRecording" class="w-16 text-justify text-slate-100">
2925
{{ timePassedString }}
3026
</div>
31-
<div v-else-if="isProcessingVideo" class="w-16 text-justify text-slate-100">
32-
<div class="text-xs text-center text-white select-none flex-nowrap">Processing video...</div>
33-
</div>
3427
<div class="flex justify-center w-6">
3528
<v-divider vertical class="h-6 ml-1" />
3629
<v-badge
@@ -125,7 +118,6 @@ const isVideoLibraryDialogOpen = ref(false)
125118
const isLoadingStream = ref(false)
126119
const timeNow = useTimestamp({ interval: 100 })
127120
const mediaStream = ref<MediaStream | undefined>()
128-
const isProcessingVideo = ref(false)
129121
const numberOfVideosOnDB = ref(0)
130122
const selectedExternalId = ref<string | undefined>()
131123
@@ -191,12 +183,63 @@ watch(nameSelectedStream, (newName) => {
191183
mediaStream.value = undefined
192184
})
193185
194-
// Fetch number of temporary videos on storage
186+
// Fetch number of videos on storage (chunk groups + processed videos without overlap)
195187
const fetchNumberOfTempVideos = async (): Promise<void> => {
196-
const keys = await videoStore.videoStorage.keys()
197-
const nProcessedVideos = keys.filter((k) => videoStore.isVideoFilename(k)).length
198-
const nFailedUnprocessedVideos = Object.keys(videoStore.keysFailedUnprocessedVideos).length
199-
numberOfVideosOnDB.value = nProcessedVideos + nFailedUnprocessedVideos
188+
// Get processed videos from videoStorage
189+
const processedKeys = await videoStore.videoStorage.keys()
190+
const processedVideos = processedKeys.filter((k) => videoStore.isVideoFilename(k))
191+
192+
// Get chunk groups from tempVideoStorage
193+
const tempKeys = await videoStore.tempVideoStorage.keys()
194+
const chunkGroups: Set<string> = new Set()
195+
196+
for (const key of tempKeys) {
197+
if (key.includes('thumbnail_')) continue
198+
199+
const parts = key.split('_')
200+
if (parts.length < 2) continue
201+
202+
const hash = parts[0]
203+
const chunkNumber = parseInt(parts[parts.length - 1], 10)
204+
if (isNaN(chunkNumber)) continue
205+
206+
// Check if this chunk actually exists and has content
207+
try {
208+
const blob = (await videoStore.tempVideoStorage.getItem(key)) as Blob
209+
if (blob && blob.size > 0) {
210+
chunkGroups.add(hash)
211+
}
212+
} catch (error) {
213+
console.warn(`Failed to check chunk ${key}:`, error)
214+
}
215+
}
216+
217+
// Count processed videos that don't have corresponding chunk groups
218+
const processedVideoHashes = new Set<string>()
219+
for (const videoKey of processedVideos) {
220+
// Extract hash from processed video filename
221+
// Processed videos have format like "MissionName (Date) #hash.webm"
222+
// We need to extract the hash after the # symbol
223+
const hashMatch = videoKey.match(/#([a-f0-9]+)\./)
224+
if (hashMatch && hashMatch[1]) {
225+
processedVideoHashes.add(hashMatch[1])
226+
}
227+
}
228+
229+
// Count unique videos: chunk groups + processed videos that don't have chunk groups
230+
const uniqueVideos = new Set<string>()
231+
232+
// Add all chunk groups
233+
chunkGroups.forEach((hash) => uniqueVideos.add(hash))
234+
235+
// Add processed videos that don't have corresponding chunk groups
236+
processedVideoHashes.forEach((hash) => {
237+
if (!chunkGroups.has(hash)) {
238+
uniqueVideos.add(hash)
239+
}
240+
})
241+
242+
numberOfVideosOnDB.value = uniqueVideos.size
200243
}
201244
202245
// eslint-disable-next-line jsdoc/require-jsdoc
@@ -312,20 +355,6 @@ if (widgetStore.isRealMiniWidget(miniWidget.value.hash)) {
312355
}
313356
onBeforeUnmount(() => clearInterval(streamConnectionRoutine))
314357
315-
// Check if there are videos being processed
316-
watch(
317-
() => videoStore.areThereVideosProcessing,
318-
(newValue) => {
319-
isProcessingVideo.value = newValue
320-
fetchNumberOfTempVideos()
321-
}
322-
)
323-
324-
watch(
325-
() => videoStore.keysFailedUnprocessedVideos,
326-
() => fetchNumberOfTempVideos()
327-
)
328-
329358
watch(
330359
() => isVideoLibraryDialogOpen.value,
331360
async (newValue) => {

0 commit comments

Comments
 (0)