fix(voice-clone): persist clone info in localStorage, auto-merge into project knobs, fix clone ID detection in CreateModal
- Move voice clone cache from module-level memory to localStorage so it survives page refresh and works across browser tabs - VoiceAvatarPlaceholder now syncs clone result to localStorage immediately after creation (both design and clone paths) - usePodcastProjectState auto-merges voice clone cache into project knobs when loading a project (fills gap for projects created before voice clone or when voice clone was created after) - CreateModal now detects voice clone IDs by prefix (vc_*) not just by VOICE_CLONE_ID constant, fixing the mismatch where VoiceSelector passes the actual clone ID but CreateModal expected the placeholder ID - AudioRegenerateModal is intentionally per-scene override and does not write back to knobs (by design) - trends.py handler added for podcast topic trend analysis
This commit is contained in:
@@ -11,7 +11,7 @@ import {
|
||||
PodcastBible,
|
||||
} from '../components/PodcastMaker/types';
|
||||
import { BlogResearchResponse, ResearchProvider } from '../services/blogWriterApi';
|
||||
import { podcastApi } from '../services/podcastApi';
|
||||
import { podcastApi, getCachedVoiceCloneInfo } from '../services/podcastApi';
|
||||
|
||||
export interface PodcastProjectState {
|
||||
// Project metadata
|
||||
@@ -79,6 +79,30 @@ const DEFAULT_KNOBS: Knobs = {
|
||||
bitrate: "standard",
|
||||
};
|
||||
|
||||
/**
|
||||
* Merge voice clone cache into knobs if the project knobs don't already have it.
|
||||
* This ensures projects created before voice clone, or after a new clone is made,
|
||||
* automatically pick up the latest voice clone info.
|
||||
*/
|
||||
function mergeVoiceCloneCacheIntoKnobs(knobs: Knobs): Knobs {
|
||||
// If knobs already has a custom voice ID, trust it (user explicitly set it)
|
||||
if (knobs.custom_voice_id) {
|
||||
return knobs;
|
||||
}
|
||||
const cached = getCachedVoiceCloneInfo();
|
||||
if (!cached || !cached.isVoiceClone) {
|
||||
return knobs;
|
||||
}
|
||||
return {
|
||||
...knobs,
|
||||
voice_id: knobs.voice_id || "Wise_Woman",
|
||||
custom_voice_id: cached.customVoiceId,
|
||||
is_voice_clone: true,
|
||||
voice_sample_url: cached.voiceSampleUrl,
|
||||
voice_clone_engine: cached.engine || "qwen3",
|
||||
};
|
||||
}
|
||||
|
||||
const DEFAULT_STATE: PodcastProjectState = {
|
||||
project: null,
|
||||
analysis: null,
|
||||
@@ -446,7 +470,7 @@ export const usePodcastProjectState = () => {
|
||||
scriptData: dbProject.script_data,
|
||||
bible: dbProject.bible,
|
||||
renderJobs: dbProject.render_jobs || [],
|
||||
knobs: { ...DEFAULT_KNOBS, ...(dbProject.knobs || {}) },
|
||||
knobs: mergeVoiceCloneCacheIntoKnobs({ ...DEFAULT_KNOBS, ...(dbProject.knobs || {}) }),
|
||||
researchProvider: dbProject.research_provider || 'exa',
|
||||
budgetCap: dbProject.budget_cap || 50,
|
||||
showScriptEditor: dbProject.show_script_editor || false,
|
||||
|
||||
Reference in New Issue
Block a user