blastoise-archive/public/upload.js

146 lines
4.1 KiB
JavaScript

(function() {
const M = window.MusicRoom;
document.addEventListener("DOMContentLoaded", () => {
const uploadBtn = M.$("#btn-upload");
const fileInput = M.$("#file-input");
const dropzone = M.$("#upload-dropzone");
const libraryPanel = M.$("#library-panel");
const progressEl = M.$("#upload-progress");
const progressBar = progressEl?.querySelector(".upload-progress-bar");
const progressText = progressEl?.querySelector(".upload-progress-text");
if (!uploadBtn || !fileInput || !dropzone) return;
// Click upload button opens file dialog
uploadBtn.onclick = () => {
if (!M.currentUser) {
M.showToast("Sign in to upload");
return;
}
fileInput.click();
};
// File input change
fileInput.onchange = () => {
if (fileInput.files.length > 0) {
uploadFiles(fileInput.files);
fileInput.value = "";
}
};
// Drag and drop on library panel
let dragCounter = 0;
libraryPanel.ondragenter = (e) => {
if (!M.currentUser) return;
if (!e.dataTransfer.types.includes("Files")) return;
e.preventDefault();
dragCounter++;
dropzone.classList.remove("hidden");
};
libraryPanel.ondragleave = (e) => {
e.preventDefault();
dragCounter--;
if (dragCounter === 0) {
dropzone.classList.add("hidden");
}
};
libraryPanel.ondragover = (e) => {
if (!M.currentUser) return;
if (!e.dataTransfer.types.includes("Files")) return;
e.preventDefault();
e.dataTransfer.dropEffect = "copy";
};
libraryPanel.ondrop = (e) => {
e.preventDefault();
dragCounter = 0;
dropzone.classList.add("hidden");
if (!M.currentUser) {
M.showToast("Sign in to upload");
return;
}
const files = e.dataTransfer.files;
if (files.length > 0) {
uploadFiles(files);
}
};
function showProgress(current, total, filename) {
if (!progressEl) return;
const pct = Math.round((current / total) * 100);
progressBar.style.width = pct + "%";
progressText.textContent = `Uploading ${current}/${total}: ${filename}`;
progressEl.classList.remove("hidden");
uploadBtn.classList.add("hidden");
}
function hideProgress() {
if (!progressEl) return;
progressEl.classList.add("hidden");
uploadBtn.classList.remove("hidden");
}
async function uploadFiles(files) {
const validExts = [".mp3", ".ogg", ".flac", ".wav", ".m4a", ".aac", ".opus", ".wma", ".mp4"];
const audioFiles = [...files].filter(f => {
const ext = f.name.toLowerCase().match(/\.[^.]+$/)?.[0];
return ext && validExts.includes(ext);
});
if (audioFiles.length === 0) {
M.showToast("No valid audio files");
return;
}
let uploaded = 0;
let failed = 0;
let duplicates = 0;
const total = audioFiles.length;
for (let i = 0; i < audioFiles.length; i++) {
const file = audioFiles[i];
showProgress(i + 1, total, file.name);
try {
const formData = new FormData();
formData.append("file", file);
const res = await fetch("/api/upload", {
method: "POST",
body: formData
});
if (res.ok) {
uploaded++;
} else if (res.status === 409) {
// File already exists
M.showToast(`Already uploaded: ${file.name}`, "warning");
duplicates++;
} else {
const err = await res.json().catch(() => ({}));
console.error(`Upload failed for ${file.name}:`, err.error || res.status);
failed++;
}
} catch (e) {
console.error(`Upload error for ${file.name}:`, e);
failed++;
}
}
hideProgress();
if (uploaded > 0) {
M.showToast(`Uploaded ${uploaded} track${uploaded > 1 ? 's' : ''}${failed > 0 ? `, ${failed} failed` : ''}`);
} else {
M.showToast(`Upload failed`);
}
}
});
})();