feat: add real-time files watch for LibraryView
Implement comprehensive monitoring system: - Watch for new registered files (5s interval) - Detect file status changes (registered → processing → completed) - Monitor file additions/removals Functions added: - startFilesWatch(): Poll get_files API every 5s - stopFilesWatch(): Clean up interval LibraryView integration: - Enable on mount - Disable on unmount Enables cross-machine monitoring: one operates, another watches changes
This commit is contained in:
45
src/store.ts
45
src/store.ts
@@ -352,6 +352,7 @@ const loadingFileStats = new Set<string>()
|
|||||||
|
|
||||||
let statsRefreshInterval: ReturnType<typeof setInterval> | null = null
|
let statsRefreshInterval: ReturnType<typeof setInterval> | null = null
|
||||||
let processingPollInterval: ReturnType<typeof setInterval> | null = null
|
let processingPollInterval: ReturnType<typeof setInterval> | null = null
|
||||||
|
let filesWatchInterval: ReturnType<typeof setInterval> | null = null
|
||||||
|
|
||||||
export function startStatsAutoRefresh() {
|
export function startStatsAutoRefresh() {
|
||||||
if (statsRefreshInterval) return
|
if (statsRefreshInterval) return
|
||||||
@@ -389,6 +390,50 @@ export function stopProcessingFilesPolling() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function startFilesWatch() {
|
||||||
|
if (filesWatchInterval) return
|
||||||
|
filesWatchInterval = setInterval(async () => {
|
||||||
|
try {
|
||||||
|
const result = await apiCall('get_files', { args: { pageSize: 500 } })
|
||||||
|
const newFiles = Array.isArray(result) ? result : []
|
||||||
|
|
||||||
|
// Check for new files
|
||||||
|
const oldUuids = new Set(filesCache.value.map((f: any) => f.file_uuid).filter(Boolean))
|
||||||
|
const newUuids = newFiles.map((f: any) => f.file_uuid).filter(Boolean)
|
||||||
|
|
||||||
|
// Detect new registered files
|
||||||
|
const added = newUuids.filter((uuid: string) => !oldUuids.has(uuid))
|
||||||
|
const removed = Array.from(oldUuids).filter((uuid: string) => !newUuids.includes(uuid))
|
||||||
|
|
||||||
|
if (added.length > 0 || removed.length > 0) {
|
||||||
|
filesCache.value = newFiles
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check status changes for existing files
|
||||||
|
for (const newFile of newFiles) {
|
||||||
|
if (!newFile.file_uuid) continue
|
||||||
|
const oldFile = filesCache.value.find((f: any) => f.file_uuid === newFile.file_uuid)
|
||||||
|
if (oldFile && oldFile.status !== newFile.status) {
|
||||||
|
// Status changed, update cache
|
||||||
|
const idx = filesCache.value.findIndex((f: any) => f.file_uuid === newFile.file_uuid)
|
||||||
|
if (idx >= 0) {
|
||||||
|
filesCache.value[idx] = newFile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[startFilesWatch] Error:', e)
|
||||||
|
}
|
||||||
|
}, 5000)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function stopFilesWatch() {
|
||||||
|
if (filesWatchInterval) {
|
||||||
|
clearInterval(filesWatchInterval)
|
||||||
|
filesWatchInterval = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function drainThumbQueue() {
|
function drainThumbQueue() {
|
||||||
if (activeThumbLoads >= MAX_CONCURRENT || thumbQueue.length === 0) return
|
if (activeThumbLoads >= MAX_CONCURRENT || thumbQueue.length === 0) return
|
||||||
activeThumbLoads++
|
activeThumbLoads++
|
||||||
|
|||||||
@@ -571,7 +571,7 @@
|
|||||||
import { ref, computed, onMounted, onUnmounted, watch, nextTick } from 'vue'
|
import { ref, computed, onMounted, onUnmounted, watch, nextTick } from 'vue'
|
||||||
import { apiCall } from '@/api'
|
import { apiCall } from '@/api'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { ensureFiles, filesCache, filesLoaded, thumbnailsCache, loadThumbnail, loadUnregisteredThumbnail, invalidateFiles, processorCountsCache, loadProcessorCounts, getProcessorCounts, ProcessorOutputInfo, pollProgress, stopPolling, getProgress, refreshFileStatus, refreshAllFilesStatus, fileProgressCache, pollingProgressSet, getFileProcessingStatus, ProcessingStatus, ProcessingLevel, loadPipelineStats, getPipelineStats, PipelineStats, loadFileStats, getFileStats, FileStats, pollPipelineProgress, startStatsAutoRefresh, stopStatsAutoRefresh, startProcessingFilesPolling, stopProcessingFilesPolling, JsonProcessorInfo, pipelineStatsCache, clearFileStatusCache } from '@/store'
|
import { ensureFiles, filesCache, filesLoaded, thumbnailsCache, loadThumbnail, loadUnregisteredThumbnail, invalidateFiles, processorCountsCache, loadProcessorCounts, getProcessorCounts, ProcessorOutputInfo, pollProgress, stopPolling, getProgress, refreshFileStatus, refreshAllFilesStatus, fileProgressCache, pollingProgressSet, getFileProcessingStatus, ProcessingStatus, ProcessingLevel, loadPipelineStats, getPipelineStats, PipelineStats, loadFileStats, getFileStats, FileStats, pollPipelineProgress, startStatsAutoRefresh, stopStatsAutoRefresh, startProcessingFilesPolling, stopProcessingFilesPolling, startFilesWatch, stopFilesWatch, JsonProcessorInfo, pipelineStatsCache, clearFileStatusCache } from '@/store'
|
||||||
import { isDark, toggleDark } from '@/composables/useTheme'
|
import { isDark, toggleDark } from '@/composables/useTheme'
|
||||||
import { getAsrStatus } from '@/utils/asrStatus'
|
import { getAsrStatus } from '@/utils/asrStatus'
|
||||||
import { useAdvancedMode } from '@/stores/advancedMode'
|
import { useAdvancedMode } from '@/stores/advancedMode'
|
||||||
@@ -753,8 +753,8 @@ const pendingCount = computed(() => files.value.filter((f: any) => f.isRegistere
|
|||||||
const processingCount = computed(() => files.value.filter((f: any) => f.status === 'processing').length)
|
const processingCount = computed(() => files.value.filter((f: any) => f.status === 'processing').length)
|
||||||
const completedCount = computed(() => files.value.filter((f: any) => f.status === 'completed').length)
|
const completedCount = computed(() => files.value.filter((f: any) => f.status === 'completed').length)
|
||||||
|
|
||||||
onMounted(() => { loadFiles(); document.addEventListener('click', docClickClose); startStatsAutoRefresh(); startProcessingFilesPolling() })
|
onMounted(() => { loadFiles(); document.addEventListener('click', docClickClose); startStatsAutoRefresh(); startProcessingFilesPolling(); startFilesWatch() })
|
||||||
onUnmounted(() => { document.removeEventListener('click', docClickClose); stopStatsAutoRefresh(); stopProcessingFilesPolling() })
|
onUnmounted(() => { document.removeEventListener('click', docClickClose); stopStatsAutoRefresh(); stopProcessingFilesPolling(); stopFilesWatch() })
|
||||||
|
|
||||||
function docClickClose(e: MouseEvent) {
|
function docClickClose(e: MouseEvent) {
|
||||||
if (e.target instanceof Element && e.target.closest('.ms-ctx-menu')) return
|
if (e.target instanceof Element && e.target.closest('.ms-ctx-menu')) return
|
||||||
|
|||||||
Reference in New Issue
Block a user