Skip to content

Commit 2fe3a98

Browse files
committed
feat: much nicer recording view
1 parent 39f0c77 commit 2fe3a98

File tree

1 file changed

+67
-77
lines changed

1 file changed

+67
-77
lines changed

ui/src/views/RecordingView.vue

Lines changed: 67 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
<script setup lang="ts">
2-
import { computed } from 'vue';
2+
import { computed, ref } from 'vue';
33
import { useRouter } from 'vue-router';
44
import { useStreamStore } from '@/stores/stream';
5+
import { ArrowLeft, Download, Maximize } from 'lucide-vue-next';
56
67
const router = useRouter();
78
@@ -14,88 +15,77 @@ const props = defineProps<{
1415
}>();
1516
1617
const recording = computed(() => streamStore.recordings.find(r => r.id === props.recording));
18+
const videoContainer = ref<HTMLDivElement | null>(null);
19+
20+
const toggleFullscreen = () => {
21+
if (!videoContainer.value) return;
22+
23+
if (!document.fullscreenElement) {
24+
videoContainer.value.requestFullscreen();
25+
} else {
26+
document.exitFullscreen();
27+
}
28+
};
1729
</script>
1830

1931
<template>
20-
<div class="h-full flex flex-col">
21-
<div class="relative h-full flex flex-col">
22-
<div class="absolute top-4 right-4 z-10">
23-
<button @click.prevent="router.push({ name: 'recordings' })" class="bg-gray-900 text-white p-2 rounded cursor-pointer">
24-
25-
</button>
26-
</div>
27-
28-
<div class="flex-1 bg-gray-700 flex items-center justify-center">
29-
<div class="w-full h-full flex items-center justify-center bg-gray-700">
30-
<video
31-
v-if="recording"
32-
style="max-height: 80vh"
33-
:src="recording.path"
34-
muted
35-
controls
36-
autoplay
37-
/>
38-
</div>
39-
</div>
40-
41-
<!-- <div class="h-16 bg-gray-800 flex items-center justify-between px-4">
42-
<div class="flex items-center gap-4">
43-
<button class="text-white">
44-
⏸️
45-
</button>
46-
<button class="text-white">
47-
🔇
48-
</button>
49-
<div class="flex items-center text-white gap-1">
50-
<span>00:22</span>
51-
<span>/</span>
52-
<span>04:12</span>
53-
</div>
54-
<div class="w-64 h-1 bg-gray-600 rounded-full overflow-hidden">
55-
<div class="w-1/4 h-full bg-nvrblue"></div>
32+
<div class="h-full flex flex-col bg-gray-900">
33+
<div class="absolute top-4 left-4 z-10 flex gap-2">
34+
<button
35+
@click="router.push({ name: 'recordings' })"
36+
class="bg-gray-900/80 hover:bg-gray-900 text-white p-3 rounded-md cursor-pointer transition-colors backdrop-blur-sm"
37+
title="Back to Recordings"
38+
>
39+
<ArrowLeft :size="20" />
40+
</button>
41+
</div>
42+
43+
<div class="absolute top-4 right-4 z-10 flex gap-2">
44+
<a
45+
v-if="recording"
46+
:href="recording.path"
47+
download
48+
class="bg-gray-900/80 hover:bg-gray-900 text-white p-3 rounded-md cursor-pointer transition-colors backdrop-blur-sm"
49+
title="Download Recording"
50+
>
51+
<Download :size="20" />
52+
</a>
53+
<button
54+
@click="toggleFullscreen"
55+
class="bg-gray-900/80 hover:bg-gray-900 text-white p-3 rounded-md cursor-pointer transition-colors backdrop-blur-sm"
56+
title="Toggle Fullscreen"
57+
>
58+
<Maximize :size="20" />
59+
</button>
60+
</div>
61+
62+
<div v-if="recording" class="flex-1 flex flex-col items-center justify-center relative">
63+
<div ref="videoContainer" class="w-full h-full max-w-7xl bg-black relative">
64+
<video
65+
class="w-full h-full"
66+
:src="recording.path"
67+
muted
68+
controls
69+
autoplay
70+
/>
71+
72+
<div class="absolute bottom-16 left-4 z-20 bg-gray-900/90 text-white px-4 py-2 rounded-md backdrop-blur-sm max-w-[calc(100%-2rem)]">
73+
<h2 class="text-lg font-medium mb-1">{{ recording.stream_name }}</h2>
74+
<div class="flex flex-col sm:flex-row sm:items-center gap-1 sm:gap-2 text-sm text-gray-300">
75+
<RouterLink :to="`/cameras/${recording.stream_id}`" class="hover:underline">
76+
View Camera
77+
</RouterLink>
78+
<span class="hidden sm:inline">•</span>
79+
<span>{{ (new Date(recording.start)).toLocaleString() }}</span>
80+
<span class="hidden sm:inline">•</span>
81+
<span>{{ Math.round(((new Date(recording.end)).getTime() - (new Date(recording.start)).getTime()) / (1000 * 60)) }} min</span>
5682
</div>
5783
</div>
58-
59-
<div class="flex items-center gap-2">
60-
<button class="text-white">
61-
📺
62-
</button>
63-
</div>
64-
</div> -->
65-
66-
<div v-if="recording" class="bg-gray-900 text-white p-4">
67-
<div class="flex items-center justify-between">
68-
<h3 class="text-lg font-medium">Info</h3>
69-
<button>▼</button>
70-
</div>
71-
72-
<div class="grid grid-cols-2 gap-y-2 mt-4">
73-
<div>Stream</div>
74-
<div class="text-right">{{ recording.stream_name }}</div>
75-
76-
<div>Date/Time</div>
77-
<div class="text-right">{{ (new Date(recording.start)).toLocaleString() }}</div>
78-
79-
<div>Duration</div>
80-
<div class="text-right">{{ Math.round(((new Date(recording.end)).getTime() - (new Date(recording.start)).getTime()) / (1000 * 60)) }} minutes</div>
81-
82-
<!--
83-
<div>Unlocked</div>
84-
<div class="text-right flex items-center justify-end">
85-
<div class="h-6 w-6 rounded bg-gray-700 mr-1"></div>
86-
<button class="h-6 w-6 rounded bg-gray-700">🔓</button>
87-
</div> -->
88-
</div>
89-
</div>
90-
91-
<div v-if="recording" class="p-4 bg-gray-100 flex justify-end gap-2">
92-
<a class="nvr-button bg-blue-500 flex items-center gap-2 py-2" :href="recording.path" download>
93-
⬇️ DOWNLOAD
94-
</a>
95-
<!-- <button class="border border-gray-300 px-4 py-2 rounded flex items-center gap-2 text-sm">
96-
🗑️ DELETE
97-
</button> -->
9884
</div>
9985
</div>
86+
87+
<div v-else class="flex-1 flex items-center justify-center text-white">
88+
<p>Recording not found</p>
89+
</div>
10090
</div>
10191
</template>

0 commit comments

Comments
 (0)