OmniVoice-Studio / frontend /src /hooks /useAppData.js
LΓͺ Phi Nam
Deploy to HF Space
94004a2
Raw
History Blame Contribute Delete
9.21 kB
import { useState, useEffect, useCallback, useRef } from 'react';
import { useAppStore } from '../store';
import { listProfiles } from '../api/profiles';
import { listHistory } from '../api/generate';
import { listProjects } from '../api/projects';
import { listDubHistory } from '../api/dub';
import { listExportHistory, exportAction, exportReveal, exportRecord } from '../api/exports';
import { modelStatus as apiModelStatus } from '../api/system';
import { useSysinfo, useModelStatus } from '../api/hooks';
import useRealtimeEvents from './useRealtimeEvents';
import { isTauri, fileToMediaUrl } from '../utils/media';
import { toast } from 'react-hot-toast';
/**
* Encapsulates all data-loading effects, localStorage persistence,
* real-time WebSocket updates, and model-status pill management.
*/
export default function useAppData() {
const mode = useAppStore(s => s.mode);
const setMode = useAppStore(s => s.setMode);
const uiScale = useAppStore(s => s.uiScale);
const setUiScale = useAppStore(s => s.setUiScale);
const setText = useAppStore(s => s.setText);
const text = useAppStore(s => s.text);
const setLanguage = useAppStore(s => s.setLanguage);
const language = useAppStore(s => s.language);
const setIsSidebarCollapsed = useAppStore(s => s.setIsSidebarCollapsed);
const isSidebarCollapsed = useAppStore(s => s.isSidebarCollapsed);
const setSidebarTab = useAppStore(s => s.setSidebarTab);
const sidebarTab = useAppStore(s => s.sidebarTab);
const setVdStates = useAppStore(s => s.setVdStates);
const vdStates = useAppStore(s => s.vdStates);
const speed = useAppStore(s => s.speed);
const setSpeed = useAppStore(s => s.setSpeed);
const steps = useAppStore(s => s.steps);
const setSteps = useAppStore(s => s.setSteps);
const cfg = useAppStore(s => s.cfg);
const setCfg = useAppStore(s => s.setCfg);
const denoise = useAppStore(s => s.denoise);
const setDenoise = useAppStore(s => s.setDenoise);
const dubJobId = useAppStore(s => s.dubJobId);
const setDubJobId = useAppStore(s => s.setDubJobId);
const dubFilename = useAppStore(s => s.dubFilename);
const setDubFilename = useAppStore(s => s.setDubFilename);
const dubDuration = useAppStore(s => s.dubDuration);
const setDubDuration = useAppStore(s => s.setDubDuration);
const dubSegments = useAppStore(s => s.dubSegments);
const setDubSegments = useAppStore(s => s.setDubSegments);
const dubLang = useAppStore(s => s.dubLang);
const setDubLang = useAppStore(s => s.setDubLang);
const dubLangCode = useAppStore(s => s.dubLangCode);
const setDubLangCode = useAppStore(s => s.setDubLangCode);
const dubTracks = useAppStore(s => s.dubTracks);
const setDubTracks = useAppStore(s => s.setDubTracks);
const dubStep = useAppStore(s => s.dubStep);
const setDubStep = useAppStore(s => s.setDubStep);
const dubTranscript = useAppStore(s => s.dubTranscript);
const setDubTranscript = useAppStore(s => s.setDubTranscript);
const exportTracks = useAppStore(s => s.exportTracks);
const setExportTracks = useAppStore(s => s.setExportTracks);
const preserveBg = useAppStore(s => s.preserveBg);
const setPreserveBg = useAppStore(s => s.setPreserveBg);
const defaultTrack = useAppStore(s => s.defaultTrack);
const setDefaultTrack = useAppStore(s => s.setDefaultTrack);
const [profiles, setProfiles] = useState([]);
const [history, setHistory] = useState([]);
const [dubHistory, setDubHistory] = useState([]);
const [studioProjects, setStudioProjects] = useState([]);
const [exportHistory, setExportHistory] = useState([]);
const [showOverrides, setShowOverrides] = useState(false);
// ── Model status + sysinfo (TanStack Query) ──
const sysQuery = useSysinfo();
const msQuery = useModelStatus();
const sysStats = sysQuery.data ?? null;
const modelStatus = msQuery.data?.status ?? 'idle';
const modelSubStage = msQuery.data?.sub_stage ?? null;
const modelDetail = msQuery.data?.detail ?? '';
const modelError = msQuery.data?.error ?? null;
const modelProgress = msQuery.data?.progress ?? null;
// ── Model loading pill ──
const prevModelStatusRef = useRef(modelStatus);
useEffect(() => {
const prev = prevModelStatusRef.current;
prevModelStatusRef.current = modelStatus;
const pill = useAppStore.getState();
if (modelStatus === 'loading') {
const label = modelDetail || 'Loading model…';
if (prev !== 'loading' && pill.stage === 'idle') pill.showPill('loading-model', label);
else if (pill.stage === 'loading-model') pill.setPillLabel(label);
// Forward real-time percentage from backend
if (modelProgress !== null && pill.stage === 'loading-model') {
pill.setPillProgress(modelProgress);
}
}
if (modelStatus === 'ready' && prev === 'loading' && pill.stage === 'loading-model') pill.completePill('Model ready');
if (modelSubStage === 'error' && modelError && pill.stage === 'loading-model') pill.errorPill(modelError);
}, [modelStatus, modelSubStage, modelDetail, modelError, modelProgress]);
// ── Data loading callbacks ──
const loadProfiles = useCallback(async () => { try { setProfiles(await listProfiles()); } catch (e) {} }, []);
const loadHistory = useCallback(async () => { try { setHistory(await listHistory()); } catch (e) {} }, []);
const loadDubHistory = useCallback(async () => { try { setDubHistory(await listDubHistory()); } catch (e) {} }, []);
const loadProjects = useCallback(async () => { try { setStudioProjects(await listProjects()); } catch (e) {} }, []);
const loadExportHistory = useCallback(async () => { try { setExportHistory(await listExportHistory()); } catch (e) {} }, []);
// ── WebSocket real-time updates ──
useRealtimeEvents({
projects: () => loadProjects(),
profiles: () => loadProfiles(),
dub_history: () => loadDubHistory(),
export_history: () => loadExportHistory(),
generation_history: () => loadHistory(),
});
// ── Initial data load with backend retry ──
useEffect(() => {
let cancelled = false;
const loadAll = async () => {
let delay = 1000;
while (!cancelled) {
try { await apiModelStatus(); break; } catch (e) {}
await new Promise(r => setTimeout(r, delay));
delay = Math.min(delay * 2, 4000);
}
if (cancelled) return;
loadProfiles(); loadHistory(); loadDubHistory(); loadProjects(); loadExportHistory();
};
loadAll();
// Restore local UI state
try {
const saved = JSON.parse(localStorage.getItem('omni_ui') || '{}');
if (saved.uiScale) setUiScale(saved.uiScale);
if (saved.text) setText(saved.text);
if (saved.mode) setMode(saved.mode);
if (saved.vdStates) setVdStates(saved.vdStates);
if (saved.language) setLanguage(saved.language);
if (saved.isSidebarCollapsed !== undefined) setIsSidebarCollapsed(saved.isSidebarCollapsed);
if (saved.sidebarTab) setSidebarTab(saved.sidebarTab);
if (saved.dubJobId) setDubJobId(saved.dubJobId);
if (saved.dubFilename) setDubFilename(saved.dubFilename);
if (saved.dubDuration !== undefined) setDubDuration(saved.dubDuration);
if (saved.dubSegments) setDubSegments(saved.dubSegments.map(s => ({ ...s, text_original: s.text_original || s.text || '' })));
if (saved.dubLang) setDubLang(saved.dubLang);
if (saved.dubLangCode) setDubLangCode(saved.dubLangCode);
if (saved.dubTracks) setDubTracks(saved.dubTracks);
if (saved.dubStep) setDubStep(saved.dubStep);
if (saved.dubTranscript) setDubTranscript(saved.dubTranscript);
if (saved.exportTracks) setExportTracks(saved.exportTracks);
if (saved.preserveBg !== undefined) setPreserveBg(saved.preserveBg);
if (saved.defaultTrack) setDefaultTrack(saved.defaultTrack);
if (saved.exportHistory) setExportHistory(saved.exportHistory);
if (saved.speed) setSpeed(saved.speed);
if (saved.steps) setSteps(saved.steps);
if (saved.cfg) setCfg(saved.cfg);
if (saved.denoise !== undefined) setDenoise(saved.denoise);
if (saved.showOverrides !== undefined) setShowOverrides(saved.showOverrides);
} catch (e) {}
return () => { cancelled = true; };
}, []);
// ── Persist to localStorage ──
useEffect(() => {
localStorage.setItem('omni_ui', JSON.stringify({
uiScale, text, mode, vdStates, language,
isSidebarCollapsed, sidebarTab,
dubJobId, dubFilename, dubDuration, dubSegments,
dubLang, dubLangCode, dubTracks, dubStep, dubTranscript,
exportTracks, preserveBg, defaultTrack, exportHistory,
speed, steps, cfg, denoise, showOverrides
}));
}, [uiScale, text, mode, vdStates, language, isSidebarCollapsed, sidebarTab,
dubJobId, dubFilename, dubDuration, dubSegments, dubLang, dubLangCode,
dubTracks, dubStep, dubTranscript, exportTracks, preserveBg, defaultTrack,
exportHistory, speed, steps, cfg, denoise, showOverrides
]);
return {
profiles, history, dubHistory, studioProjects, exportHistory,
showOverrides, setShowOverrides,
sysStats, modelStatus,
loadProfiles, loadHistory, loadDubHistory, loadProjects, loadExportHistory,
};
}