azer / static /app.js
gooookim's picture
Update static/app.js
da3ed67 verified
Raw
History Blame Contribute Delete
7.68 kB
const pageLabel = document.getElementById("pageLabel");
const statusText = document.getElementById("status");
const slideImage = document.getElementById("slideImage");
const imageFallback = document.getElementById("imageFallback");
const slideTitle = document.getElementById("slideTitle");
const slideScript = document.getElementById("slideScript");
const prevBtn = document.getElementById("prevBtn");
const playBtn = document.getElementById("playBtn");
const stopBtn = document.getElementById("stopBtn");
const nextBtn = document.getElementById("nextBtn");
const player = document.getElementById("player");
const workspace = document.getElementById("workspace");
const slidePane = document.getElementById("slidePane");
const scriptPane = document.getElementById("scriptPane");
const resizer = document.getElementById("resizer");
let slides = [];
let currentIndex = 0;
let isPlaying = false;
let isPaused = false;
let isDragging = false;
let pointerId = null;
function setStatus(text) {
statusText.textContent = text;
}
function updatePlayButton() {
if (isPlaying && !isPaused) {
playBtn.textContent = "⏸ Pause";
return;
}
if (isPlaying && isPaused) {
playBtn.textContent = "▶ Resume";
return;
}
playBtn.textContent = "▶ Play";
}
function updateButtons() {
prevBtn.disabled = currentIndex <= 0;
nextBtn.disabled = currentIndex >= slides.length - 1;
}
function renderSlide(index) {
if (!slides.length) {
pageLabel.textContent = "No slides";
slideTitle.textContent = "";
slideScript.textContent = "No script data was loaded.";
setStatus("No slides");
updateButtons();
return;
}
if (index < 0) index = 0;
if (index >= slides.length) index = slides.length - 1;
currentIndex = index;
const slide = slides[currentIndex];
pageLabel.textContent = `Slide ${currentIndex} / ${slides.length - 1}`;
slideTitle.textContent = slide.title || `Slide ${currentIndex}`;
slideScript.textContent = slide.speech || "";
imageFallback.hidden = true;
slideImage.hidden = false;
slideImage.onerror = () => {
slideImage.hidden = true;
imageFallback.hidden = false;
};
slideImage.src = `${slide.image}?t=${Date.now()}`;
updateButtons();
if (!isPlaying) {
setStatus("Ready");
}
}
async function loadSlides() {
try {
const response = await fetch("/api/slides");
if (!response.ok) {
throw new Error(`Failed to load slides: ${response.status}`);
}
slides = await response.json();
if (!Array.isArray(slides) || slides.length === 0) {
throw new Error("No slides found. Check scripts.txt.");
}
renderSlide(0);
setStatus(`Ready · ${slides.length} slides`);
updatePlayButton();
} catch (error) {
console.error(error);
pageLabel.textContent = "Load failed";
slideTitle.textContent = "Error";
slideScript.textContent = String(error.message || error);
setStatus("Error");
}
}
function stopAudio(status = "Stopped") {
isPlaying = false;
isPaused = false;
player.pause();
player.removeAttribute("src");
player.load();
setStatus(status);
updatePlayButton();
}
function playCurrent() {
if (!slides.length) return;
isPlaying = true;
isPaused = false;
const slide = slides[currentIndex];
setStatus(`Reading ${currentIndex + 1}/${slides.length}`);
player.pause();
player.currentTime = 0;
player.src = `${slide.audio}?t=${Date.now()}`;
updatePlayButton();
player.play().catch(error => {
console.error(error);
stopAudio("Audio play failed");
});
}
function goToSlide(index, keepPlaying = false) {
const wasPlaying =
(isPlaying && !isPaused) || keepPlaying;
player.pause();
player.currentTime = 0;
renderSlide(index);
if (wasPlaying) {
playCurrent();
} else {
isPlaying = false;
isPaused = false;
updatePlayButton();
}
}
prevBtn.addEventListener("click", () => {
if (currentIndex <= 0) return;
goToSlide(currentIndex - 1, isPlaying && !isPaused);
});
nextBtn.addEventListener("click", () => {
if (currentIndex >= slides.length - 1) return;
goToSlide(currentIndex + 1, isPlaying && !isPaused);
});
playBtn.addEventListener("click", () => {
if (!isPlaying) {
playCurrent();
return;
}
if (isPlaying && !isPaused) {
player.pause();
isPaused = true;
setStatus("Paused");
updatePlayButton();
return;
}
if (isPlaying && isPaused) {
player.play()
.then(() => {
isPaused = false;
setStatus(
`Reading ${currentIndex + 1}/${slides.length}`
);
updatePlayButton();
})
.catch(error => {
console.error(error);
stopAudio(
"Audio play failed"
);
});
}
});
stopBtn.addEventListener("click", () => {
stopAudio("Stopped");
});
player.addEventListener("ended", () => {
if (!isPlaying) return;
if (isPaused) return;
if (currentIndex < slides.length - 1) {
goToSlide(
currentIndex + 1,
true
);
} else {
stopAudio(
"Finished"
);
}
});
document.addEventListener("keydown", event => {
if (event.key === "ArrowLeft") {
prevBtn.click();
}
if (event.key === "ArrowRight") {
nextBtn.click();
}
if (event.key === " ") {
event.preventDefault();
playBtn.click();
}
});
function startResize(event) {
if (window.innerWidth <= 900)
return;
isDragging = true;
pointerId = event.pointerId;
document.body.classList.add(
"dragging"
);
try {
resizer.setPointerCapture(
pointerId
);
} catch (error) {
// ignore
}
}
function resizePanes(clientX) {
if (!isDragging)
return;
if (window.innerWidth <= 900)
return;
const rect =
workspace.getBoundingClientRect();
const x =
clientX - rect.left;
const percent =
(x / rect.width) * 100;
const scriptWidth =
Math.max(
18,
Math.min(
58,
percent
)
);
const slideWidth =
100 - scriptWidth;
scriptPane.style.flexBasis =
`${scriptWidth}%`;
scriptPane.style.width =
`${scriptWidth}%`;
slidePane.style.flexBasis =
`${slideWidth}%`;
}
function stopResize() {
if (!isDragging)
return;
isDragging = false;
pointerId = null;
document.body.classList.remove(
"dragging"
);
}
resizer.addEventListener(
"pointerdown",
event => {
startResize(event);
}
);
window.addEventListener(
"pointermove",
event => {
resizePanes(event.clientX);
}
);
window.addEventListener(
"pointerup",
() => {
stopResize();
}
);
window.addEventListener(
"pointercancel",
() => {
stopResize();
}
);
window.addEventListener(
"resize",
() => {
if (
window.innerWidth <= 900
) {
slidePane.style.flexBasis = "";
scriptPane.style.flexBasis = "";
scriptPane.style.width = "";
stopResize();
}
}
);
loadSlides();