diff --git a/src/pages/music/components/VideoProjectsTab.jsx b/src/pages/music/components/VideoProjectsTab.jsx index 51b82d7..2a87a95 100644 --- a/src/pages/music/components/VideoProjectsTab.jsx +++ b/src/pages/music/components/VideoProjectsTab.jsx @@ -1,3 +1,269 @@ +// src/pages/music/components/VideoProjectsTab.jsx +import { useState, useEffect, useRef } from 'react'; +import { + createVideoProject, getVideoProjects, + renderVideoProject, exportVideoProject, deleteVideoProject, +} from '../../../api'; + +const COUNTRY_OPTIONS = ['BR', 'US', 'ID', 'MX', 'KR']; +const COUNTRY_FLAGS = { BR: '๐ง๐ท', US: '๐บ๐ธ', ID: '๐ฎ๐ฉ', MX: '๐ฒ๐ฝ', KR: '๐ฐ๐ท' }; + export default function VideoProjectsTab({ library, initialTrackId, onClearInitialTrack }) { - return null; + const [projects, setProjects] = useState([]); + const [selectedTrackId, setSelectedTrackId] = useState(initialTrackId ?? ''); + const [format, setFormat] = useState('visualizer'); + const [countries, setCountries] = useState(['BR']); + const [creating, setCreating] = useState(false); + const [exportData, setExportData] = useState(null); + const [exportingId, setExportingId] = useState(null); + const pollRef = useRef(null); + + // initialTrackId prop ๋ฐ์ + useEffect(() => { + if (initialTrackId) { + setSelectedTrackId(String(initialTrackId)); + onClearInitialTrack?.(); + } + }, [initialTrackId]); + + const loadProjects = async () => { + try { + const data = await getVideoProjects(); + setProjects(Array.isArray(data) ? data : data.projects ?? []); + } catch (e) { + console.error('getVideoProjects:', e); + } + }; + + useEffect(() => { loadProjects(); }, []); + + // ๋ ๋๋ง ์ค์ธ ํ๋ก์ ํธ๊ฐ ์์ผ๋ฉด 5์ด๋ง๋ค ํด๋ง + useEffect(() => { + const hasRendering = projects.some(p => p.status === 'rendering'); + if (hasRendering && !pollRef.current) { + pollRef.current = setInterval(loadProjects, 5000); + } else if (!hasRendering && pollRef.current) { + clearInterval(pollRef.current); + pollRef.current = null; + } + return () => { + if (pollRef.current) { clearInterval(pollRef.current); pollRef.current = null; } + }; + }, [projects]); + + const toggleCountry = (c) => { + setCountries(prev => + prev.includes(c) ? prev.filter(x => x !== c) : [...prev, c] + ); + }; + + const handleCreate = async () => { + if (!selectedTrackId || countries.length === 0) return; + setCreating(true); + try { + await createVideoProject({ + track_id: Number(selectedTrackId), + format, + target_countries: countries, + }); + await loadProjects(); + } catch (e) { + console.error('createVideoProject:', e); + } finally { + setCreating(false); + } + }; + + const handleRender = async (id) => { + try { + await renderVideoProject(id); + await loadProjects(); + } catch (e) { + console.error('renderVideoProject:', e); + } + }; + + const handleExport = async (id) => { + setExportingId(id); + try { + const data = await exportVideoProject(id); + setExportData({ id, ...data }); + } catch (e) { + console.error('exportVideoProject:', e); + } finally { + setExportingId(null); + } + }; + + const handleDelete = async (id) => { + if (!window.confirm('์ด ํ๋ก์ ํธ๋ฅผ ์ญ์ ํ ๊น์?')) return; + try { + await deleteVideoProject(id); + setProjects(prev => prev.filter(p => p.id !== id)); + if (exportData?.id === id) setExportData(null); + } catch (e) { + console.error('deleteVideoProject:', e); + } + }; + + return ( +
ํธ๋์ ์ ํํด ์์์ ๋ง๋ค์ด๋ณด์ธ์
+ ) : ( +
+ {JSON.stringify(exportData.metadata, null, 2)}
+
+