<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>MelindaAI AutoVideo - Advanced Video Creator</title>
<!-- Include GSAP from CDN -->
<script src="https://c...content-available-to-author-only...e.com/ajax/libs/gsap/3.11.5/gsap.min.js"></script>
<!-- Include CCapture.js from CDN -->
<script src="https://c...content-available-to-author-only...r.net/npm/ccapture.js@1.1.10/build/CCapture.all.min.js"></script>
<!-- Include ffmpeg.wasm from CDN -->
<script src="https://c...content-available-to-author-only...r.net/npm/@ffmpeg/ffmpeg@0.11.5/dist/ffmpeg.min.js"></script>
<style>
body {
margin: 0;
font-family: Arial, sans-serif;
background-color: #f9fafb;
}
#app {
display: flex;
flex-direction: column;
height: 100vh;
}
background-color: #2d3748;
color: #edf2f7;
padding: 20px;
text-align: center;
}
main {
flex: 1;
display: flex;
overflow: hidden;
}
#editor {
width: 300px;
background-color: #edf2f7;
padding: 20px;
overflow-y: auto;
}
#media-section, #slide-section, #audio-section {
margin-bottom: 20px;
}
h2 {
margin-top: 0;
}
#media-gallery, #slide-list {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.media-item, .slide-item {
width: 80px;
height: 80px;
background-size: cover;
background-position: center;
border: 2px solid #cbd5e0;
cursor: pointer;
position: relative;
}
.slide-item .remove-btn {
position: absolute;
top: -10px;
right: -10px;
background-color: #e53e3e;
color: #fff;
border: none;
border-radius: 50%;
width: 20px;
height: 20px;
cursor: pointer;
}
#preview {
flex: 1;
padding: 20px;
display: flex;
flex-direction: column;
}
#canvas {
flex: 1;
background-color: #000;
}
#controls {
margin-top: 10px;
text-align: center;
}
#controls button {
padding: 10px 20px;
margin: 0 5px;
font-size: 16px;
}
</style>
</head>
<body>
<div id="app">
<header>
<h1>MelindaAI AutoVideo - Advanced Video Creator</h1>
</header>
<main>
<section id="editor">
<div id="media-section">
<h2>Media Library</h2>
<input type="file" id="media-input" accept="image/*,video/*" multiple>
<div id="media-gallery"></div>
</div>
<div id="slide-section">
<h2>Slides</h2>
<button id="new-slide-btn">Add Slide</button>
<div id="slide-list"></div>
</div>
<div id="audio-section">
<h2>Audio Settings</h2>
<label>Background Music:</label>
<input type="file" id="background-music-input" accept="audio/*"><br><br>
<label>Voiceover:</label>
<input type="file" id="voiceover-input" accept="audio/*">
</div>
</section>
<section id="preview">
<h2>Preview</h2>
<canvas id="canvas"></canvas>
<div id="controls">
<button id="play-btn">Play</button>
<button id="stop-btn" disabled>Stop</button>
<button id="download-btn" disabled>Download Video</button>
</div>
</section>
</main>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
// Elements
const mediaInput = document.getElementById('media-input');
const mediaGallery = document.getElementById('media-gallery');
const slideList = document.getElementById('slide-list');
const newSlideBtn = document.getElementById('new-slide-btn');
const playBtn = document.getElementById('play-btn');
const stopBtn = document.getElementById('stop-btn');
const downloadBtn = document.getElementById('download-btn');
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const canvasWidth = 1280;
const canvasHeight = 720;
canvas.width = canvasWidth;
canvas.height = canvasHeight;
const backgroundMusicInput = document.getElementById('background-music-input');
const voiceoverInput = document.getElementById('voiceover-input');
// Variables
let mediaItems = [];
let slides = [];
let isPlaying = false;
let currentSlideIndex = 0;
let capturer = null;
let recording = false;
const frameRate = 30;
let mediaRecorder;
let recordedChunks = [];
let backgroundMusic = null;
let voiceover = null;
// Event Listeners
mediaInput.addEventListener('change', handleMediaInput);
newSlideBtn.addEventListener('click', () => addSlide(null));
playBtn.addEventListener('click', startPresentation);
stopBtn.addEventListener('click', stopPresentation);
downloadBtn.addEventListener('click', downloadVideo);
backgroundMusicInput.addEventListener('change', handleBackgroundMusicInput);
voiceoverInput.addEventListener('change', handleVoiceoverInput);
function handleMediaInput(event) {
const files = event.target.files;
for (let
file of files
) { const url
= URL
.createObjectURL
(file); const mediaItem = {
url,
type
: file.type
.startsWith
('video/') ?
'video' : 'image', };
mediaItems.push(mediaItem);
displayMediaItem(mediaItem);
}
}
function displayMediaItem(mediaItem) {
const div = document.createElement('div');
div.classList.add('media-item');
div.style.backgroundImage = `url(${mediaItem.url})`;
div.addEventListener('click', () => addSlide(mediaItem));
mediaGallery.appendChild(div);
}
function addSlide(mediaItem) {
const slide = {
mediaItem,
duration: 5,
title: 'Title Here',
subtitle: 'Subtitle Here',
kenBurns: true,
};
slides.push(slide);
displaySlideItem(slide);
}
function displaySlideItem(slide) {
const div = document.createElement('div');
div.classList.add('slide-item');
if (slide.mediaItem) {
div.style.backgroundImage = `url(${slide.mediaItem.url})`;
} else {
div.style.backgroundColor = '#cbd5e0';
}
const removeBtn = document.createElement('button');
removeBtn.classList.add('remove-btn');
removeBtn.textContent = '×';
removeBtn.addEventListener('click', (e) => {
e.stopPropagation();
removeSlide(slide);
});
div.appendChild(removeBtn);
div.addEventListener('click', () => editSlide(slide));
slideList.appendChild(div);
}
function removeSlide(slide) {
const index = slides.indexOf(slide);
if (index > -1) {
slides.splice(index, 1);
renderSlideList();
}
}
function renderSlideList() {
slideList.innerHTML = '';
slides.forEach((slide) => displaySlideItem(slide));
}
function editSlide(slide) {
const title = prompt('Enter title:', slide.title);
const subtitle = prompt('Enter subtitle:', slide.subtitle);
const duration = prompt('Enter duration (seconds):', slide.duration);
const kenBurns = confirm('Apply Ken Burns effect?');
if (title !== null) slide.title = title;
if (subtitle !== null) slide.subtitle = subtitle;
if (duration !== null) slide.duration = parseFloat(duration) || 5;
slide.kenBurns = kenBurns;
}
function handleBackgroundMusicInput(event) {
const file = event
.target
.files
[0]; const url
= URL
.createObjectURL
(file); backgroundMusic = new Audio(url);
backgroundMusic.loop = true;
backgroundMusic.crossOrigin = 'anonymous';
}
}
function handleVoiceoverInput(event) {
const file = event
.target
.files
[0]; const url
= URL
.createObjectURL
(file); voiceover = new Audio(url);
voiceover.crossOrigin = 'anonymous';
}
}
function startPresentation() {
if (isPlaying || slides.length === 0) return;
isPlaying = true;
playBtn.disabled = true;
stopBtn.disabled = false;
downloadBtn.disabled = true;
// Play audio
if (backgroundMusic) backgroundMusic.play();
if (voiceover) voiceover.play();
// Capture streams
const canvasStream = canvas.captureStream(frameRate);
const audioTracks = [];
if (backgroundMusic && backgroundMusic.captureStream) {
const bgMusicStream = backgroundMusic.captureStream();
audioTracks.push(...bgMusicStream.getAudioTracks());
}
if (voiceover && voiceover.captureStream) {
const voiceoverStream = voiceover.captureStream();
audioTracks.push(...voiceoverStream.getAudioTracks());
}
const combinedStream = new MediaStream([...canvasStream.getVideoTracks(), ...audioTracks]);
mediaRecorder = new MediaRecorder(combinedStream, { mimeType: 'video/webm; codecs=vp9' });
recordedChunks = [];
mediaRecorder.ondataavailable = (event) => {
if (event.data.size > 0) {
recordedChunks.push(event.data);
}
};
mediaRecorder.onstop = () => {
const blob = new Blob(recordedChunks, { type: 'video/webm' });
const url = URL.createObjectURL(blob);
downloadBtn.href = url;
downloadBtn.download = 'presentation.webm';
downloadBtn.disabled = false;
alert('Your video is ready to download.');
};
mediaRecorder.start();
recording = true;
currentSlideIndex = 0;
renderFrame();
}
function stopPresentation() {
if (!isPlaying) return;
isPlaying = false;
playBtn.disabled = false;
stopBtn.disabled = true;
if (backgroundMusic) backgroundMusic.pause();
if (voiceover) voiceover.pause();
mediaRecorder.stop();
recording = false;
}
function renderFrame() {
if (!isPlaying) return;
const slide = slides[currentSlideIndex];
const totalFrames = slide.duration * frameRate;
const frameIndex = (slide.frameIndex || 0) + 1;
slide.frameIndex = frameIndex;
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
if (slide.mediaItem && slide.mediaItem.type === 'image') {
renderImageSlide(slide, frameIndex / totalFrames);
} else if (slide.mediaItem && slide.mediaItem.type === 'video') {
renderVideoSlide(slide);
} else {
ctx.fillStyle = '#000';
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
}
drawText(slide);
if (recording) {
capturer.capture(canvas);
}
if (frameIndex >= totalFrames) {
slide.frameIndex = 0;
currentSlideIndex++;
if (currentSlideIndex >= slides.length) {
stopPresentation();
return;
}
}
requestAnimationFrame(renderFrame);
}
function renderImageSlide(slide, progress) {
const img = new Image();
img.src = slide.mediaItem.url;
img.onload = () => {
let scale = 1;
let x = 0;
let y = 0;
if (slide.kenBurns) {
scale = 1 + 0.2 * progress;
x = (canvasWidth - img.width * scale) / 2;
y = (canvasHeight - img.height * scale) / 2;
}
ctx.drawImage(img, x, y, img.width * scale, img.height * scale);
};
}
function renderVideoSlide(slide) {
if (!slide.videoElement) {
const video = document.createElement('video');
video.src = slide.mediaItem.url;
video.crossOrigin = 'anonymous';
video.load();
video.play();
slide.videoElement = video;
}
const video = slide.videoElement;
ctx.drawImage(video, 0, 0, canvasWidth, canvasHeight);
}
function drawText(slide) {
ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';
ctx.fillRect(0, canvasHeight - 150, canvasWidth, 150);
ctx.fillStyle = '#fff';
ctx.font = 'bold 48px Arial';
ctx.textAlign = 'center';
ctx.fillText(slide.title, canvasWidth / 2, canvasHeight - 100);
ctx.font = '32px Arial';
ctx.fillText(slide.subtitle, canvasWidth / 2, canvasHeight - 50);
}
function downloadVideo() {
alert('Your video is ready for download.');
}
});
</script>
</body>
</html>