Music / index.html
abeea's picture
Update index.html
b0cd8a1 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Cinematic Lyric Studio - Mohsin Kamil Credits</title>
<style>
:root {
--bg: #050505;
--panel: #111111;
--accent: #3498db;
--text: #ffffff;
}
body {
background: var(--bg);
color: var(--text);
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
margin: 0;
display: flex;
height: 100vh;
overflow: hidden;
}
/* LEFT: Preview & Player */
.preview-side {
flex: 1.2;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 20px;
border-right: 1px solid #222;
}
#canvas-wrap {
width: 100%;
max-width: 800px;
aspect-ratio: 16 / 9;
background: #000;
box-shadow: 0 0 50px rgba(0,0,0,1);
border: 1px solid #333;
border-radius: 4px;
overflow: hidden;
}
canvas { width: 100%; height: 100%; display: block; }
.player-ui {
width: 100%;
max-width: 800px;
margin-top: 25px;
background: var(--panel);
padding: 20px;
border-radius: 12px;
border: 1px solid #222;
}
.seek-bar {
width: 100%;
accent-color: var(--accent);
cursor: pointer;
margin-bottom: 15px;
}
.controls {
display: flex;
align-items: center;
gap: 15px;
}
.btn {
padding: 10px 25px;
border-radius: 20px;
border: none;
cursor: pointer;
font-weight: 600;
transition: 0.2s;
}
.btn-play { background: #fff; color: #000; min-width: 100px; }
.btn-export { background: var(--accent); color: #fff; margin-left: auto; }
.btn:hover { transform: scale(1.05); opacity: 0.9; }
/* RIGHT: Editor */
.editor-side {
flex: 0.8;
background: var(--panel);
display: flex;
flex-direction: column;
padding: 20px;
overflow-y: auto;
}
.lyric-card {
background: #1a1a1a;
padding: 12px;
border-radius: 10px;
margin-bottom: 12px;
border: 1px solid #222;
}
.lyric-card input[type="text"] {
width: 100%;
background: transparent;
border: none;
color: #ddd;
font-size: 15px;
margin-bottom: 8px;
outline: none;
}
.timing-row {
display: flex;
gap: 8px;
align-items: center;
font-size: 11px;
color: #777;
}
.timing-row input {
width: 55px;
background: #000;
border: 1px solid #333;
color: var(--accent);
padding: 4px;
border-radius: 4px;
}
.stamp-btn {
background: #333;
color: #ccc;
border: none;
padding: 4px 10px;
border-radius: 4px;
cursor: pointer;
}
#exportOverlay {
position: fixed;
inset: 0;
background: rgba(0,0,0,0.95);
display: none;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 999;
}
</style>
</head>
<body>
<div id="exportOverlay">
<h2>Exporting 1080p Master...</h2>
<div style="width: 300px; height: 6px; background: #222; margin-top: 20px; border-radius: 3px;">
<div id="exportBar" style="width: 0%; height: 100%; background: var(--accent); border-radius: 3px;"></div>
</div>
</div>
<div class="preview-side">
<div style="margin-bottom: 15px;">
<input type="file" id="audioUpload" accept="audio/*" hidden>
<button class="btn btn-play" onclick="document.getElementById('audioUpload').click()">📁 Select Song</button>
</div>
<div id="canvas-wrap">
<canvas id="mainCanvas" width="1920" height="1080"></canvas>
</div>
<div class="player-ui">
<input type="range" class="seek-bar" id="seekBar" value="0" min="0" step="0.01">
<div class="controls">
<span id="timeDisplay" style="font-family: monospace;">00:00 / 00:00</span>
<button class="btn btn-play" id="playBtn">PLAY</button>
<button class="btn btn-export" id="exportBtn">EXPORT 1080p</button>
</div>
</div>
</div>
<div class="editor-side" id="editorList">
<!-- Rows generated by JS -->
</div>
<script>
const canvas = document.getElementById('mainCanvas');
const ctx = canvas.getContext('2d');
const audio = new Audio();
const playBtn = document.getElementById('playBtn');
const seekBar = document.getElementById('seekBar');
const editorList = document.getElementById('editorList');
// INITIAL DATA (Synced to the Audio)
let lyrics = [
{ start: 6.0, end: 19.0, text: "Lyrics by: Mohsin Kamil" }, // THE CREDITS
{ start: 31.5, end: 34.5, text: "Kaash hum na milte..." },
{ start: 35.5, end: 40.5, text: "Naa hotey judaa..." },
{ start: 41.5, end: 50.5, text: "Milna bhi tu tha Khuda ka faisla." },
{ start: 52.0, end: 56.5, text: "Pal do pal mile phir," },
{ start: 57.5, end: 62.5, text: "ho gaye judaa..." },
{ start: 63.5, end: 76.0, text: "Janey aesa kyon kiya tuu ney Khuda..." },
{ start: 77.0, end: 84.0, text: "Kiya thi wajah," },
{ start: 84.5, end: 90.0, text: "do na mujh ko ishara..." },
{ start: 90.5, end: 96.0, text: "Mein hun tootaa," },
{ start: 96.5, end: 105.0, text: "mein hun haara." },
{ start: 105.5, end: 113.0, text: "Shayer tha shayad mujhe banana..." },
{ start: 113.5, end: 126.0, text: "Isi liye zaroori tha kisi ko bewafa banana..." },
{ start: 126.5, end: 132.5, text: "Shayad... mein na tha us ke liye sahi..." },
{ start: 133.0, end: 139.0, text: "Shayad... wo na tha acha merey liye..." },
{ start: 139.5, end: 149.0, text: "Shayad... na hotey judaa tu bari hoti saza..." },
{ start: 149.5, end: 160.0, text: "Shayad... pal do pal mil ke bichadna hi thaa." },
{ start: 160.5, end: 165.0, text: "Pal do pal mile phir," },
{ start: 165.5, end: 171.0, text: "ho gaye judaa..." },
{ start: 171.5, end: 181.0, text: "Janey aesa kyon kiya... Khuda..." }
];
// BACKGROUND PARTICLES
const particles = Array.from({ length: 60 }, () => ({
x: Math.random() * 1920, y: Math.random() * 1080,
s: Math.random() * 2 + 1, v: Math.random() * 0.4 + 0.1, o: Math.random() * 0.4
}));
function drawParticles() {
particles.forEach(p => {
p.y -= p.v; if (p.y < 0) p.y = 1080;
ctx.fillStyle = `rgba(255,255,255,${p.o})`;
ctx.beginPath(); ctx.arc(p.x, p.y, p.s, 0, Math.PI*2); ctx.fill();
});
}
// CINEMATIC RENDERER
function render() {
const t = audio.currentTime;
ctx.fillStyle = "#000"; ctx.fillRect(0, 0, 1920, 1080);
const grad = ctx.createRadialGradient(960, 540, 50, 960, 540, 1100);
grad.addColorStop(0, "#0d0e12"); grad.addColorStop(1, "#000");
ctx.fillStyle = grad; ctx.fillRect(0,0,1920,1080);
drawParticles();
const active = lyrics.find(l => t >= l.start && t <= l.end);
if (active) {
const elapsed = t - active.start;
const remaining = active.end - t;
let alpha = 1;
if (elapsed < 1.0) alpha = elapsed / 1.0;
if (remaining < 1.0) alpha = remaining / 1.0;
ctx.save();
ctx.translate(960, 540);
const scale = 1 + (elapsed * 0.012);
ctx.scale(scale, scale);
ctx.filter = `blur(${(1-alpha)*15}px)`;
ctx.textAlign = "center"; ctx.textBaseline = "middle";
// Stylize Credits vs Lyrics
if(active.text.includes("Lyrics by")) {
ctx.font = "300 45px 'Segoe UI Light', sans-serif";
ctx.letterSpacing = "4px";
} else {
ctx.font = "italic 75px 'Georgia'";
ctx.shadowBlur = 20; ctx.shadowColor = "rgba(255,255,255,0.4)";
}
ctx.fillStyle = `rgba(255,255,255,${alpha})`;
ctx.fillText(active.text, 0, 0);
ctx.restore();
}
if (!audio.paused || isExporting) requestAnimationFrame(render);
}
// EDITOR ENGINE
function refreshEditor() {
editorList.innerHTML = '<h3>Scene & Lyrics Timeline</h3>';
lyrics.forEach((l, i) => {
const div = document.createElement('div');
div.className = 'lyric-card';
div.innerHTML = `
<input type="text" value="${l.text}" oninput="lyrics[${i}].text=this.value">
<div class="timing-row">
S: <input type="number" step="0.1" value="${l.start}" onchange="lyrics[${i}].start=parseFloat(this.value)">
<button class="stamp-btn" onclick="setIn(${i})">⏱️ In</button>
E: <input type="number" step="0.1" value="${l.end}" onchange="lyrics[${i}].end=parseFloat(this.value)">
<button class="stamp-btn" onclick="setOut(${i})">⏱️ Out</button>
</div>
`;
editorList.appendChild(div);
});
}
window.setIn = (i) => { lyrics[i].start = Math.round(audio.currentTime*10)/10; refreshEditor(); };
window.setOut = (i) => { lyrics[i].end = Math.round(audio.currentTime*10)/10; refreshEditor(); };
document.getElementById('audioUpload').onchange = (e) => {
audio.src = URL.createObjectURL(e.target.files[0]);
audio.onloadedmetadata = () => { seekBar.max = audio.duration; refreshEditor(); render(); };
};
playBtn.onclick = () => {
if (audio.paused) { audio.play(); playBtn.innerText = "PAUSE"; render(); }
else { audio.pause(); playBtn.innerText = "PLAY"; }
};
audio.ontimeupdate = () => {
seekBar.value = audio.currentTime;
document.getElementById('timeDisplay').innerText = `${fmt(audio.currentTime)} / ${fmt(audio.duration)}`;
};
seekBar.oninput = () => { audio.currentTime = seekBar.value; render(); };
function fmt(s) { return new Date(s * 1000).toISOString().substr(14, 5); }
// EXPORT HANDLER
let isExporting = false;
document.getElementById('exportBtn').onclick = async () => {
isExporting = true; audio.pause(); audio.currentTime = 0;
document.getElementById('exportOverlay').style.display = 'flex';
const stream = canvas.captureStream(60);
const audioCtx = new AudioContext();
const source = audioCtx.createMediaElementSource(audio);
const dest = audioCtx.createMediaStreamDestination();
source.connect(dest); source.connect(audioCtx.destination);
const recorder = new MediaRecorder(new MediaStream([...stream.getTracks(), ...dest.stream.getTracks()]), {
mimeType: 'video/webm;codecs=vp9', videoBitsPerSecond: 20000000
});
let chunks = [];
recorder.ondataavailable = e => chunks.push(e.data);
recorder.onstop = () => {
const a = document.createElement('a');
a.href = URL.createObjectURL(new Blob(chunks, {type:'video/webm'}));
a.download = 'Kaash_Hum_Na_Milte_Master.webm'; a.click();
location.reload();
};
recorder.start(); audio.play();
const intv = setInterval(() => {
document.getElementById('exportBar').style.width = (audio.currentTime/audio.duration)*100 + '%';
if (audio.ended) { clearInterval(intv); recorder.stop(); }
}, 100);
};
refreshEditor();
</script>
</body>
</html>