diff --git a/services/editor/apps/web/src/bridges/media-bridge.ts b/services/editor/apps/web/src/bridges/media-bridge.ts index fbf6df2..febd8a7 100644 --- a/services/editor/apps/web/src/bridges/media-bridge.ts +++ b/services/editor/apps/web/src/bridges/media-bridge.ts @@ -485,6 +485,7 @@ let mediaBridgeInstance: MediaBridge | null = null; export function getMediaBridge(): MediaBridge { if (!mediaBridgeInstance) { mediaBridgeInstance = new MediaBridge(); + (window as any).__mediaBridge = mediaBridgeInstance; } return mediaBridgeInstance; } @@ -493,6 +494,7 @@ export function getMediaBridge(): MediaBridge { * Initialize the shared MediaBridge */ export async function initializeMediaBridge(): Promise { + // Z-AMPP: expose for mam-bridge.ts const bridge = getMediaBridge(); await bridge.initialize(); return bridge; diff --git a/services/editor/apps/web/src/mam-bridge.ts b/services/editor/apps/web/src/mam-bridge.ts index 961d72c..037ca98 100644 --- a/services/editor/apps/web/src/mam-bridge.ts +++ b/services/editor/apps/web/src/mam-bridge.ts @@ -25,15 +25,18 @@ function getMediaBridge(): any { async function importAsset(assetId: string, name?: string): Promise { const url = await getStreamURL(assetId); - let bridge = getMediaBridge(); - if (!bridge || typeof bridge.importFromURL !== 'function') { - await new Promise(r => setTimeout(r, 500)); - bridge = getMediaBridge(); + const safeName = (name || (assetId + '.mp4')).replace(/[^\w.\-]+/g, '_'); + const store: any = (window as any).__projectStore; + if (store && typeof store.getState === 'function' && typeof store.getState().importMedia === 'function') { + const res = await fetch(url, { credentials: 'omit' }); + if (!res.ok) throw new Error('fetch failed: ' + res.status); + const blob = await res.blob(); + const inferred = (safeName.toLowerCase().endsWith('.webm') ? 'video/webm' : safeName.toLowerCase().endsWith('.mov') ? 'video/quicktime' : safeName.toLowerCase().endsWith('.mp3') ? 'audio/mpeg' : safeName.toLowerCase().endsWith('.wav') ? 'audio/wav' : 'video/mp4'); const ct = (blob.type && blob.type !== 'application/octet-stream') ? blob.type : inferred; const file = new File([blob], safeName, { type: ct }); + return store.getState().importMedia(file); } - if (!bridge || typeof bridge.importFromURL !== 'function') { - throw new Error('MediaBridge.importFromURL not available'); - } - return bridge.importFromURL(url, name || (assetId + '.mp4')); + const bridge = getMediaBridge(); + if (bridge && typeof bridge.importFromURL === 'function') return bridge.importFromURL(url, safeName); + throw new Error('No import target'); } export async function pickFromMAM(): Promise { @@ -69,10 +72,42 @@ export function initMAMBridge(): void { (window as any).__zamppPickFromMAM = pickFromMAM; const assetId = qs.get('asset'); if (!assetId) return; + // Auto-skip the startup chooser so MediaBridge initializes immediately. + const clickMatching = (matches: string[]): boolean => { + const btns = document.querySelectorAll('button,[role="button"],a'); + for (const el of Array.from(btns)) { + const t = (el.textContent || '').trim().toLowerCase(); + if (matches.some(m => t === m || t.startsWith(m))) { (el as HTMLElement).click(); return true; } + } + return false; + }; + let dismissTries = 0; + const dismissInterval = setInterval(() => { + const acted = clickMatching(['horizontal', 'open editor', 'start fresh', 'skip tour']); + if (dismissTries++ > 80) clearInterval(dismissInterval); + if (acted) dismissTries = Math.max(0, dismissTries - 4); + }, 250); + let lastProjectId: string | null = null; + let stableSince = 0; + let imported = false; const tryImport = async (n = 0): Promise => { - try { await importAsset(assetId); console.log('[mam-bridge] imported', assetId); } - catch (e) { - if (n < 10) setTimeout(() => tryImport(n + 1), 500); + if (imported) return; + const w = window as any; + const st = w.__projectStore && w.__projectStore.getState && w.__projectStore.getState(); + const p = st && st.project; + const ready = !!(p && p.mediaLibrary); + if (!ready) { + if (n < 80) { setTimeout(() => tryImport(n + 1), 750); return; } + console.error('[mam-bridge] project never loaded'); return; + } + if (p.id !== lastProjectId) { lastProjectId = p.id; stableSince = Date.now(); setTimeout(() => tryImport(n + 1), 750); return; } + if (Date.now() - stableSince < 1500) { setTimeout(() => tryImport(n + 1), 500); return; } + try { + await importAsset(assetId); + imported = true; + console.log('[mam-bridge] imported', assetId, 'into project', p.id); + } catch (e) { + if (n < 80) setTimeout(() => tryImport(n + 1), 1000); else console.error('[mam-bridge] import retries exhausted', e); } }; diff --git a/services/editor/apps/web/src/stores/project-store.ts b/services/editor/apps/web/src/stores/project-store.ts index 4087926..3aafcb7 100644 --- a/services/editor/apps/web/src/stores/project-store.ts +++ b/services/editor/apps/web/src/stores/project-store.ts @@ -5901,3 +5901,5 @@ export const useProjectStore = create()( }; }), ); + +;(window as any).__projectStore = useProjectStore;