Spaces:
Running
Running
<html lang="ko"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Veo3 영상 제작 스튜디오</title> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<script src="https://unpkg.com/lucide@latest/dist/umd/lucide.js"></script> | |
<script src="https://apis.google.com/js/api.js"></script> | |
<script> | |
API_KEY = "AIzaSyBt2gdFwXehWeYlcNetW89f5JHnQVXuhwI"; | |
</script> | |
<style> | |
@keyframes float { | |
0%, 100% { transform: translateY(0px); } | |
50% { transform: translateY(-10px); } | |
} | |
@keyframes pulse-glow { | |
0%, 100% { box-shadow: 0 0 5px rgba(59, 130, 246, 0.5); } | |
50% { box-shadow: 0 0 20px rgba(59, 130, 246, 0.8); } | |
} | |
.float-animation { | |
animation: float 3s ease-in-out infinite; | |
} | |
.pulse-glow { | |
animation: pulse-glow 2s ease-in-out infinite; | |
} | |
.glass-effect { | |
background: rgba(255, 255, 255, 0.1); | |
backdrop-filter: blur(10px); | |
border: 1px solid rgba(255, 255, 255, 0.2); | |
} | |
.gradient-bg { | |
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
} | |
.progress-bar { | |
transition: width 0.3s ease; | |
} | |
.drag-area { | |
border: 2px dashed rgba(59, 130, 246, 0.5); | |
transition: all 0.3s ease; | |
} | |
.drag-area:hover { | |
border-color: rgba(59, 130, 246, 1); | |
background: rgba(59, 130, 246, 0.05); | |
} | |
.drag-area.active { | |
border-color: #10b981; | |
background: rgba(16, 185, 129, 0.1); | |
} | |
.timeline-item { | |
transition: all 0.3s ease; | |
} | |
.timeline-item:hover { | |
transform: translateY(-2px); | |
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); | |
} | |
</style> | |
</head> | |
<body class="bg-gray-900 text-white min-h-screen"> | |
<!-- 헤더 --> | |
<header class="glass-effect p-4"> | |
<div class="max-w-7xl mx-auto flex items-center justify-between"> | |
<div class="flex items-center space-x-3"> | |
<div class="w-10 h-10 bg-blue-500 rounded-lg flex items-center justify-center"> | |
<i data-lucide="video" class="w-6 h-6"></i> | |
</div> | |
<h1 class="text-2xl font-bold">Veo3 영상 제작 스튜디오</h1> | |
</div> | |
<div class="flex items-center space-x-4"> | |
<button class="px-4 py-2 bg-blue-600 rounded-lg hover:bg-blue-700 transition"> | |
<i data-lucide="save" class="w-4 h-4 inline mr-2"></i>저장 | |
</button> | |
<button class="px-4 py-2 bg-green-600 rounded-lg hover:bg-green-700 transition"> | |
<i data-lucide="play" class="w-4 h-4 inline mr-2"></i>내보내기 | |
</button> | |
</div> | |
</div> | |
</header> | |
<!-- 메인 콘텐츠 --> | |
<main class="max-w-7xl mx-auto p-6 grid grid-cols-1 lg:grid-cols-3 gap-6"> | |
<!-- 왼쪽 패널 - 프로젝트 설정 --> | |
<div class="glass-effect rounded-xl p-6"> | |
<h2 class="text-xl font-semibold mb-4 flex items-center"> | |
<i data-lucide="settings" class="w-5 h-5 mr-2"></i>프로젝트 설정 | |
</h2> | |
<div class="space-y-4"> | |
<div> | |
<label class="block text-sm font-medium mb-2">프로젝트 이름</label> | |
<input type="text" class="w-full px-3 py-2 bg-gray-800 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" placeholder="나의 멋진 영상"> | |
</div> | |
<div> | |
<label class="block text-sm font-medium mb-2">비디오 제목</label> | |
<input type="text" class="w-full px-3 py-2 bg-gray-800 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" placeholder="AI의 놀라운 미래"> | |
</div> | |
<div> | |
<label class="block text-sm font-medium mb-2">영상 스타일</label> | |
<select class="w-full px-3 py-2 bg-gray-800 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"> | |
<option>영화적(시네마틱)</option> | |
<option>다큐멘터리</option> | |
<option>광고(광고홍보)</option> | |
<option>교육(강의형)</option> | |
<option>브이로그</option> | |
<option>리뷰/언박싱</option> | |
<option>게임 스트리밍</option> | |
<option>셀프 인터뷰</option> | |
<option>모션그래픽</option> | |
<option>3D 애니메이션</option> | |
<option>2D 카툰/일러스트</option> | |
<option>브랜드 필름</option> | |
<option>프로모션 뮤직비디오</option> | |
<option>스톱모션</option> | |
<option>타임랩스/하이퍼랩스</option> | |
</select> | |
</div> | |
<div> | |
<label class="block text-sm font-medium mb-2">길이 (초)</label> | |
<input type="range" min="15" max="300" value="60" class="w-full"> | |
<span class="text-sm text-gray-400">60초</span> | |
</div> | |
<div> | |
<label class="block text-sm font-medium mb-2">화질</label> | |
<select class="w-full px-3 py-2 bg-gray-800 rounded-lg"> | |
<option>HD (720p)</option> | |
<option>FHD (1080p)</option> | |
<option>4K (2160p)</option> | |
</select> | |
</div> | |
</div> | |
</div> | |
<!-- 중앙 패널 - 프롬프트 입력 --> | |
<div class="glass-effect rounded-xl p-6"> | |
<h2 class="text-xl font-semibold mb-4 flex items-center"> | |
<i data-lucide="edit-3" class="w-5 h-5 mr-2"></i>영상 프롬프트 | |
</h2> | |
<div class="space-y-4"> | |
<div> | |
<label class="block text-sm font-medium mb-2">메인 프롬프트</label> | |
<textarea class="w-full px-3 py-2 bg-gray-800 rounded-lg h-32 resize-none focus:outline-none focus:ring-2 focus:ring-blue-500" placeholder="예: 미래 도시의 하늘에서 내려다보는 드론 뷰, 네온 불빛이 반짝이는 사이버펑크 세계..."></textarea> | |
</div> | |
<div> | |
<label class="block text-sm font-medium mb-2">장면 설명</label> | |
<textarea class="w-full px-3 py-2 bg-gray-800 rounded-lg h-24 resize-none focus:outline-none focus:ring-2 focus:ring-blue-500" placeholder="특정 장면이나 순서를 설명하세요..."></textarea> | |
</div> | |
<div> | |
<label class="block text-sm font-medium mb-2">음악 스타일</label> | |
<select class="w-full px-3 py-2 bg-gray-800 rounded-lg"> | |
<option>에픽 오케스트라</option> | |
<option>시네마틱 트레일러</option> | |
<option>로파이 힙합</option> | |
<option>퓨처 베이스</option> | |
<option>하우스/테크노</option> | |
<option>드럼앤베이스</option> | |
<option>트랩/EDM</option> | |
<option>재즈 피아노</option> | |
<option>어쿠스틱 팝</option> | |
<option>인디록</option> | |
<option>발라드(감성)</option> | |
<option>국악 크로스오버</option> | |
<option>어린이 동요</option> | |
<option>성악/클래식</option> | |
<option>카페 재즈</option> | |
<option>레트로 신스웨이브</option> | |
<option>8비트 게임음악</option> | |
<option>뉴에이지 명상</option> | |
<option>펑크/소울</option> | |
<option>월드뮤직(아프리카/라틴)</option> | |
</select> | |
</div> | |
<button class="w-full py-3 bg-gradient-to-r from-purple-600 to-blue-600 rounded-lg hover:from-purple-700 hover:to-blue-700 transition font-semibold"> | |
<i data-lucide="wand-2" class="w-5 h-5 inline mr-2"></i>AI로 영상 생성하기 | |
</button> | |
</div> | |
</div> | |
<!-- 오른쪽 패널 - 미리보기 & 타임라인 --> | |
<div class="glass-effect rounded-xl p-6"> | |
<h2 class="text-xl font-semibold mb-4 flex items-center"> | |
<i data-lucide="eye" class="w-5 h-5 mr-2"></i>미리보기 | |
</h2> | |
<!-- 비디오 미리보어 --> | |
<div class="aspect-video bg-gray-800 rounded-lg mb-4 flex items-center justify-center relative"> | |
<div class="absolute inset-0 bg-gradient-to-br from-purple-900/20 to-blue-900/20 rounded-lg"></div> | |
<div class="text-center z-10"> | |
<i data-lucide="play-circle" class="w-16 h-16 mx-auto mb-2 text-gray-400"></i> | |
<p class="text-gray-400">미리보기 준비중...</p> | |
</div> | |
</div> | |
<!-- 진행률 --> | |
<div class="mb-4"> | |
<div class="flex justify-between text-sm mb-1"> | |
<span>생성 진행률</span> | |
<span id="progressText">0%</span> | |
</div> | |
<div class="w-full bg-gray-700 rounded-full h-2"> | |
<div id="progressBar" class="bg-blue-500 h-2 rounded-full progress-bar" style="width: 0%"></div> | |
</div> | |
</div> | |
<!-- 타임라인 --> | |
<div class="space-y-2"> | |
<h3 class="text-sm font-medium">타임라인</h3> | |
<div class="space-y-2 max-h-64 overflow-y-auto"> | |
<div class="timeline-item bg-gray-800 rounded-lg p-3"> | |
<div class="flex items-center justify-between"> | |
<span class="text-sm">도시의 새벽 장면</span> | |
<span class="text-xs text-gray-400">0-15초</span> | |
</div> | |
</div> | |
<div class="timeline-item bg-gray-800 rounded-lg p-3"> | |
<div class="flex items-center justify-between"> | |
<span class="text-sm">드론이 날아가는 장면</span> | |
<span class="text-xs text-gray-400">15-30초</span> | |
</div> | |
</div> | |
<div class="timeline-item bg-gray-800 rounded-lg p-3"> | |
<div class="flex items-center justify-between"> | |
<span class="text-sm">네온 불빛의 교차로</span> | |
<span class="text-xs text-gray-400">30-45초</span> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</main> | |
<!-- 하단 드래그 앤 드롭 영역 --> | |
<footer class="max-w-7xl mx-auto px-6 pb-6"> | |
<div class="glass-effect rounded-xl p-6"> | |
<h3 class="text-lg font-semibold mb-4 flex items-center"> | |
<i data-lucide="upload-cloud" class="w-5 h-5 mr-2"></i>리소스 업로드 | |
</h3> | |
<div class="drag-area rounded-lg p-8 text-center"> | |
<i data-lucide="cloud-upload" class="w-12 h-12 mx-auto mb-3 text-gray-400"></i> | |
<p class="text-gray-400 mb-2">이미지, 비디오, 오디오 파일을 드래그하거나 클릭하여 업로드</p> | |
<p class="text-sm text-gray-500">지원 형식: JPG, PNG, MP4, MP3</p> | |
<input type="file" multiple class="hidden" id="fileInput" accept="image/*,video/*,audio/*"> | |
</div> | |
</div> | |
</footer> | |
<!-- 모달 --> | |
<div id="modal" class="fixed inset-0 bg-black bg-opacity-50 hidden items-center justify-center"> | |
<div class="glass-effect rounded-xl p-6 max-w-md"> | |
<h3 class="text-lg font-semibold mb-4">생성 완료!</h3> | |
<p class="text-gray-400 mb-4">영상 생성이 완료되었습니다. 다운로드하시겠습니까?</p> | |
<div class="flex space-x-3"> | |
<button class="flex-1 py-2 bg-blue-600 rounded-lg hover:bg-blue-700">다운로드</button> | |
<button class="flex-1 py-2 bg-gray-700 rounded-lg hover:bg-gray-600" onclick="closeModal()">닫기</button> | |
</div> | |
</div> | |
</div> | |
<script> | |
// Lucide 아이콘 초기화 | |
lucide.createIcons(); | |
// 실제 진행률 | |
let progress = 0; | |
const progressBar = document.getElementById('progressBar'); | |
const progressText = document.getElementById('progressText'); | |
// Veo API로 실제 영상 생성 | |
async function generateVideo(prompt) { | |
const requestBody = { | |
instances: [{ | |
prompt: prompt | |
}], | |
parameters: { | |
aspectRatio: "16:9", | |
durationSeconds: 8 | |
} | |
}; | |
const res = await fetch( | |
`https://generativelanguage.googleapis.com/v1beta/videos:generate?key=${API_KEY}`, | |
{ | |
method: "POST", | |
headers: { "Content-Type": "application/json" }, | |
body: JSON.stringify(requestBody) | |
} | |
); | |
const data = await res.json(); | |
return data.name; // operation name | |
} | |
async function getOperationStatus(name) { | |
const res = await fetch( | |
`https://generativelanguage.googleapis.com/v1beta/${name}?key=${API_KEY}` | |
); | |
return await res.json(); | |
} | |
async function pollUntilDone(name) { | |
progress = 0; | |
const check = setInterval(async () => { | |
const op = await getOperationStatus(name); | |
const state = op.done ? 100 : Math.min(op.metadata?.video?.videoProgressPercent || progress + 5, 95); | |
progress = state; | |
progressBar.style.width = progress + '%'; | |
progressText.textContent = Math.floor(progress) + '%'; | |
if (op.done) { | |
clearInterval(check); | |
progressBar.style.width = '100%'; | |
progressText.textContent = '100%'; | |
const videoUri = op.response?.video?.videoUri; | |
if (videoUri) { | |
document.querySelector('.aspect-video').innerHTML = | |
`<video class="w-full h-full rounded-lg" controls src="${videoUri}"></video>`; | |
} | |
setTimeout(() => { | |
document.getElementById('modal').classList.remove('hidden'); | |
document.getElementById('modal').classList.add('flex'); | |
}, 500); | |
} | |
}, 2000); | |
} | |
async function handleGenerate() { | |
const prompt = document.querySelector('textarea').value.trim(); | |
if (!prompt) { | |
alert('프롬프트를 입력하세요!'); | |
return; | |
} | |
try { | |
const operationName = await generateVideo(prompt); | |
await pollUntilDone(operationName); | |
} catch (e) { | |
alert('영상 생성 실패: ' + e.message); | |
} | |
} | |
// 드래그 앤 드롭 | |
const dragArea = document.querySelector('.drag-area'); | |
const fileInput = document.getElementById('fileInput'); | |
dragArea.addEventListener('click', () => fileInput.click()); | |
dragArea.addEventListener('dragover', (e) => { | |
e.preventDefault(); | |
dragArea.classList.add('active'); | |
}); | |
dragArea.addEventListener('dragleave', () => { | |
dragArea.classList.remove('active'); | |
}); | |
dragArea.addEventListener('drop', (e) => { | |
e.preventDefault(); | |
dragArea.classList.remove('active'); | |
console.log('파일 업로드:', e.dataTransfer.files); | |
}); | |
// AI 생성 버튼 클릭 | |
document.querySelector('button').addEventListener('click', handleGenerate); | |
// 모달 닫기 | |
function closeModal() { | |
document.getElementById('modal').classList.add('hidden'); | |
document.getElementById('modal').classList.remove('flex'); | |
} | |
// 반응형 메뉴 | |
const rangeInput = document.querySelector('input[type="range"]'); | |
const rangeValue = document.querySelector('.text-sm.text-gray-400'); | |
rangeInput.addEventListener('input', (e) => { | |
rangeValue.textContent = e.target.value + '초'; | |
}); | |
</script> | |
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=UltronBasecamp/veo3" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
</html> |