autoupdate index and autoplay new files

This commit is contained in:
lelo 2025-03-24 19:15:40 +01:00
parent 502cdf3291
commit e7e042f3c7
5 changed files with 94 additions and 94 deletions

View File

@ -8,13 +8,14 @@ html, body {
body {
font-family: 'Helvetica Neue', Arial, sans-serif;
background-color: #f4f7f9;
padding: 0;
padding-top: 70px; /* Adjust to your header height */
color: #333;
}
/* Header styles */
.site-header {
position: sticky;
position: fixed;
top: 0;
width: 94%;
z-index: 1000;
display: flex;
align-items: center;
@ -52,7 +53,7 @@ body {
/* Breadcrumb Styles */
.breadcrumb {
margin-bottom: 15px;
font-size: 18px;
font-size: 22px;
}
.breadcrumb a {
text-decoration: none;
@ -64,6 +65,11 @@ body {
margin-right: 5px;
}
.separator::before {
content: "\A"; /* Inserts a line break */
white-space: pre; /* Ensures the newline is honored */
}
/* List Styles */
ul {
list-style-type: none;

View File

@ -22,7 +22,7 @@ function renderContent(data) {
data.breadcrumbs.forEach((crumb, index) => {
breadcrumbHTML += `<a href="#" class="breadcrumb-link" data-path="${crumb.path}">${crumb.name}</a>`;
if (index < data.breadcrumbs.length - 1) {
breadcrumbHTML += `<span>&gt;</span>`;
breadcrumbHTML += `<span class="separator">&#8203;&gt;</span>`;
}
});
document.getElementById('breadcrumbs').innerHTML = breadcrumbHTML;
@ -50,6 +50,7 @@ function renderContent(data) {
}
// Render files.
currentMusicFiles = []; // Reset the music files array.
if (data.files.length > 0) {
contentHTML += '<ul>';
data.files.forEach((file, idx) => {
@ -61,7 +62,9 @@ function renderContent(data) {
symbol = '🖼️';
}
const indexAttr = file.file_type === 'music' ? ` data-index="${currentMusicFiles.length - 1}"` : '';
contentHTML += `<li class="file-item">
// preserve currently-playing class during reloads
const isCurrentlyPlaying = file.file_type === 'music' && currentMusicIndex === currentMusicFiles.length - 1 ? ' currently-playing' : '';
contentHTML += `<li class="file-item ${isCurrentlyPlaying}">
<a href="#" class="play-file"${indexAttr} data-url="${file.path}" data-file-type="${file.file_type}">${symbol} ${file.name.replace('.mp3', '')}</a>`;
if (file.has_transcript) {
contentHTML += `<a href="#" class="show-transcript" data-url="${file.transcript_url}" title="Show Transcript">&#128196;</a>`;
@ -125,14 +128,16 @@ function renderContent(data) {
function loadDirectory(subpath) {
const encodedPath = encodeSubpath(subpath);
const apiUrl = '/api/path/' + encodedPath;
fetch(apiUrl)
return fetch(apiUrl)
.then(response => response.json())
.then(data => {
renderContent(data);
return data; // return data for further chaining
})
.catch(error => {
console.error('Error loading directory:', error);
document.getElementById('content').innerHTML = '<p>Error loading directory!</p><p>'+error+'</p>';
document.getElementById('content').innerHTML = `<p>Error loading directory!</p><p>${error}</p>`;
throw error; // Propagate the error
});
}
@ -144,10 +149,10 @@ function preload_audio() {
const nextMediaUrl = '/media/' + nextFile.path;
// Use a HEAD request so that the backend reads and caches the file without returning the full content.
fetch(nextMediaUrl, { method: 'HEAD' })
.then(response => {
console.log('Backend diskcache initiated for next file:', nextFile.path);
})
.catch(error => console.error('Error initiating backend diskcache for next file:', error));
// .then(response => {
// console.log('Backend diskcache initiated for next file:', nextFile.path);
// })
// .catch(error => console.error('Error initiating backend diskcache for next file:', error));
}
}
@ -340,24 +345,25 @@ window.addEventListener('popstate', function (event) {
let isReloadButtonVisible = true; // Boolean to track the visibility of the reload button.
function reloadDirectory() {
if (!isReloadButtonVisible) return; // Exit if the reload button is hidden.
if (!isReloadButtonVisible) return Promise.resolve();
// Extract the current path from the URL, similar to your initial load code.
// Extract the current path from the URL.
let currentSubpath = '';
if (window.location.pathname.indexOf('/path/') === 0) {
currentSubpath = window.location.pathname.substring(6); // Remove "/path/"
currentSubpath = window.location.pathname.substring(6);
}
loadDirectory(currentSubpath);
// Return the promise so that we can chain actions after the directory has reloaded.
return loadDirectory(currentSubpath)
.then(() => {
// Animate the reload button as before.
const reloadBtn = document.querySelector('#reload-button');
const reloadBtnSVG = document.querySelector('#reload-button svg');
// Force reflow to reset the animation
void reloadBtnSVG.offsetWidth;
void reloadBtnSVG.offsetWidth; // Force reflow to reset the animation
reloadBtnSVG.classList.add("rotate");
isReloadButtonVisible = false;
// Gradually fade back in after 10 seconds
setTimeout(() => {
reloadBtnSVG.classList.remove("rotate");
reloadBtn.style.transition = 'opacity 0.5s ease';
@ -365,6 +371,7 @@ function reloadDirectory() {
reloadBtn.style.pointer = 'cursor';
isReloadButtonVisible = true;
}, 10000);
});
}
@ -404,17 +411,29 @@ if ('mediaSession' in navigator) {
});
}
// auto-play on audio 'ended' event logic
document.getElementById('globalAudio').addEventListener('ended', () => {
// Check if there's a next file in the array.
// Save the current track's path (if any)
const currentTrackPath = currentMusicFiles[currentMusicIndex] ? currentMusicFiles[currentMusicIndex].path : null;
reloadDirectory().then(() => {
// If we had a track playing, try to find it in the updated list.
if (currentTrackPath) {
const newIndex = currentMusicFiles.findIndex(file => file.path === currentTrackPath);
currentMusicIndex = newIndex;
}
// Now, if there's a next track, auto-play it.
if (currentMusicIndex >= 0 && currentMusicIndex < currentMusicFiles.length - 1) {
const nextFile = currentMusicFiles[currentMusicIndex + 1];
// Find the corresponding play link (or directly trigger playback).
const nextLink = document.querySelector(`.play-file[data-url="${nextFile.path}"]`);
if (nextLink) {
nextLink.click();
}
}
}).catch(error => {
console.error('Error during reload:', error);
});
});
document.addEventListener("DOMContentLoaded", function() {

View File

@ -34,36 +34,24 @@ let isSeeking = false;
// --- Slider (Timeline) events ---
// Mouse events
timeline.addEventListener('mousedown', () => { isSeeking = true; });
timeline.addEventListener('mouseup', () => {
isSeeking = false;
timeline.addEventListener('mouseup', () => { isSeeking = false;
changeSeek(); // Update the audio currentTime based on the slider position
});
timeline.addEventListener('change', changeSeek);
// Touch events
timeline.addEventListener('touchstart', () => { isSeeking = true; });
timeline.addEventListener('touchend', () => {
isSeeking = false;
timeline.addEventListener('touchend', () => { isSeeking = false;
changeSeek();
});
timeline.addEventListener('touchcancel', () => { isSeeking = false; });
// --- When metadata is loaded, set slider range in seconds ---
audio.addEventListener('loadedmetadata', () => {
updateTimeInfo();
timeline.min = 0;
timeline.max = audio.duration;
timeline.value = 0; // Ensures the thumb starts at the left.
});
// --- Update the slider thumb position (in seconds) while playing ---
function changeTimelinePosition() {
if (!isSeeking) {
timeline.value = audio.currentTime;
const percentagePosition = (audio.currentTime / audio.duration) * 100;
timeline.style.backgroundSize = `${percentagePosition}% 100%`;
}
}
// --- Seek function: directly set audio.currentTime using slider's value (in seconds) ---
function changeSeek() {
@ -77,17 +65,22 @@ function formatTime(seconds) {
return `${minutes < 10 ? '0' : ''}${minutes}:${secs < 10 ? '0' : ''}${secs}`;
}
// --- Update the time display ---
function updateTimeInfo() {
const currentTimeFormatted = formatTime(audio.currentTime);
const durationFormatted = isNaN(audio.duration) ? "00:00" : formatTime(audio.duration);
timeInfo.innerHTML = `${currentTimeFormatted} / ${durationFormatted}`;
}
// --- Update timeline, time info, and media session on each time update ---
audio.ontimeupdate = function() {
changeTimelinePosition();
updateTimeInfo();
// --- Update the slider thumb position (in seconds) while playing ---
if (!isSeeking) {
timeline.value = audio.currentTime;
const percentagePosition = (audio.currentTime / audio.duration) * 100;
timeline.style.backgroundSize = `${percentagePosition}% 100%`;
}
// --- Update the time display ---
const currentTimeFormatted = formatTime(audio.currentTime);
const durationFormatted = isNaN(audio.duration) ? "00:00" : formatTime(audio.duration);
timeInfo.innerHTML = `${currentTimeFormatted} / ${durationFormatted}`;
if ('mediaSession' in navigator &&
'setPositionState' in navigator.mediaSession &&
!isNaN(audio.duration)) {
@ -99,30 +92,6 @@ audio.ontimeupdate = function() {
}
};
// --- Also update media session on play (in case timeupdate lags) ---
audio.onplay = function() {
if ('mediaSession' in navigator &&
typeof navigator.mediaSession.setPositionState === 'function' &&
!isNaN(audio.duration)) {
navigator.mediaSession.setPositionState({
duration: audio.duration,
playbackRate: audio.playbackRate,
position: audio.currentTime
});
}
};
// --- Handle external seek requests (e.g. from Android or Windows system controls) ---
if ('mediaSession' in navigator) {
navigator.mediaSession.setActionHandler('seekto', function(details) {
if (details.fastSeek && 'fastSeek' in audio) {
audio.fastSeek(details.seekTime);
} else {
audio.currentTime = details.seekTime;
}
changeTimelinePosition();
});
}
// --- When audio ends ---
audio.onended = function() {

View File

@ -1,12 +1,16 @@
const cacheName = 'gottesdienste-v1.1';
const cacheName = 'gottesdienste-v1.2';
const assets = [
'/',
'/static/styles.css',
'/static/gallery.css',
'/static/app.css',
'/static/app.js',
'/static/gallery.css',
'/static/gallery.js',
'/static/audioplayer.css',
'/static/audioplayer.js',
'/static/icons/logo-192x192.png',
'/static/icons/logo-512x512.png'
'/static/icons/logo-512x512.png',
'/static/logo.png',
'/static/logoW.png'
];
self.addEventListener('install', e => {

View File

@ -10,6 +10,8 @@
<title>Gottesdienste</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<meta name="description" content="... uns aber, die wir gerettet werden, ist es eine Gotteskraft.">
<meta name="author" content="Bethaus Speyer">
<link rel="icon" href="/static/icons/logo-192x192.png" type="image/png" sizes="192x192">
@ -45,9 +47,9 @@
<div id="directory-controls">
<button id="reload-button" onclick="reloadDirectory()" title="Reload Directory">
<svg width="70px" height="70px" viewBox="0 0 24 24"xmlns="http://www.w3.org/2000/svg">
<path d="M16.2721 3.13079L17.4462 6.15342C17.6461 6.66824 17.3909 7.24768 16.8761 7.44764L13.8535 8.6217" stroke="#999" stroke-width="2.5" stroke-linecap="round"/>
<path d="M7.61555 20.5111L6.93391 17.3409C6.81782 16.801 7.16142 16.2692 7.70136 16.1531L10.8715 15.4714" stroke="#999" stroke-width="2.5" stroke-linecap="round"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.9153 7.86755C17.6501 7.5052 17.8749 6.55584 17.263 6.01119C16.4869 5.32046 15.5626 4.77045 14.5169 4.41551C10.3331 2.99538 5.79017 5.23581 4.37005 9.41964C4.01511 10.4653 3.88883 11.5335 3.96442 12.5696C4.02402 13.3867 4.9141 13.7862 5.64883 13.4239V13.4239C6.17327 13.1652 6.44536 12.5884 6.44406 12.0037C6.44274 11.4136 6.53712 10.8132 6.73739 10.2232C7.71372 7.34681 10.837 5.80651 13.7134 6.78285C14.3034 6.98311 14.8371 7.27371 15.3045 7.63397C15.7677 7.99095 16.3909 8.12619 16.9153 7.86755V7.86755ZM6.97575 16.1145C7.50019 15.8558 8.12343 15.991 8.58656 16.348C9.05394 16.7083 9.58773 16.9989 10.1777 17.1992C13.0541 18.1755 16.1774 16.6352 17.1537 13.7588C17.354 13.1688 17.4483 12.5684 17.447 11.9783C17.4457 11.3936 17.7178 10.8168 18.2423 10.5581V10.5581C18.977 10.1958 19.8671 10.5953 19.9267 11.4124C20.0022 12.4485 19.876 13.5167 19.521 14.5624C18.1009 18.7462 13.558 20.9866 9.37418 19.5665C8.32849 19.2116 7.4042 18.6615 6.62812 17.9708C6.01616 17.4262 6.24102 16.4768 6.97575 16.1145V16.1145Z" fill="#999"/>
<path d="M16.2721 3.13079L17.4462 6.15342C17.6461 6.66824 17.3909 7.24768 16.8761 7.44764L13.8535 8.6217" stroke="#CCC" stroke-width="2.5" stroke-linecap="round"/>
<path d="M7.61555 20.5111L6.93391 17.3409C6.81782 16.801 7.16142 16.2692 7.70136 16.1531L10.8715 15.4714" stroke="#CCC" stroke-width="2.5" stroke-linecap="round"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.9153 7.86755C17.6501 7.5052 17.8749 6.55584 17.263 6.01119C16.4869 5.32046 15.5626 4.77045 14.5169 4.41551C10.3331 2.99538 5.79017 5.23581 4.37005 9.41964C4.01511 10.4653 3.88883 11.5335 3.96442 12.5696C4.02402 13.3867 4.9141 13.7862 5.64883 13.4239V13.4239C6.17327 13.1652 6.44536 12.5884 6.44406 12.0037C6.44274 11.4136 6.53712 10.8132 6.73739 10.2232C7.71372 7.34681 10.837 5.80651 13.7134 6.78285C14.3034 6.98311 14.8371 7.27371 15.3045 7.63397C15.7677 7.99095 16.3909 8.12619 16.9153 7.86755V7.86755ZM6.97575 16.1145C7.50019 15.8558 8.12343 15.991 8.58656 16.348C9.05394 16.7083 9.58773 16.9989 10.1777 17.1992C13.0541 18.1755 16.1774 16.6352 17.1537 13.7588C17.354 13.1688 17.4483 12.5684 17.447 11.9783C17.4457 11.3936 17.7178 10.8168 18.2423 10.5581V10.5581C18.977 10.1958 19.8671 10.5953 19.9267 11.4124C20.0022 12.4485 19.876 13.5167 19.521 14.5624C18.1009 18.7462 13.558 20.9866 9.37418 19.5665C8.32849 19.2116 7.4042 18.6615 6.62812 17.9708C6.01616 17.4262 6.24102 16.4768 6.97575 16.1145V16.1145Z" fill="#CCC"/>
</svg>
</button>
</div>
@ -57,7 +59,7 @@
<footer>
<div class="audio-player-container" id="audioPlayerContainer">
<div class="audio-player">
<audio id="globalAudio">
<audio id="globalAudio" prefetch="auto">
Your browser does not support the audio element.
</audio>
<div class="controls">