/** * Функции экспорта для Timeline AI инструментов */ import type { TimelineClip, TimelineProject } from "@/features/timeline/types/timeline" import { escapeCsv, escapeXml, formatTimecode } from "./formatters" export function exportAsJSON(project: TimelineProject, includeData: any): any { // Основные данные проекта const exportData: any = { project_info: { name: project.name, duration: project.duration, resolution: project.settings.resolution, fps: project.settings.fps, created_at: project.createdAt?.toISOString(), updated_at: project.updatedAt?.toISOString(), }, sections: project.sections.map((section) => ({ id: section.id, name: section.name, start_time: section.startTime, duration: section.duration, real_start_time: section.realStartTime?.toISOString(), })), } // Дополнительные данные if (includeData.includeTracks) { exportData.tracks = [...project.globalTracks, ...project.sections.flatMap((s) => s.tracks)].map((track) => ({ id: track.id, name: track.name, type: track.type, clips_count: track.clips.length, })) } if (includeData.includeClips) { const allClips: TimelineClip[] = [] project.globalTracks.forEach((track) => allClips.push(...track.clips)) project.sections.forEach((section) => { section.tracks.forEach((track) => allClips.push(...track.clips)) }) exportData.clips = allClips.map((clip) => ({ id: clip.id, name: clip.name, start_time: clip.startTime, duration: clip.duration, media_file: clip.mediaFile?.name, track_id: clip.trackId, })) } return exportData } export function exportAsXML(project: TimelineProject, includeData: any): string { let xml = '\n' xml += "\n" // Информация о проекте xml += " \n" xml += ` ${escapeXml(project.name)}\n` xml += ` ${project.duration}\n` xml += ` ${project.settings.fps}\n` xml += " \n" // Секции xml += " \n" project.sections.forEach((section) => { xml += `
\n` xml += ` ${escapeXml(section.name)}\n` xml += ` ${section.startTime}\n` xml += ` ${section.duration}\n` xml += "
\n" }) xml += "
\n" // Треки (если запрошено) if (includeData.includeTracks) { xml += " \n" const allTracks = [...project.globalTracks, ...project.sections.flatMap((s) => s.tracks)] allTracks.forEach((track) => { xml += ` \n` xml += ` ${escapeXml(track.name)}\n` xml += ` ${track.clips.length}\n` xml += " \n" }) xml += " \n" } xml += "
" return xml } export function exportAsCSV(project: TimelineProject, includeData: any): string { let csv = "" if (includeData.includeClips) { // Экспорт клипов csv += "Clip ID,Name,Start Time,Duration,Track ID,Media File\n" const allClips: TimelineClip[] = [] project.globalTracks.forEach((track) => allClips.push(...track.clips)) project.sections.forEach((section) => { section.tracks.forEach((track) => allClips.push(...track.clips)) }) allClips.forEach((clip) => { csv += `${clip.id},"${escapeCsv(clip.name)}",${clip.startTime},${clip.duration},${clip.trackId},"${escapeCsv(clip.mediaFile?.name ?? "")}"\n` }) } else { // Экспорт секций csv += "Section ID,Name,Start Time,Duration\n" project.sections.forEach((section) => { csv += `${section.id},"${escapeCsv(section.name)}",${section.startTime},${section.duration}\n` }) } return csv } export function exportAsEDL(project: TimelineProject, _includeData: any): string { // EDL (Edit Decision List) формат let edl = `TITLE: ${project.name}\n` edl += "FCM: NON-DROP FRAME\n\n" let eventNumber = 1 // Собираем все клипы и сортируем по времени const allClips: TimelineClip[] = [] project.globalTracks.forEach((track) => allClips.push(...track.clips)) project.sections.forEach((section) => { section.tracks.forEach((track) => allClips.push(...track.clips)) }) const sortedClips = allClips.sort((a, b) => a.startTime - b.startTime) sortedClips.forEach((clip) => { const sourceIn = formatTimecode(clip.mediaStartTime || 0, project.settings.fps) const sourceOut = formatTimecode((clip.mediaStartTime || 0) + clip.duration, project.settings.fps) const recordIn = formatTimecode(clip.startTime, project.settings.fps) const recordOut = formatTimecode(clip.startTime + clip.duration, project.settings.fps) edl += `${String(eventNumber).padStart(3, "0")} ${clip.mediaFile?.name.substring(0, 8).toUpperCase().padEnd(8)} V C ${sourceIn} ${sourceOut} ${recordIn} ${recordOut}\n` eventNumber++ }) return edl } export function exportAsFCPXML(project: TimelineProject, _includeData: any): string { // Final Cut Pro XML формат let xml = '\n' xml += "\n" xml += '\n' xml += "\n" // Медиа ресурсы const allClips: TimelineClip[] = [] project.globalTracks.forEach((track) => allClips.push(...track.clips)) project.sections.forEach((section) => { section.tracks.forEach((track) => allClips.push(...track.clips)) }) const uniqueMedia = new Map() allClips.forEach((clip) => { if (clip.mediaId && !uniqueMedia.has(clip.mediaId)) { uniqueMedia.set(clip.mediaId, clip) } }) uniqueMedia.forEach((clip, mediaId) => { xml += ` \n` xml += ` \n` xml += " \n" }) xml += "\n" xml += "\n" xml += ` \n` xml += ` \n` xml += ` \n` xml += " \n" // Добавляем клипы const sortedClips = allClips.sort((a, b) => a.startTime - b.startTime) sortedClips.forEach((clip) => { const offset = `${Math.round(clip.startTime * project.settings.fps)}/${project.settings.fps}s` const duration = `${Math.round(clip.duration * project.settings.fps)}/${project.settings.fps}s` xml += ` \n` xml += ` \n` xml += " \n" }) xml += " \n" xml += " \n" xml += " \n" xml += " \n" xml += "\n" xml += "" return xml } export function exportAsDaVinciResolve(project: TimelineProject, _includeData: any): string { // DaVinci Resolve .drt формат (упрощенный) let drt = "# DaVinci Resolve Timeline Export\n" drt += `# Project: ${project.name}\n` drt += `# FPS: ${project.settings.fps}\n` drt += `# Resolution: ${project.settings.resolution.width}x${project.settings.resolution.height}\n\n` const allClips: TimelineClip[] = [] project.globalTracks.forEach((track) => allClips.push(...track.clips)) project.sections.forEach((section) => { section.tracks.forEach((track) => allClips.push(...track.clips)) }) const sortedClips = allClips.sort((a, b) => a.startTime - b.startTime) sortedClips.forEach((clip, index) => { drt += `Clip ${index + 1}:\n` drt += ` Name: ${clip.name}\n` drt += ` Start: ${formatTimecode(clip.startTime, project.settings.fps)}\n` drt += ` End: ${formatTimecode(clip.startTime + clip.duration, project.settings.fps)}\n` drt += ` Duration: ${formatTimecode(clip.duration, project.settings.fps)}\n` drt += ` Track: ${clip.trackId}\n` if (clip.mediaFile) { drt += ` Media: ${clip.mediaFile.name}\n` } drt += "\n" }) return drt }