harmosplit / static /app.js
indigo0511
initial: HarmoSplit app
d90b8a8
// app.js โ€” HarmoSplit ใƒ•ใƒญใƒณใƒˆใ‚จใƒณใƒ‰ใƒญใ‚ธใƒƒใ‚ฏ๏ผˆStripe ่ช่จผๅฏพๅฟœ๏ผ‰
const dropZone = document.getElementById('dropZone');
const fileInput = document.getElementById('fileInput');
const startBtn = document.getElementById('startBtn');
const instVol = document.getElementById('instVol');
const instVolLabel= document.getElementById('instVolLabel');
const modelSelect = document.getElementById('modelSelect');
const useMdx = document.getElementById('useMdx');
const uploadSection = document.getElementById('uploadSection');
const progressSection= document.getElementById('progressSection');
const resultSection = document.getElementById('resultSection');
const errorSection = document.getElementById('errorSection');
const progressBar = document.getElementById('progressBar');
const progressPct = document.getElementById('progressPct');
const progressFile = document.getElementById('progressFile');
const logBox = document.getElementById('logBox');
const downloadBtn = document.getElementById('downloadBtn');
const errorMsg = document.getElementById('errorMsg');
let selectedFile = null;
let verifiedToken = '';
// โ”€โ”€ ่ตทๅ‹•ๆ™‚: Stripe ๆœ‰ๅŠนใ‹ใฉใ†ใ‹็ขบ่ช โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
async function initAuth() {
try {
const res = await fetch('/auth-mode');
const data = await res.json();
if (!data.free_mode) {
// ๆœ‰ๆ–™ใƒขใƒผใƒ‰: ใƒˆใƒผใ‚ฏใƒณใ‚จใƒชใ‚ขใ‚’่กจ็คบ
document.getElementById('tokenArea').style.display = 'block';
// ใƒญใƒผใ‚ซใƒซใ‚นใƒˆใƒฌใƒผใ‚ธใซไฟๅญ˜ๆธˆใฟใƒˆใƒผใ‚ฏใƒณใŒใ‚ใ‚Œใฐๅพฉๅ…ƒ
const saved = localStorage.getItem('hmsplit_token');
if (saved) {
document.getElementById('tokenInput').value = saved;
await verifyToken(saved, false);
}
} else {
// ็„กๆ–™ใƒขใƒผใƒ‰: ใƒˆใƒผใ‚ฏใƒณไธ่ฆ
verifiedToken = 'FREE';
}
} catch (e) {
verifiedToken = 'FREE'; // ใ‚จใƒฉใƒผๆ™‚ใฏ็„กๆ–™ๆ‰ฑใ„
}
}
// โ”€โ”€ ใƒˆใƒผใ‚ฏใƒณๆคœ่จผ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
async function verifyToken(token, showAlert = true) {
try {
const res = await fetch('/verify-token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token }),
});
const data = await res.json();
if (data.valid) {
verifiedToken = token;
localStorage.setItem('hmsplit_token', token);
document.getElementById('tokenOk').classList.remove('hidden');
document.getElementById('tokenInput').style.borderColor = 'var(--success)';
} else {
verifiedToken = '';
localStorage.removeItem('hmsplit_token');
document.getElementById('tokenOk').classList.add('hidden');
document.getElementById('tokenInput').style.borderColor = 'var(--error)';
if (showAlert) alert('ใƒˆใƒผใ‚ฏใƒณใŒ็„กๅŠนใงใ™ใ€‚ๆ–™้‡‘ใƒšใƒผใ‚ธใ‹ใ‚‰็™ป้Œฒใ—ใฆใใ ใ•ใ„ใ€‚');
}
return data.valid;
} catch {
return false;
}
}
document.getElementById('verifyBtn')?.addEventListener('click', async () => {
const token = document.getElementById('tokenInput').value.trim();
if (!token) { alert('ใƒˆใƒผใ‚ฏใƒณใ‚’ๅ…ฅๅŠ›ใ—ใฆใใ ใ•ใ„'); return; }
await verifyToken(token, true);
});
// โ”€โ”€ ใƒ•ใ‚กใ‚คใƒซ้ธๆŠž โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
function selectFile(file) {
selectedFile = file;
dropZone.classList.add('has-file');
dropZone.querySelector('.drop-icon').textContent = fileIsVideo(file.name) ? '๐ŸŽฌ' : '๐ŸŽต';
const sub = dropZone.querySelector('.drop-sub');
sub.textContent = file.name;
sub.classList.add('drop-filename');
updateStartBtn();
}
function fileIsVideo(name) {
return /\.(mp4|mov|avi|mkv|m4v|flv|webm|ts)$/i.test(name);
}
function updateStartBtn() {
const hasFile = !!selectedFile;
const hasToken = !!verifiedToken;
startBtn.disabled = !(hasFile && hasToken);
}
dropZone.addEventListener('dragover', e => { e.preventDefault(); dropZone.classList.add('dragover'); });
dropZone.addEventListener('dragleave', () => dropZone.classList.remove('dragover'));
dropZone.addEventListener('drop', e => {
e.preventDefault(); dropZone.classList.remove('dragover');
if (e.dataTransfer.files.length) selectFile(e.dataTransfer.files[0]);
});
dropZone.addEventListener('click', () => fileInput.click());
fileInput.addEventListener('change', () => { if (fileInput.files.length) selectFile(fileInput.files[0]); });
instVol.addEventListener('input', () => {
instVolLabel.textContent = Math.round(instVol.value * 100) + '%';
});
// โ”€โ”€ ๅ‡ฆ็†้–‹ๅง‹ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
startBtn.addEventListener('click', async () => {
if (!selectedFile || !verifiedToken) return;
uploadSection.classList.add('hidden');
progressSection.classList.remove('hidden');
logBox.innerHTML = '';
progressBar.style.width = '0%';
progressPct.textContent = '0%';
progressFile.textContent = selectedFile.name;
const fd = new FormData();
fd.append('file', selectedFile);
fd.append('inst_vol', instVol.value);
fd.append('model', modelSelect.value);
fd.append('use_mdx', useMdx.checked ? 'true' : 'false');
fd.append('token', verifiedToken === 'FREE' ? '' : verifiedToken);
let jobId;
try {
const res = await fetch('/upload', { method: 'POST', body: fd });
const data = await res.json();
if (!res.ok || data.error) throw new Error(data.error || 'ใ‚ขใƒƒใƒ—ใƒญใƒผใƒ‰ๅคฑๆ•—');
jobId = data.job_id;
} catch (e) {
showError(e.message); return;
}
const sse = new EventSource(`/progress/${jobId}`);
sse.addEventListener('message', e => {
let payload;
try { payload = JSON.parse(e.data); } catch { return; }
if (payload.status) {
sse.close();
if (payload.status === 'done') showResult(jobId);
else showError('ๅ‡ฆ็†ไธญใซใ‚จใƒฉใƒผใŒ็™บ็”Ÿใ—ใพใ—ใŸใ€‚ใƒญใ‚ฐใ‚’็ขบ่ชใ—ใฆใใ ใ•ใ„ใ€‚');
return;
}
if (payload.msg) {
const p = document.createElement('p');
p.textContent = payload.msg;
if (payload.msg.includes('โœ…') || payload.msg.includes('ๅฎŒไบ†')) p.classList.add('ok');
if (payload.msg.includes('โŒ')) p.classList.add('err');
logBox.appendChild(p);
logBox.scrollTop = logBox.scrollHeight;
}
if (payload.pct !== undefined) {
progressBar.style.width = payload.pct + '%';
progressPct.textContent = payload.pct + '%';
}
});
sse.onerror = () => {
sse.close();
setTimeout(async () => {
try {
const res = await fetch(`/status/${jobId}`);
const data = await res.json();
if (data.status === 'done') showResult(jobId);
else showError(data.error || 'ๆŽฅ็ถšใ‚จใƒฉใƒผ');
} catch { showError('ใ‚ตใƒผใƒใƒผใจใฎๆŽฅ็ถšใŒๅˆ‡ใ‚Œใพใ—ใŸ'); }
}, 500);
};
});
function showResult(jobId) {
progressSection.classList.add('hidden');
resultSection.classList.remove('hidden');
downloadBtn.href = `/download/${jobId}`;
}
function showError(msg) {
progressSection.classList.add('hidden');
errorSection.classList.remove('hidden');
errorMsg.textContent = msg;
}
function reset() {
selectedFile = null;
fileInput.value = '';
dropZone.classList.remove('has-file', 'dragover');
dropZone.querySelector('.drop-icon').textContent = '๐ŸŽต';
const sub = dropZone.querySelector('.drop-sub');
sub.textContent = 'ใพใŸใฏ'; sub.classList.remove('drop-filename');
progressBar.style.width = '0%'; progressPct.textContent = '0%';
logBox.innerHTML = '';
uploadSection.classList.remove('hidden');
progressSection.classList.add('hidden');
resultSection.classList.add('hidden');
errorSection.classList.add('hidden');
updateStartBtn();
}
document.getElementById('resetBtn').addEventListener('click', reset);
document.getElementById('resetBtnErr').addEventListener('click', reset);
// ่ตทๅ‹•
initAuth().then(() => updateStartBtn());