Loading first song…
];
let songs = [];
let currentFilter = "All";
let currentVideoId = null;
/* ELEMENTS */
const playerIframe = document.getElementById("player");
const currentTitleEl = document.getElementById("currentTitle");
const searchInput = document.getElementById("searchInput");
const openYTBtn = document.getElementById("openYTBtn");
const stopBtn = document.getElementById("stopBtn");
/* INIT */
function init() {
let saved = localStorage.getItem(KEY);
if (saved) {
songs = JSON.parse(saved);
} else {
songs = defaultSongs;
save();
}
render();
// Auto-load first song
if (songs.length > 0) {
setPlayerToSong(songs[0], true); // autoplay=true
} else {
currentTitleEl.textContent = "Select a song to play.";
}
}
/* SAVE */
function save() {
localStorage.setItem(KEY, JSON.stringify(songs));
}
/* BUILD EMBED URL */
function buildEmbedUrl(id, autoplay=true) {
const params = new URLSearchParams({
autoplay: autoplay ? "1" : "0",
rel: "0",
modestbranding: "1"
});
return `https://www.youtube.com/embed/${id}?${params.toString()}`;
}
/* SET PLAYER */
function setPlayerToSong(song, autoplay=true) {
currentVideoId = song.id;
playerIframe.src = buildEmbedUrl(song.id, autoplay);
currentTitleEl.textContent = song.title;
}
/* OPEN ON YOUTUBE */
openYTBtn.addEventListener("click", () => {
if (!currentVideoId) return;
window.open("https://www.youtube.com/watch?v=" + currentVideoId, "_blank");
});
/* STOP BUTTON */
stopBtn.addEventListener("click", () => {
playerIframe.src = "";
currentTitleEl.textContent = "Playback stopped. Select a song to play.";
});
/* FILTER */
function setFilter(f) {
currentFilter = f;
document.querySelectorAll(".filter button").forEach(btn =>
btn.classList.remove("active")
);
document.getElementById("f-" + f).classList.add("active");
render();
}
/* RESTORE DEFAULTS (merges missing defaults, keeps custom) */
function restoreDefaults() {
defaultSongs.forEach(d => {
if (!songs.find(s => s.id === d.id)) {
songs.unshift(d);
}
});
save();
render();
}
/* EXTRACT ID */
function extractId(url) {
const patterns = [/v=([^&]+)/, /youtu\.be\/([^?]+)/, /embed\/([^?]+)/];
for (let p of patterns) {
const m = url.match(p);
if (m) return m[1];
}
return null;
}
/* ADD SONG */
function addSong() {
const title = document.getElementById("songTitle").value.trim();
const url = document.getElementById("songUrl").value.trim();
const id = extractId(url);
if (!title || !id) {
alert("Invalid title or YouTube link.");
return;
}
songs.push({ id, title, cat:"Custom" });
save();
render();
document.getElementById("songTitle").value = "";
document.getElementById("songUrl").value = "";
}
/* DELETE SONG */
function deleteSong(index) {
songs.splice(index, 1);
save();
render();
// If current video was deleted, just leave player as-is (no reset required)
}
/* DRAG SORT */
let draggedIndex = null;
function handleDragStart(e, index) {
draggedIndex = index;
e.target.classList.add("dragging");
}
function handleDragEnd(e) {
e.target.classList.remove("dragging");
}
function handleDragOver(e, index) {
e.preventDefault();
if (draggedIndex === null || draggedIndex === index) return;
const draggedItem = songs[draggedIndex];
songs.splice(draggedIndex, 1);
songs.splice(index, 0, draggedItem);
draggedIndex = index;
save();
render();
}
/* RENDER PLAYLIST */
function render() {
const grid = document.getElementById("songGrid");
grid.innerHTML = "";
const search = searchInput.value.toLowerCase();
const filtered = songs.filter(s =>
(currentFilter === "All" || s.cat === currentFilter) &&
s.title.toLowerCase().includes(search)
);
filtered.forEach(s => {
const realIndex = songs.indexOf(s);
const card = document.createElement("div");
card.className = "card";
card.draggable = true;
card.ondragstart = e => handleDragStart(e, realIndex);
card.ondragend = handleDragEnd;
card.ondragover = e => handleDragOver(e, realIndex);
card.innerHTML = `