diff --git a/services/studio/FlatRender.StudioSvc/Application/Services/StudioService.cs b/services/studio/FlatRender.StudioSvc/Application/Services/StudioService.cs index 2330422..b948430 100644 --- a/services/studio/FlatRender.StudioSvc/Application/Services/StudioService.cs +++ b/services/studio/FlatRender.StudioSvc/Application/Services/StudioService.cs @@ -193,6 +193,14 @@ public class StudioService(StudioDbContext db) FROM content.shared_colors sc WHERE sc.project_id = {1};", savedProjectId, originalProjectId); + + // 5. carry the template's render mode (FIX/FLEXIBLE/…) so the studio knows whether + // adding scenes is allowed, plus a few useful project defaults. + await db.Database.ExecuteSqlRawAsync(@" + UPDATE studio.saved_projects sp + SET choose_mode = COALESCE((SELECT p.choose_mode::text FROM content.projects p WHERE p.id = {1}), sp.choose_mode) + WHERE sp.id = {0};", + savedProjectId, originalProjectId); } public async Task UpdateProjectAsync( diff --git a/src/components/admin/ProjectScenes.tsx b/src/components/admin/ProjectScenes.tsx index 5861130..fccad8c 100644 --- a/src/components/admin/ProjectScenes.tsx +++ b/src/components/admin/ProjectScenes.tsx @@ -67,7 +67,10 @@ function sceneToDraft(s: Scene): SceneDraft { } // ============================================================================= -export function ProjectScenes({ projectId }: { projectId: string }) { +// FIX / MusicVisualizer projects have a fixed scene set from After Effects layer names. +const FIXED_SCENE_MODES = new Set(["fix", "musicvisualizer"]); + +export function ProjectScenes({ projectId, mode }: { projectId: string; mode?: string }) { const [tab, setTab] = useState<"scenes" | "colors" | "presets">("scenes"); return (
@@ -80,7 +83,9 @@ export function ProjectScenes({ projectId }: { projectId: string }) { >{l} ))}
- {tab === "scenes" && } + {tab === "scenes" && ( + + )} {tab === "colors" && } {tab === "presets" && } @@ -88,7 +93,7 @@ export function ProjectScenes({ projectId }: { projectId: string }) { } // ── Scenes ──────────────────────────────────────────────────────────────────── -function ScenesTab({ projectId }: { projectId: string }) { +function ScenesTab({ projectId, fixedScenes }: { projectId: string; fixedScenes?: boolean }) { const [rows, setRows] = useState([]); const [loading, setLoading] = useState(true); const [draft, setDraft] = useState(null); @@ -152,7 +157,11 @@ function ScenesTab({ projectId }: { projectId: string }) {

صحنه‌ها بلوک‌های قابل‌ویرایش این قالب هستند. کلید هر صحنه باید با نام کامپوزیشن افترافکت یکی باشد.

- + {fixedScenes ? ( + صحنه‌ها از روی پروژهٔ افترافکت تعریف می‌شوند (پروژهٔ Fix) + ) : ( + + )}
{scanOpen && ( diff --git a/src/components/admin/ProjectsAdmin.tsx b/src/components/admin/ProjectsAdmin.tsx index a4ece70..46ba805 100644 --- a/src/components/admin/ProjectsAdmin.tsx +++ b/src/components/admin/ProjectsAdmin.tsx @@ -12,6 +12,7 @@ interface Proj { id: string; container_id: string; container_name: string; container_slug: string; name: string; image?: string | null; aspect?: string | null; resolution: string; aep_file_url?: string | null; render_aep_comp: string; is_published: boolean; sort: number; + choose_mode?: string | null; } const card = "rounded-xl border border-[#1e2235] bg-[#0f1120]"; @@ -315,7 +316,7 @@ export function ProjectsAdmin() {
- +
diff --git a/src/components/studio/AddSceneMenu.tsx b/src/components/studio/AddSceneMenu.tsx index 3d5181b..251a372 100644 --- a/src/components/studio/AddSceneMenu.tsx +++ b/src/components/studio/AddSceneMenu.tsx @@ -10,16 +10,26 @@ import { PopoverContent, PopoverTrigger, } from "@/components/ui/popover"; +import { useStudioStore } from "@/lib/studio-store"; interface AddSceneMenuProps { onAddBlank: () => void; variant?: "header" | "footer"; } +// FIX / MusicVisualizer projects have a fixed scene set defined in After Effects +// (scenes come from layer names), so adding scenes is not allowed. +const FIXED_SCENE_MODES = new Set(["fix", "musicvisualizer"]); + export function AddSceneMenu({ onAddBlank, variant = "footer" }: AddSceneMenuProps) { const t = useTranslations("auto.componentsStudioAddSceneMenu"); const [open, setOpen] = useState(false); const isHeader = variant === "header"; + const chooseMode = useStudioStore((s) => s.chooseMode); + + if (FIXED_SCENE_MODES.has(chooseMode.toLowerCase())) { + return null; // fixed-structure project — no add-scene + } return ( diff --git a/src/lib/studio-scene-data.ts b/src/lib/studio-scene-data.ts index 0c70553..f453aab 100644 --- a/src/lib/studio-scene-data.ts +++ b/src/lib/studio-scene-data.ts @@ -164,6 +164,9 @@ export interface VideoPersistedSceneData { audioVolume?: number; sceneBackgroundColor?: string; sceneAccentColor?: string; + /** Project render mode (FIX / FLEXIBLE / MusicVisualizer / …). FIX/MusicVisualizer + * scenes come from AE layer names, so adding scenes is disabled. */ + chooseMode?: string; } export function buildVideoSceneDataPayload( @@ -214,5 +217,11 @@ export function parseVideoSceneData( typeof sceneData.sceneAccentColor === "string" ? sceneData.sceneAccentColor : DEFAULT_SCENE_ACCENT_COLOR, + chooseMode: + typeof sceneData.chooseMode === "string" + ? sceneData.chooseMode + : typeof sceneData.primaryMode === "string" + ? sceneData.primaryMode + : undefined, }; } diff --git a/src/lib/studio-store.ts b/src/lib/studio-store.ts index c16d3f7..b90872a 100644 --- a/src/lib/studio-store.ts +++ b/src/lib/studio-store.ts @@ -159,6 +159,8 @@ export interface StudioState { audioVolume: number; sceneBackgroundColor: string; sceneAccentColor: string; + /** Project render mode (FIX / FLEXIBLE / MusicVisualizer / …). Empty until hydrated. */ + chooseMode: string; past: StudioHistorySnapshot[]; future: StudioHistorySnapshot[]; layerClipboard: Layer | null; @@ -249,6 +251,7 @@ export const useStudioStore = create((set, get) => { audioVolume: 100, sceneBackgroundColor: DEFAULT_SCENE_BACKGROUND_COLOR, sceneAccentColor: DEFAULT_SCENE_ACCENT_COLOR, + chooseMode: "", past: [], future: [], layerClipboard: null, @@ -757,6 +760,7 @@ export const useStudioStore = create((set, get) => { parsed.sceneBackgroundColor ?? DEFAULT_SCENE_BACKGROUND_COLOR, sceneAccentColor: parsed.sceneAccentColor ?? DEFAULT_SCENE_ACCENT_COLOR, + chooseMode: parsed.chooseMode ?? "", past: [], future: [], });