All API through Rust proxy, fix unregistered files null uuid, People enhancements

This commit is contained in:
2026-06-14 11:05:23 +08:00
parent a700cc983e
commit 3ba2120c8e
8 changed files with 265 additions and 45 deletions

View File

@@ -64,7 +64,7 @@
<div v-for="f in sortedFilteredFiles" :key="f.file_uuid" class="mp-file-card" :class="{ 'is-completed': f.is_registered, 'is-selected': selectedFiles.includes(f.file_uuid) }" @click="toggleSelect(f)">
<div class="mp-thumb-wrap">
<div class="mp-badge-type">{{ isVideo(f) ? 'VIDEO' : (isPhoto(f) ? 'PHOTO' : 'DOC') }}</div>
<img v-if="isPhoto(f) || isVideo(f)" class="lt-thumb" :src="getThumbUrl(f.file_uuid)" alt="" loading="lazy" @error="handleThumbError">
<img v-if="isPhoto(f) || isVideo(f)" class="lt-thumb" :src="thumbnails[f.file_uuid]" alt="" loading="lazy" @error="handleThumbError" @vue:mounted="loadThumbnail(f.file_uuid)">
<div v-else class="mp-doc-thumb">
<span class="mp-doc-icon">📄</span>
<span class="mp-doc-ext">{{ getFileExt(f.file_name) }}</span>
@@ -85,7 +85,7 @@
</template>
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { ref, computed, onMounted, onUnmounted } from 'vue'
import { invoke } from '@tauri-apps/api/core'
import VideoPlayer from '../components/VideoPlayer.vue'
@@ -99,10 +99,12 @@ const selectedFiles = ref<string[]>([])
const playing = ref(false)
const currentVideo = ref({ fileUuid: '', startTime: 0, endTime: 0, title: '' })
// Sort & Filter
const thumbnails = ref<Record<string, string>>({})
const thumbnailLoading = ref<Set<string>>(new Set())
const sortBy = ref('time_desc')
const filterUnregistered = ref(false)
const filterRegistered = ref(false)
const filterRegistered = ref(true)
const onlyVideos = ref(false)
const onlyPhotos = ref(false)
const sizeMin = ref<number | null>(null)
@@ -110,9 +112,7 @@ const sizeMax = ref<number | null>(null)
const durationMin = ref<number | null>(null)
const durationMax = ref<number | null>(null)
const CORE_API = 'http://localhost:3002'
const API_KEY = 'muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69'
// Sort & Filter
const sortedFilteredFiles = computed(() => {
let result = [...files.value]
@@ -127,8 +127,8 @@ const sortedFilteredFiles = computed(() => {
}
// Checkbox filters
if (filterUnregistered.value && !filterRegistered.value) result = result.filter((f: any) => !f.is_registered)
else if (filterRegistered.value && !filterUnregistered.value) result = result.filter((f: any) => f.is_registered)
if (filterUnregistered.value && !filterRegistered.value) result = result.filter((f: any) => !f.isRegistered)
else if (filterRegistered.value && !filterUnregistered.value) result = result.filter((f: any) => f.isRegistered)
// 兩者都不勾選或都勾選:顯示全部
if (onlyVideos.value) result = result.filter(isVideo)
@@ -172,8 +172,8 @@ async function loadFiles() {
statusText.value = 'Loading...'
try {
console.log('Calling get_files...')
files.value = await invoke('get_files', { page_size: 500 })
console.log('Files loaded:', files.value.length)
files.value = await invoke('get_files', { args: { pageSize: 500 } })
console.log('Files loaded:', files.value.length); console.log('First file:', JSON.stringify(files.value[0]))
if (files.value.length > 0) {
console.log('First file:', files.value[0])
console.log('is_registered sample:', files.value.slice(0,5).map((f:any) => ({ name: f.file_name?.slice(0,20), is_registered: f.is_registered, type: typeof f.is_registered })))
@@ -209,8 +209,16 @@ function formatDate(date: string) {
return new Date(date).toISOString().slice(0, 10)
}
function getThumbUrl(uuid: string) {
return `${CORE_API}/api/v1/file/${uuid}/thumbnail?api_key=${API_KEY}&frame=30`
async function loadThumbnail(uuid: string) {
if (thumbnails.value[uuid] || thumbnailLoading.value.has(uuid)) return
thumbnailLoading.value.add(uuid)
try {
thumbnails.value[uuid] = await invoke('get_thumbnail', { uuid, frame: 30 })
} catch (e: any) {
console.error('Thumbnail load failed:', e)
} finally {
thumbnailLoading.value.delete(uuid)
}
}
function handleThumbError(e: Event) {