// Z-AMPP MAM bridge. On boot, reads ?asset= from URL and imports it. // Also exports pickFromMAM() for an in-app picker. type MAMAsset = { id: string; display_name?: string; filename?: string; }; async function getStreamURL(assetId: string): Promise { const r = await fetch('/api/v1/assets/' + assetId + '/stream', { credentials: 'omit' }); if (!r.ok) throw new Error('stream lookup failed: ' + r.status); const j = await r.json(); if (!j || !j.url) throw new Error('stream lookup: no url'); return j.url; } async function listAssets(limit = 200): Promise { const r = await fetch('/api/v1/assets?limit=' + limit, { credentials: 'omit' }); if (!r.ok) throw new Error('list assets failed: ' + r.status); const j = await r.json(); return (j && j.assets) || []; } function getMediaBridge(): any { const w = window as any; return w.__mediaBridge || w.mediaBridge || w.MediaBridge || (w.editor && w.editor.mediaBridge) || null; } 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(); } if (!bridge || typeof bridge.importFromURL !== 'function') { throw new Error('MediaBridge.importFromURL not available'); } return bridge.importFromURL(url, name || (assetId + '.mp4')); } export async function pickFromMAM(): Promise { const assets = await listAssets(200); const overlay = document.createElement('div'); Object.assign(overlay.style, { position:'fixed',inset:'0',background:'rgba(0,0,0,0.72)',zIndex:'999999',display:'flex',alignItems:'center',justifyContent:'center' } as CSSStyleDeclaration); const modal = document.createElement('div'); Object.assign(modal.style, { width:'min(960px,92vw)',maxHeight:'84vh',background:'#161618',color:'#e8e8ea',borderRadius:'12px',padding:'16px',display:'flex',flexDirection:'column',gap:'12px',overflow:'hidden' } as CSSStyleDeclaration); modal.innerHTML = '
Import from Z-AMPP MAM
'; const grid = document.createElement('div'); Object.assign(grid.style, { display:'grid',gridTemplateColumns:'repeat(auto-fill,minmax(180px,1fr))',gap:'10px',overflowY:'auto',padding:'4px' } as CSSStyleDeclaration); for (const a of assets) { const card = document.createElement('button'); Object.assign(card.style, { background:'#1f1f23',border:'1px solid #2c2c32',borderRadius:'8px',padding:'6px',color:'#e8e8ea',cursor:'pointer',display:'flex',flexDirection:'column',gap:'6px',textAlign:'left' } as CSSStyleDeclaration); const label = a.display_name || a.filename || a.id; card.innerHTML = '' + label + ''; card.addEventListener('click', async () => { card.disabled = true; card.style.opacity='0.5'; try { await importAsset(a.id, label); } catch(e){ alert('Import failed: '+(e as Error).message); } finally { card.disabled=false; card.style.opacity='1'; } }); grid.appendChild(card); } modal.appendChild(grid); overlay.appendChild(modal); document.body.appendChild(overlay); const close = () => overlay.remove(); overlay.addEventListener('click', (e) => { if (e.target === overlay) close(); }); modal.querySelector('[data-close]')?.addEventListener('click', close); } export function initMAMBridge(): void { try { const qs = new URLSearchParams(window.location.search); const projectId = qs.get('project'); if (projectId) { try { window.localStorage.setItem('mamProjectId', projectId); } catch {} } (window as any).__zamppPickFromMAM = pickFromMAM; const assetId = qs.get('asset'); if (!assetId) return; 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); else console.error('[mam-bridge] import retries exhausted', e); } }; if (document.readyState === 'complete') tryImport(); else window.addEventListener('load', () => tryImport()); } catch (e) { console.error('[mam-bridge] init failed', e); } } initMAMBridge();