alinamarquardt.com/manage-audio/index.html

111 lines
3.3 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Audio Uploader</title>
<link rel="stylesheet" href="https://unpkg.com/dropzone@5/dist/min/dropzone.min.css" />
<script src="https://unpkg.com/dropzone@5/dist/min/dropzone.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.min.js"></script>
<style>
body { font-family: sans-serif; padding: 2rem; width:500px; margin: 0 auto; }
.dropzn { border: 2px dashed #666; padding: 2rem; margin-bottom: 2rem; }
#playlist { padding-left: 0; }
#playlist li { margin-bottom: 0.5rem; background: #eee; padding: 0.5rem; border-radius: 4px; cursor: grab; display: flex; justify-content: space-between; align-items: center; }
#playlist li span { width: 85%; }
</style>
</head>
<body>
<h1>Upload Audio</h1>
<form action="http://localhost:3000/upload" class="dropzn" id="audio-dropzone">
<div class="dz-message">Drop audio files here or click to upload</div>
</form>
<h2>Playlist</h2>
<ul id="playlist"></ul>
<script>
Dropzone.autoDiscover = false;
new Dropzone("#audio-dropzone", {
url: "http://localhost:3000/upload",
method: "post",
paramName: "file", // matches Multer field
maxFilesize: 20,
acceptedFiles: "audio/*",
init: function () {
this.on("sending", function(file, xhr, formData) {
const title = prompt("Enter title for: " + file.name);
formData.append("title", title || file.name);
const token = btoa("admin:supersecret");
xhr.setRequestHeader("Authorization", "Basic " + token);
});
this.on("success", function() {
setTimeout(loadPlaylist, 500);
});
}
});
async function fetchPlaylist() {
const res = await fetch("http://localhost:3000/api/playlist");
return await res.json();
}
async function loadPlaylist() {
const list = document.getElementById("playlist");
list.innerHTML = "";
const tracks = await fetchPlaylist();
tracks.forEach(track => {
const li = document.createElement("li");
li.setAttribute("data-url", track.url);
li.innerHTML = `
🎵 <span>${track.title}</span>
<button onclick="deleteTrack('${track.url}')">X </button>
`;
list.appendChild(li);
});
}
async function deleteTrack(url) {
const filename = url.split("/").pop();
const res = await fetch("http://localhost:3000/delete", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Basic " + btoa("admin:supersecret")
},
body: JSON.stringify({ filename })
});
if (res.ok) loadPlaylist();
}
function saveNewOrder() {
const items = document.querySelectorAll("#playlist li");
const newOrder = Array.from(items).map(li => ({
title: li.querySelector("span").textContent.trim(),
url: li.getAttribute("data-url")
}));
fetch("http://localhost:3000/reorder", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Basic " + btoa("admin:supersecret")
},
body: JSON.stringify({ playlist: newOrder })
});
}
new Sortable(document.getElementById("playlist"), {
animation: 150,
onEnd: saveNewOrder
});
loadPlaylist();
</script>
</body>
</html>