This commit is contained in:
peterino2 2026-02-03 01:11:31 -08:00
parent d9ece80418
commit 0a67dc864e
2 changed files with 160 additions and 1 deletions

145
public/upload.js Normal file
View File

@ -0,0 +1,145 @@
(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`);
}
}
});
})();

View File

@ -45,7 +45,21 @@ interface Config {
} }
const CONFIG_PATH = join(import.meta.dir, "config.json"); const CONFIG_PATH = join(import.meta.dir, "config.json");
const config: Config = await file(CONFIG_PATH).json();
const DEFAULT_CONFIG: Config = {
port: 3001,
musicDir: "./music",
allowGuests: true
};
// Create default config if missing
const configFile = file(CONFIG_PATH);
if (!(await configFile.exists())) {
console.log("[Config] Creating default config.json...");
await Bun.write(CONFIG_PATH, JSON.stringify(DEFAULT_CONFIG, null, 2));
}
const config: Config = await configFile.json();
const MUSIC_DIR = resolve(import.meta.dir, config.musicDir); const MUSIC_DIR = resolve(import.meta.dir, config.musicDir);
const PUBLIC_DIR = join(import.meta.dir, "public"); const PUBLIC_DIR = join(import.meta.dir, "public");