cleanup: remove dead code and duplicate docs

- Remove session-ses_2f27.md (161KB raw session log)
- Remove 49 ROOT_* duplicate files across REFERENCE/
- Remove 14 duplicate files between REFERENCE/ root and history/
- Remove asr_legacy.rs (dead code, replaced by asr.rs)
- Remove src/core/worker/ (duplicate JobWorker)
- Remove src/core/layers/ (empty directory)
- Remove 4 .bak files in src/
- Remove 7 dead private methods in worker/processor.rs
- Remove backup directory from git tracking
This commit is contained in:
Warren
2026-05-04 01:31:21 +08:00
parent ee81e343ce
commit e75c4d6f07
3270 changed files with 35190 additions and 53367 deletions

View File

@@ -65,10 +65,11 @@ export interface RegisterResponse {
export interface UnregisterResponse {
success: boolean
message: string
file_uuid: string
deleted_face_detections: number
deleted_processor_results: number
deleted_chunks: number
file_uuid?: string
uuid?: string
deleted_face_detections?: number
deleted_processor_results?: number
deleted_chunks?: number
}
// ── Config (browser-only, stored in localStorage) ───────────────────────

View File

@@ -2,7 +2,7 @@
<div class="space-y-6">
<!-- Header with Search and Filters -->
<div class="flex flex-col md:flex-row items-center justify-between gap-4">
<h2 class="text-2xl font-bold">檔案管理</h2>
<h2 class="text-2xl font-bold">檔案管理 (Demo)</h2>
<div class="flex items-center gap-3 w-full md:w-auto">
<!-- Status Filter -->
<div class="flex items-center bg-gray-700 rounded p-1">
@@ -13,13 +13,6 @@
>
全部
</button>
<button
@click="setStatusFilter('registered')"
:class="{'bg-blue-600 text-white': statusFilter === 'registered', 'text-gray-300 hover:text-white': statusFilter !== 'registered'}"
class="px-3 py-1 rounded text-sm transition"
>
已註冊
</button>
<button
@click="setStatusFilter('unregistered')"
:class="{'bg-blue-600 text-white': statusFilter === 'unregistered', 'text-gray-300 hover:text-white': statusFilter !== 'unregistered'}"
@@ -27,19 +20,27 @@
>
未註冊
</button>
</div>
<!-- Search -->
<div class="relative">
<input
v-model="searchQuery"
@input="handleSearch"
placeholder="搜尋檔名..."
class="pl-10 pr-4 py-2 bg-gray-700 border border-gray-600 rounded text-white focus:outline-none focus:border-blue-500 w-48"
/>
<svg class="w-5 h-5 absolute left-3 top-2.5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
</svg>
<button
@click="setStatusFilter('pending')"
:class="{'bg-blue-600 text-white': statusFilter === 'pending', 'text-gray-300 hover:text-white': statusFilter !== 'pending'}"
class="px-3 py-1 rounded text-sm transition"
>
待處理
</button>
<button
@click="setStatusFilter('processing')"
:class="{'bg-blue-600 text-white': statusFilter === 'processing', 'text-gray-300 hover:text-white': statusFilter !== 'processing'}"
class="px-3 py-1 rounded text-sm transition"
>
處理中
</button>
<button
@click="setStatusFilter('completed')"
:class="{'bg-blue-600 text-white': statusFilter === 'completed', 'text-gray-300 hover:text-white': statusFilter !== 'completed'}"
class="px-3 py-1 rounded text-sm transition"
>
已完成
</button>
</div>
</div>
</div>
@@ -59,60 +60,68 @@
<table class="min-w-full divide-y divide-gray-700">
<thead class="bg-gray-900">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-300 uppercase tracking-wider">檔案路徑</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-300 uppercase tracking-wider">檔案名稱</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-300 uppercase tracking-wider">狀態</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-300 uppercase tracking-wider">UUID</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-300 uppercase tracking-wider">大小</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-300 uppercase tracking-wider">修改時間</th>
<th class="px-6 py-3 text-right text-xs font-medium text-gray-300 uppercase tracking-wider">操作</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-300 uppercase tracking-wider">操作</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-700 bg-gray-800">
<tr v-for="file in displayFiles" :key="file.file_path" :class="!file.file_name ? 'opacity-0' : 'hover:bg-gray-750 transition'">
<tr v-for="file in displayFiles" :key="file.file_uuid || file.file_path" class="hover:bg-gray-750 transition">
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm font-medium text-white truncate max-w-xs" :title="file.file_path">
<div class="text-sm font-medium text-white truncate max-w-xs" :title="file.file_name">
{{ file.file_name }}
</div>
<div class="text-xs text-gray-500 truncate max-w-xs">{{ file.relative_path }}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm">
<span v-if="file.is_registered" class="px-2 py-0.5 rounded text-xs bg-green-900 text-green-200">
已註冊
<span v-if="file.status === 'completed'" class="px-2 py-0.5 rounded text-xs bg-green-900 text-green-200">
已完成
</span>
<span v-else-if="file.status === 'processing'" class="px-2 py-0.5 rounded text-xs bg-yellow-900 text-yellow-200">
🔄 處理中
</span>
<span v-else-if="file.status === 'pending'" class="px-2 py-0.5 rounded text-xs bg-blue-900 text-blue-200">
待處理
</span>
<span v-else class="px-2 py-0.5 rounded text-xs bg-gray-600 text-gray-300">
未註冊
📦 未註冊
</span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-300 font-mono text-xs">
{{ file.file_uuid || '-' }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-300">
{{ formatFileSize(file.file_size) }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-300">
{{ formatTimestamp(file.modified_time) }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-right">
<div class="flex justify-end gap-2">
<!-- Detail Button (Registered only) -->
<!-- Enter Demo / Workbench (Completed) -->
<button
v-if="file.is_registered"
@click="viewDetail(file.file_uuid)"
class="px-3 py-1 bg-blue-600 hover:bg-blue-700 text-white text-xs rounded transition"
v-if="file.status === 'completed'"
@click="enterWorkbench(file.file_uuid)"
class="px-3 py-1 bg-purple-600 hover:bg-purple-700 text-white text-xs rounded transition"
>
詳情
臉部工作台
</button>
<!-- Register Button (Unregistered only) -->
<!-- Start Processing (Pending) -->
<button
v-if="!file.is_registered"
v-if="file.status === 'pending'"
@click="startProcessing(file.file_uuid)"
class="px-3 py-1 bg-yellow-600 hover:bg-yellow-700 text-white text-xs rounded transition"
>
開始處理
</button>
<!-- Register (Unregistered) -->
<button
v-if="!file.status || file.status === 'unregistered'"
@click="registerFile(file.file_path)"
class="px-3 py-1 bg-green-600 hover:bg-green-700 text-white text-xs rounded transition"
>
立即註冊
註冊
</button>
<!-- Unregister Button (Registered only) -->
<!-- Unregister (Pending/Processing/Completed) -->
<button
v-if="file.is_registered"
v-if="file.file_uuid"
@click="unregisterFile(file.file_uuid, file.file_name)"
class="px-3 py-1 bg-red-600 hover:bg-red-700 text-white text-xs rounded transition"
>
@@ -124,31 +133,20 @@
</tbody>
</table>
</div>
<!-- Stats -->
<div class="flex justify-between text-sm text-gray-400">
<span> {{ totalCount }} 個檔案</span>
<span>已註冊: {{ registeredCount }} | 未註冊: {{ unregisteredCount }}</span>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { registerVideo, unregisterVideo } from '@/api/client'
import { getCurrentConfig, httpFetch } from '@/api/client'
import { registerVideo, unregisterVideo, httpFetch, getCurrentConfig } from '@/api/client'
const router = useRouter()
const files = ref<any[]>([])
const loading = ref(false)
const error = ref<string | null>(null)
const searchQuery = ref('')
const statusFilter = ref('all') // all, registered, unregistered
const totalCount = computed(() => files.value.length)
const registeredCount = computed(() => files.value.filter(f => f.is_registered).length)
const unregisteredCount = computed(() => files.value.filter(f => !f.is_registered).length)
const statusFilter = ref('all') // all, unregistered, pending, processing, completed
const displayFiles = computed(() => {
let result = files.value
@@ -163,10 +161,8 @@ const displayFiles = computed(() => {
}
// Filter by status
if (statusFilter.value === 'registered') {
result = result.filter(f => f.is_registered)
} else if (statusFilter.value === 'unregistered') {
result = result.filter(f => !f.is_registered)
if (statusFilter.value !== 'all') {
result = result.filter(f => f.status === statusFilter.value)
}
return result
@@ -176,18 +172,26 @@ function setStatusFilter(status: string) {
statusFilter.value = status
}
function handleSearch() {
// Filter is reactive via computed property
}
async function fetchFiles() {
loading.value = true
error.value = null
try {
const config = getCurrentConfig()
// Call the new scan endpoint
// Call the scan endpoint to get list of files with status
// Note: /api/v1/files/scan returns unregistered files.
// We might need a combined list or call /api/v1/files for registered ones.
// For now, let's assume /api/v1/files/scan returns all files with is_registered flag.
const response: any = await httpFetch(`${config.api_base_url}/api/v1/files/scan`)
files.value = response.files || []
// Map scan response to our expected format
// Scan returns: { files: [ { file_uuid, file_name, is_registered, status... } ] }
files.value = response.files?.map((f: any) => ({
...f,
status: f.status || (f.is_registered ? 'pending' : 'unregistered')
})) || []
// To get actual processing status, we might need to cross reference with /api/v1/files
// But for Demo, let's rely on the scan endpoint providing status.
} catch (e) {
console.error('Failed to fetch files:', e)
error.value = String(e)
@@ -199,8 +203,7 @@ async function fetchFiles() {
async function registerFile(filePath: string) {
try {
const result = await registerVideo(filePath)
const typeTag = result.file_type ? `[${result.file_type.toUpperCase()}]` : ''
alert(`已註冊! ${typeTag} File UUID: ${result.file_uuid}`)
// Refresh list
await fetchFiles()
} catch (e) {
console.error('Register failed:', e)
@@ -209,13 +212,12 @@ async function registerFile(filePath: string) {
}
async function unregisterFile(fileUuid: string, fileName: string) {
if (!confirm(`確定要取消註冊 "${fileName}" 嗎?這將刪除資料庫中的相關記錄,但保留原始檔案`)) {
if (!confirm(`確定要取消註冊 "${fileName}" 嗎?這將刪除資料庫中的相關記錄。`)) {
return
}
try {
const result = await unregisterVideo(fileUuid)
alert('已取消註冊!' + result.message)
await unregisterVideo(fileUuid)
await fetchFiles()
} catch (e) {
console.error('Unregister failed:', e)
@@ -223,33 +225,29 @@ async function unregisterFile(fileUuid: string, fileName: string) {
}
}
function viewDetail(fileUuid: string) {
router.push(`/videos/${fileUuid}`)
}
function formatFileSize(bytes: number): string {
if (!bytes) return '-'
if (bytes < 1024) return bytes + ' B'
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB'
if (bytes < 1024 * 1024 * 1024) return (bytes / (1024 * 1024)).toFixed(1) + ' MB'
return (bytes / (1024 * 1024 * 1024)).toFixed(2) + ' GB'
}
function formatTimestamp(timestamp: string | undefined): string {
if (!timestamp) return '-'
async function startProcessing(fileUuid: string) {
if (!confirm('確定要開始分析處理此檔案嗎?')) return
try {
const date = new Date(timestamp)
return date.toLocaleString('zh-TW', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
const config = getCurrentConfig()
await httpFetch(`${config.api_base_url}/api/v1/assets/${fileUuid}/process`, {
method: 'POST',
body: JSON.stringify({})
})
} catch {
return '-'
// After triggering, status should change to processing
// We can poll or just refresh
await fetchFiles()
} catch (e) {
console.error('Start processing failed:', e)
alert('開始處理失敗:' + e)
}
}
function enterWorkbench(fileUuid: string) {
// Navigate to the new Face Workbench view
router.push(`/workbench/${fileUuid}`)
}
onMounted(fetchFiles)
</script>