Spaces:
Running
Running
영상 스타일과 음악 스타일을 좀 더 세분화 해줘 - Follow Up Deployment
Browse files- index.html +106 -20
index.html
CHANGED
@@ -6,6 +6,10 @@
|
|
6 |
<title>Veo3 영상 제작 스튜디오</title>
|
7 |
<script src="https://cdn.tailwindcss.com"></script>
|
8 |
<script src="https://unpkg.com/lucide@latest/dist/umd/lucide.js"></script>
|
|
|
|
|
|
|
|
|
9 |
<style>
|
10 |
@keyframes float {
|
11 |
0%, 100% { transform: translateY(0px); }
|
@@ -94,12 +98,23 @@
|
|
94 |
</div>
|
95 |
|
96 |
<div>
|
97 |
-
<label class="block text-sm font-medium mb-2"
|
98 |
<select class="w-full px-3 py-2 bg-gray-800 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
|
99 |
-
<option
|
100 |
<option>다큐멘터리</option>
|
101 |
-
<option
|
102 |
-
<option
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
103 |
</select>
|
104 |
</div>
|
105 |
|
@@ -141,10 +156,25 @@
|
|
141 |
<label class="block text-sm font-medium mb-2">음악 스타일</label>
|
142 |
<select class="w-full px-3 py-2 bg-gray-800 rounded-lg">
|
143 |
<option>에픽 오케스트라</option>
|
144 |
-
<option
|
145 |
-
<option
|
146 |
-
<option
|
147 |
-
<option
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
148 |
</select>
|
149 |
</div>
|
150 |
|
@@ -238,25 +268,81 @@
|
|
238 |
// Lucide 아이콘 초기화
|
239 |
lucide.createIcons();
|
240 |
|
241 |
-
// 진행률
|
242 |
let progress = 0;
|
243 |
const progressBar = document.getElementById('progressBar');
|
244 |
const progressText = document.getElementById('progressText');
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
252 |
setTimeout(() => {
|
253 |
document.getElementById('modal').classList.remove('hidden');
|
254 |
document.getElementById('modal').classList.add('flex');
|
255 |
}, 500);
|
256 |
}
|
257 |
-
|
258 |
-
|
259 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
260 |
}
|
261 |
|
262 |
// 드래그 앤 드롭
|
@@ -281,7 +367,7 @@
|
|
281 |
});
|
282 |
|
283 |
// AI 생성 버튼 클릭
|
284 |
-
document.querySelector('button').addEventListener('click',
|
285 |
|
286 |
// 모달 닫기
|
287 |
function closeModal() {
|
|
|
6 |
<title>Veo3 영상 제작 스튜디오</title>
|
7 |
<script src="https://cdn.tailwindcss.com"></script>
|
8 |
<script src="https://unpkg.com/lucide@latest/dist/umd/lucide.js"></script>
|
9 |
+
<script src="https://apis.google.com/js/api.js"></script>
|
10 |
+
<script>
|
11 |
+
window.GOOGLE_API_KEY = "AIzaSyBt2gdFwXehWeYlcNetW89f5JHnQVXuhwI";
|
12 |
+
</script>
|
13 |
<style>
|
14 |
@keyframes float {
|
15 |
0%, 100% { transform: translateY(0px); }
|
|
|
98 |
</div>
|
99 |
|
100 |
<div>
|
101 |
+
<label class="block text-sm font-medium mb-2">영상 스타일</label>
|
102 |
<select class="w-full px-3 py-2 bg-gray-800 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
|
103 |
+
<option>영화적(시네마틱)</option>
|
104 |
<option>다큐멘터리</option>
|
105 |
+
<option>광고(광고홍보)</option>
|
106 |
+
<option>교육(강의형)</option>
|
107 |
+
<option>브이로그</option>
|
108 |
+
<option>리뷰/언박싱</option>
|
109 |
+
<option>게임 스트리밍</option>
|
110 |
+
<option>셀프 인터뷰</option>
|
111 |
+
<option>모션그래픽</option>
|
112 |
+
<option>3D 애니메이션</option>
|
113 |
+
<option>2D 카툰/일러스트</option>
|
114 |
+
<option>브랜드 필름</option>
|
115 |
+
<option>프로모션 뮤직비디오</option>
|
116 |
+
<option>스톱모션</option>
|
117 |
+
<option>타임랩스/하이퍼랩스</option>
|
118 |
</select>
|
119 |
</div>
|
120 |
|
|
|
156 |
<label class="block text-sm font-medium mb-2">음악 스타일</label>
|
157 |
<select class="w-full px-3 py-2 bg-gray-800 rounded-lg">
|
158 |
<option>에픽 오케스트라</option>
|
159 |
+
<option>시네마틱 트레일러</option>
|
160 |
+
<option>로파이 힙합</option>
|
161 |
+
<option>퓨처 베이스</option>
|
162 |
+
<option>하우스/테크노</option>
|
163 |
+
<option>드럼앤베이스</option>
|
164 |
+
<option>트랩/EDM</option>
|
165 |
+
<option>재즈 피아노</option>
|
166 |
+
<option>어쿠스틱 팝</option>
|
167 |
+
<option>인디록</option>
|
168 |
+
<option>발라드(감성)</option>
|
169 |
+
<option>국악 크로스오버</option>
|
170 |
+
<option>어린이 동요</option>
|
171 |
+
<option>성악/클래식</option>
|
172 |
+
<option>카페 재즈</option>
|
173 |
+
<option>레트로 신스웨이브</option>
|
174 |
+
<option>8비트 게임음악</option>
|
175 |
+
<option>뉴에이지 명상</option>
|
176 |
+
<option>펑크/소울</option>
|
177 |
+
<option>월드뮤직(아프리카/라틴)</option>
|
178 |
</select>
|
179 |
</div>
|
180 |
|
|
|
268 |
// Lucide 아이콘 초기화
|
269 |
lucide.createIcons();
|
270 |
|
271 |
+
// 실제 진행률
|
272 |
let progress = 0;
|
273 |
const progressBar = document.getElementById('progressBar');
|
274 |
const progressText = document.getElementById('progressText');
|
275 |
+
|
276 |
+
// Veo API로 실제 영상 생성
|
277 |
+
async function generateVideo(prompt) {
|
278 |
+
const requestBody = {
|
279 |
+
instances: [{
|
280 |
+
prompt: prompt
|
281 |
+
}],
|
282 |
+
parameters: {
|
283 |
+
aspectRatio: "16:9",
|
284 |
+
durationSeconds: 8
|
285 |
+
}
|
286 |
+
};
|
287 |
+
|
288 |
+
const res = await fetch(
|
289 |
+
`https://generativelanguage.googleapis.com/v1beta/videos:generate?key=${API_KEY}`,
|
290 |
+
{
|
291 |
+
method: "POST",
|
292 |
+
headers: { "Content-Type": "application/json" },
|
293 |
+
body: JSON.stringify(requestBody)
|
294 |
+
}
|
295 |
+
);
|
296 |
+
const data = await res.json();
|
297 |
+
return data.name; // operation name
|
298 |
+
}
|
299 |
+
|
300 |
+
async function getOperationStatus(name) {
|
301 |
+
const res = await fetch(
|
302 |
+
`https://generativelanguage.googleapis.com/v1beta/${name}?key=${API_KEY}`
|
303 |
+
);
|
304 |
+
return await res.json();
|
305 |
+
}
|
306 |
+
|
307 |
+
async function pollUntilDone(name) {
|
308 |
+
progress = 0;
|
309 |
+
const check = setInterval(async () => {
|
310 |
+
const op = await getOperationStatus(name);
|
311 |
+
const state = op.done ? 100 : Math.min(op.metadata?.video?.videoProgressPercent || progress + 5, 95);
|
312 |
+
progress = state;
|
313 |
+
progressBar.style.width = progress + '%';
|
314 |
+
progressText.textContent = Math.floor(progress) + '%';
|
315 |
+
|
316 |
+
if (op.done) {
|
317 |
+
clearInterval(check);
|
318 |
+
progressBar.style.width = '100%';
|
319 |
+
progressText.textContent = '100%';
|
320 |
+
|
321 |
+
const videoUri = op.response?.video?.videoUri;
|
322 |
+
if (videoUri) {
|
323 |
+
document.querySelector('.aspect-video').innerHTML =
|
324 |
+
`<video class="w-full h-full rounded-lg" controls src="${videoUri}"></video>`;
|
325 |
+
}
|
326 |
setTimeout(() => {
|
327 |
document.getElementById('modal').classList.remove('hidden');
|
328 |
document.getElementById('modal').classList.add('flex');
|
329 |
}, 500);
|
330 |
}
|
331 |
+
}, 2000);
|
332 |
+
}
|
333 |
+
|
334 |
+
async function handleGenerate() {
|
335 |
+
const prompt = document.querySelector('textarea').value.trim();
|
336 |
+
if (!prompt) {
|
337 |
+
alert('프롬프트를 입력하세요!');
|
338 |
+
return;
|
339 |
+
}
|
340 |
+
try {
|
341 |
+
const operationName = await generateVideo(prompt);
|
342 |
+
await pollUntilDone(operationName);
|
343 |
+
} catch (e) {
|
344 |
+
alert('영상 생성 실패: ' + e.message);
|
345 |
+
}
|
346 |
}
|
347 |
|
348 |
// 드래그 앤 드롭
|
|
|
367 |
});
|
368 |
|
369 |
// AI 생성 버튼 클릭
|
370 |
+
document.querySelector('button').addEventListener('click', handleGenerate);
|
371 |
|
372 |
// 모달 닫기
|
373 |
function closeModal() {
|