play files directly in search
This commit is contained in:
parent
47c5010434
commit
de03e6f2b9
1
app.py
1
app.py
@ -44,7 +44,6 @@ app.add_url_rule('/connections', view_func=a.connections)
|
|||||||
app.add_url_rule('/mylinks', view_func=auth.mylinks)
|
app.add_url_rule('/mylinks', view_func=auth.mylinks)
|
||||||
app.add_url_rule('/remove_secret', view_func=auth.remove_secret, methods=['POST'])
|
app.add_url_rule('/remove_secret', view_func=auth.remove_secret, methods=['POST'])
|
||||||
app.add_url_rule('/remove_token', view_func=auth.remove_token, methods=['POST'])
|
app.add_url_rule('/remove_token', view_func=auth.remove_token, methods=['POST'])
|
||||||
app.add_url_rule('/search', view_func=search.search, methods=['GET'])
|
|
||||||
app.add_url_rule('/searchcommand', view_func=search.searchcommand, methods=['POST'])
|
app.add_url_rule('/searchcommand', view_func=search.searchcommand, methods=['POST'])
|
||||||
|
|
||||||
app.add_url_rule('/songs_dashboard', view_func=a.songs_dashboard)
|
app.add_url_rule('/songs_dashboard', view_func=a.songs_dashboard)
|
||||||
|
|||||||
16
search.py
16
search.py
@ -5,9 +5,7 @@ import json
|
|||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
SEARCH_DB_NAME = 'search.db'
|
search_db = sqlite3.connect('search.db', check_same_thread=False)
|
||||||
|
|
||||||
search_db = sqlite3.connect(SEARCH_DB_NAME, check_same_thread=False)
|
|
||||||
search_db.row_factory = sqlite3.Row
|
search_db.row_factory = sqlite3.Row
|
||||||
|
|
||||||
with open("app_config.json", 'r') as file:
|
with open("app_config.json", 'r') as file:
|
||||||
@ -100,15 +98,3 @@ def searchcommand():
|
|||||||
# Limit results
|
# Limit results
|
||||||
results = results[:100]
|
results = results[:100]
|
||||||
return jsonify(results=results)
|
return jsonify(results=results)
|
||||||
|
|
||||||
|
|
||||||
def search():
|
|
||||||
allowed_basefolders = list(session['folders'].keys())
|
|
||||||
title_short = app_config.get('TITLE_SHORT', 'Default Title')
|
|
||||||
title_long = app_config.get('TITLE_LONG' , 'Default Title')
|
|
||||||
return render_template("search.html",
|
|
||||||
title_short=title_short,
|
|
||||||
title_long=title_long,
|
|
||||||
search_folders=allowed_basefolders
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|||||||
108
static/app.js
108
static/app.js
@ -3,6 +3,26 @@ let currentMusicFiles = []; // Array of objects with at least { path, index }
|
|||||||
let currentMusicIndex = -1; // Index of the current music file
|
let currentMusicIndex = -1; // Index of the current music file
|
||||||
let currentTrackPath = "";
|
let currentTrackPath = "";
|
||||||
|
|
||||||
|
// Cache common DOM elements
|
||||||
|
const mainContainer = document.querySelector('main');
|
||||||
|
const searchContainer = document.querySelector('search');
|
||||||
|
const footer = document.querySelector('footer');
|
||||||
|
|
||||||
|
console.log(mainContainer, searchContainer, footer);
|
||||||
|
|
||||||
|
function viewSearch() {
|
||||||
|
// Hide the main container and show the search container
|
||||||
|
mainContainer.style.display = 'none';
|
||||||
|
searchContainer.style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
function viewMain() {
|
||||||
|
// Hide the search container and show the main container
|
||||||
|
searchContainer.style.display = 'none';
|
||||||
|
mainContainer.style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Helper function: decode each segment then re-encode to avoid double encoding.
|
// Helper function: decode each segment then re-encode to avoid double encoding.
|
||||||
function encodeSubpath(subpath) {
|
function encodeSubpath(subpath) {
|
||||||
if (!subpath) return '';
|
if (!subpath) return '';
|
||||||
@ -100,7 +120,7 @@ function renderContent(data) {
|
|||||||
</li>`;
|
</li>`;
|
||||||
});
|
});
|
||||||
if (data.breadcrumbs.length === 1) {
|
if (data.breadcrumbs.length === 1) {
|
||||||
contentHTML += `<li class="link-item" onclick="window.location.href='/search'"><a href="/search" class="link-link">🔎 Suche</a></li>`;
|
contentHTML += `<li class="link-item" onclick="viewSearch()"><a onclick="viewSearch() class="link-link">🔎 Suche</a></li>`;
|
||||||
}
|
}
|
||||||
contentHTML += '</ul>';
|
contentHTML += '</ul>';
|
||||||
}
|
}
|
||||||
@ -236,15 +256,8 @@ function attachEventListeners() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Cache common DOM elements
|
|
||||||
const nowPlayingInfo = document.getElementById('nowPlayingInfo');
|
|
||||||
const audioPlayer = document.getElementById('globalAudio');
|
|
||||||
const playerButton = document.querySelector('.player-button');
|
|
||||||
const audioPlayerContainer = document.getElementById('audioPlayerContainer');
|
|
||||||
const footer = document.querySelector('footer');
|
|
||||||
|
|
||||||
// Global variable to store the current fetch's AbortController.
|
|
||||||
let currentFetchController = null;
|
|
||||||
|
|
||||||
document.querySelectorAll('.play-file').forEach(link => {
|
document.querySelectorAll('.play-file').forEach(link => {
|
||||||
link.addEventListener('click', async function (event) {
|
link.addEventListener('click', async function (event) {
|
||||||
@ -261,85 +274,16 @@ document.querySelectorAll('.play-file').forEach(link => {
|
|||||||
// Update the current music index.
|
// Update the current music index.
|
||||||
currentMusicIndex = index !== undefined ? parseInt(index) : -1;
|
currentMusicIndex = index !== undefined ? parseInt(index) : -1;
|
||||||
|
|
||||||
// Display the audio player container.
|
|
||||||
audioPlayerContainer.style.display = "block";
|
|
||||||
|
|
||||||
// Mark the clicked item as currently playing.
|
// Mark the clicked item as currently playing.
|
||||||
this.closest('.file-item').classList.add('currently-playing');
|
this.closest('.file-item').classList.add('currently-playing');
|
||||||
|
|
||||||
// Abort any previous fetch if still running.
|
startPlaying(relUrl);
|
||||||
if (currentFetchController) {
|
|
||||||
currentFetchController.abort();
|
|
||||||
}
|
|
||||||
currentFetchController = new AbortController();
|
|
||||||
|
|
||||||
// Pause the audio and clear its source.
|
|
||||||
audioPlayer.pause();
|
|
||||||
audioPlayer.src = '';
|
|
||||||
|
|
||||||
// Set a timeout to display a loader message if needed.
|
|
||||||
const loaderTimeout = setTimeout(() => {
|
|
||||||
playerButton.innerHTML = playIcon;
|
|
||||||
nowPlayingInfo.textContent = "Wird geladen...";
|
|
||||||
}, 500);
|
|
||||||
|
|
||||||
footer.style.display = 'flex';
|
|
||||||
|
|
||||||
const mediaUrl = `/media/${relUrl}`;
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Perform a HEAD request to verify media availability.
|
|
||||||
const response = await fetch(mediaUrl, { method: 'HEAD', signal: currentFetchController.signal });
|
|
||||||
clearTimeout(loaderTimeout);
|
|
||||||
|
|
||||||
if (response.status === 403) {
|
|
||||||
nowPlayingInfo.textContent = "Fehler: Zugriff verweigert.";
|
|
||||||
window.location.href = '/';
|
|
||||||
return;
|
|
||||||
} else if (!response.ok) {
|
|
||||||
nowPlayingInfo.textContent = `Fehler: Unerwarteter Status (${response.status}).`;
|
|
||||||
console.error('Unexpected response status:', response.status);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the media URL, load, and play the audio.
|
|
||||||
audioPlayer.src = mediaUrl;
|
|
||||||
audioPlayer.load();
|
|
||||||
await audioPlayer.play();
|
|
||||||
currentTrackPath = relUrl;
|
|
||||||
playerButton.innerHTML = pauseIcon;
|
|
||||||
|
|
||||||
// Process file path for display.
|
|
||||||
const pathParts = relUrl.split('/');
|
|
||||||
const folderName = pathParts[pathParts.length - 2];
|
|
||||||
const fileName = pathParts.pop();
|
|
||||||
const pathStr = pathParts.join('/');
|
|
||||||
|
|
||||||
// Update Media Session metadata if available.
|
|
||||||
if ('mediaSession' in navigator) {
|
|
||||||
navigator.mediaSession.metadata = new MediaMetadata({
|
|
||||||
title: currentMusicFiles[currentMusicIndex].title,
|
|
||||||
artist: folderName,
|
|
||||||
artwork: [
|
|
||||||
{ src: '/icons/logo-192x192.png', sizes: '192x192', type: 'image/png' }
|
|
||||||
]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
nowPlayingInfo.innerHTML = pathStr.replace(/\//g, ' > ') +
|
|
||||||
'<br><span style="font-size: larger; font-weight: bold;">' +
|
|
||||||
fileName.replace('.mp3', '') + '</span>';
|
|
||||||
|
|
||||||
// Delay preloading to avoid blocking playback.
|
// Delay preloading to avoid blocking playback.
|
||||||
setTimeout(preload_audio, 1000);
|
setTimeout(preload_audio, 1000);
|
||||||
} catch (error) {
|
|
||||||
if (error.name === 'AbortError') {
|
|
||||||
console.log('Previous fetch aborted.');
|
|
||||||
} else {
|
|
||||||
console.error('Error fetching media:', error);
|
|
||||||
nowPlayingInfo.textContent = "Fehler: Netzwerkproblem oder ungültige URL.";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (fileType === 'image') {
|
} else if (fileType === 'image') {
|
||||||
// Open the gallery modal for image files.
|
// Open the gallery modal for image files.
|
||||||
openGalleryModal(relUrl);
|
openGalleryModal(relUrl);
|
||||||
|
|||||||
@ -1,6 +1,11 @@
|
|||||||
// read the CSS variable from :root (or any selector)
|
// read the CSS variable from :root (or any selector)
|
||||||
const cssVar = getComputedStyle(document.documentElement).getPropertyValue('--dark-background').trim();
|
const cssVar = getComputedStyle(document.documentElement).getPropertyValue('--dark-background').trim();
|
||||||
|
|
||||||
|
// player DOM elements
|
||||||
|
const nowPlayingInfo = document.getElementById('nowPlayingInfo');
|
||||||
|
const audioPlayer = document.getElementById('globalAudio');
|
||||||
|
const audioPlayerContainer = document.getElementById('audioPlayerContainer');
|
||||||
|
|
||||||
const playerButton = document.querySelector('.player-button'),
|
const playerButton = document.querySelector('.player-button'),
|
||||||
audio = document.querySelector('audio'),
|
audio = document.querySelector('audio'),
|
||||||
timeline = document.querySelector('.timeline'),
|
timeline = document.querySelector('.timeline'),
|
||||||
@ -119,3 +124,82 @@ async function downloadAudio() {
|
|||||||
a.click();
|
a.click();
|
||||||
document.body.removeChild(a);
|
document.body.removeChild(a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Global variable to store the current fetch's AbortController.
|
||||||
|
let currentFetchController = null;
|
||||||
|
|
||||||
|
async function startPlaying(relUrl) {
|
||||||
|
// Pause the audio and clear its source.
|
||||||
|
audioPlayer.pause();
|
||||||
|
audioPlayer.src = '';
|
||||||
|
|
||||||
|
// Display the audio player container.
|
||||||
|
audioPlayerContainer.style.display = "block";
|
||||||
|
|
||||||
|
// Set a timeout to display a loader message if needed.
|
||||||
|
const loaderTimeout = setTimeout(() => {
|
||||||
|
playerButton.innerHTML = playIcon;
|
||||||
|
nowPlayingInfo.textContent = "Wird geladen...";
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
footer.style.display = 'flex';
|
||||||
|
|
||||||
|
// Abort any previous fetch if still running.
|
||||||
|
if (currentFetchController) {
|
||||||
|
currentFetchController.abort();
|
||||||
|
}
|
||||||
|
currentFetchController = new AbortController();
|
||||||
|
|
||||||
|
const mediaUrl = `/media/${relUrl}`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Perform a HEAD request to verify media availability.
|
||||||
|
const response = await fetch(mediaUrl, { method: 'HEAD', signal: currentFetchController.signal });
|
||||||
|
clearTimeout(loaderTimeout);
|
||||||
|
|
||||||
|
if (response.status === 403) {
|
||||||
|
nowPlayingInfo.textContent = "Fehler: Zugriff verweigert.";
|
||||||
|
window.location.href = '/';
|
||||||
|
return;
|
||||||
|
} else if (!response.ok) {
|
||||||
|
nowPlayingInfo.textContent = `Fehler: Unerwarteter Status (${response.status}).`;
|
||||||
|
console.error('Unexpected response status:', response.status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the media URL, load, and play the audio.
|
||||||
|
audioPlayer.src = mediaUrl;
|
||||||
|
audioPlayer.load();
|
||||||
|
await audioPlayer.play();
|
||||||
|
currentTrackPath = relUrl;
|
||||||
|
playerButton.innerHTML = pauseIcon;
|
||||||
|
|
||||||
|
// Process file path for display.
|
||||||
|
const pathParts = relUrl.split('/');
|
||||||
|
const folderName = pathParts[pathParts.length - 2];
|
||||||
|
const fileName = pathParts.pop();
|
||||||
|
const pathStr = pathParts.join('/');
|
||||||
|
|
||||||
|
// Update Media Session metadata if available.
|
||||||
|
if ('mediaSession' in navigator) {
|
||||||
|
navigator.mediaSession.metadata = new MediaMetadata({
|
||||||
|
title: fileName.replace(/\.[^/.]+$/, ''), // remove extension
|
||||||
|
artist: folderName,
|
||||||
|
artwork: [
|
||||||
|
{ src: '/icons/logo-192x192.png', sizes: '192x192', type: 'image/png' }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
nowPlayingInfo.innerHTML = pathStr.replace(/\//g, ' > ') +
|
||||||
|
'<br><span style="font-size: larger; font-weight: bold;">' +
|
||||||
|
fileName.replace('.mp3', '') + '</span>';
|
||||||
|
} catch (error) {
|
||||||
|
if (error.name === 'AbortError') {
|
||||||
|
console.log('Previous fetch aborted.');
|
||||||
|
} else {
|
||||||
|
console.error('Error fetching media:', error);
|
||||||
|
nowPlayingInfo.textContent = "Fehler: Netzwerkproblem oder ungültige URL.";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
162
static/search.js
Normal file
162
static/search.js
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
|
||||||
|
// Function to render search results from a response object
|
||||||
|
function renderResults(data) {
|
||||||
|
const resultsDiv = document.getElementById('results');
|
||||||
|
resultsDiv.innerHTML = '<h5>Suchergebnisse:</h5>';
|
||||||
|
|
||||||
|
if (data.results && data.results.length > 0) {
|
||||||
|
data.results.forEach(file => {
|
||||||
|
const card = document.createElement('div');
|
||||||
|
card.className = 'card';
|
||||||
|
card.innerHTML = `
|
||||||
|
<div class="card-body">
|
||||||
|
<p><button class="btn btn-light" onclick="startPlaying('${file.relative_path}')" style="width:100%;">🔊 ${file.filename}</button></p>
|
||||||
|
<p><button onclick="window.open('/path/${file.relative_path}', '_self');" class="btn btn-light btn-sm" style="width:100%;">📁 ${file.relative_path}</button></p>
|
||||||
|
${ file.transcript_hits !== undefined
|
||||||
|
? `<p class="card-text">Treffer im Transkript: ${file.transcript_hits}</p>`
|
||||||
|
: `<p class="card-text">Downloads: ${file.hitcount}</p>`
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
resultsDiv.appendChild(card);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
resultsDiv.innerHTML = '<p>No results found.</p>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore previous search response if available from localStorage
|
||||||
|
const previousResponse = localStorage.getItem("searchResponse");
|
||||||
|
if (previousResponse) {
|
||||||
|
try {
|
||||||
|
const data = JSON.parse(previousResponse);
|
||||||
|
renderResults(data);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error parsing searchResponse from localStorage:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore previous search word (Suchwort) if available
|
||||||
|
const previousQuery = localStorage.getItem("searchQuery");
|
||||||
|
if (previousQuery) {
|
||||||
|
document.getElementById('query').value = previousQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore previous selected category if available, otherwise default remains "Alles"
|
||||||
|
const previousCategory = localStorage.getItem("searchCategory");
|
||||||
|
if (previousCategory !== null) {
|
||||||
|
const radio = document.querySelector('input[name="category"][value="' + previousCategory + '"]');
|
||||||
|
if (radio) {
|
||||||
|
radio.checked = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore the checkbox state for "Im Transkript suchen"
|
||||||
|
const previousIncludeTranscript = localStorage.getItem("searchIncludeTranscript");
|
||||||
|
if (previousIncludeTranscript !== null) {
|
||||||
|
document.getElementById('includeTranscript').checked = (previousIncludeTranscript === 'true');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Form submission event
|
||||||
|
document.getElementById('searchForm').addEventListener('submit', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const query = document.getElementById('query').value.trim();
|
||||||
|
const includeTranscript = document.getElementById('includeTranscript').checked;
|
||||||
|
|
||||||
|
// Get the selected category radio button, if any
|
||||||
|
const categoryRadio = document.querySelector('input[name="category"]:checked');
|
||||||
|
const category = categoryRadio ? categoryRadio.value : '';
|
||||||
|
|
||||||
|
// Prevent accidental re-selection of already selected radio buttons
|
||||||
|
const radios = document.querySelectorAll('input[name="category"]');
|
||||||
|
radios.forEach(radio => {
|
||||||
|
radio.addEventListener('mousedown', function(e) {
|
||||||
|
this.wasChecked = this.checked;
|
||||||
|
});
|
||||||
|
radio.addEventListener('click', function(e) {
|
||||||
|
if (this.wasChecked) {
|
||||||
|
this.checked = false;
|
||||||
|
this.wasChecked = false;
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Prepare form data for the fetch request
|
||||||
|
// Send the query and the category as separate parameters.
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('query', query);
|
||||||
|
formData.append('category', category);
|
||||||
|
formData.append('folder', document.getElementById('folder').value);
|
||||||
|
formData.append('datefrom', document.getElementById('datefrom').value);
|
||||||
|
formData.append('dateto', document.getElementById('dateto').value);
|
||||||
|
formData.append('includeTranscript', includeTranscript);
|
||||||
|
|
||||||
|
fetch('/searchcommand', {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
// Render the results
|
||||||
|
renderResults(data);
|
||||||
|
// Store the raw response in localStorage
|
||||||
|
try {
|
||||||
|
localStorage.setItem("searchResponse", JSON.stringify(data));
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error saving searchResponse to localStorage:', e);
|
||||||
|
}
|
||||||
|
// Save the search word, selected category, and checkbox state in localStorage
|
||||||
|
localStorage.setItem("searchQuery", query);
|
||||||
|
localStorage.setItem("searchCategory", category);
|
||||||
|
localStorage.setItem("searchIncludeTranscript", includeTranscript);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error:', error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Clear button event handler
|
||||||
|
document.getElementById('clearBtn').addEventListener('click', function() {
|
||||||
|
// Remove stored items
|
||||||
|
localStorage.removeItem("searchResponse");
|
||||||
|
localStorage.removeItem("searchQuery");
|
||||||
|
localStorage.removeItem("searchCategory");
|
||||||
|
localStorage.removeItem("folder");
|
||||||
|
localStorage.removeItem("datefrom");
|
||||||
|
localStorage.removeItem("dateto");
|
||||||
|
localStorage.removeItem("searchIncludeTranscript");
|
||||||
|
// Reset form fields to defaults
|
||||||
|
document.getElementById('query').value = '';
|
||||||
|
document.querySelector('input[name="category"][value=""]').checked = true;
|
||||||
|
const otherRadios = document.querySelectorAll('input[name="category"]:not([value=""])');
|
||||||
|
otherRadios.forEach(radio => radio.checked = false);
|
||||||
|
document.getElementById('folder').value = ''; // Reset to "Alle"
|
||||||
|
document.getElementById('datefrom').value = ''; // Reset date from
|
||||||
|
document.getElementById('dateto').value = ''; // Reset date to
|
||||||
|
document.getElementById('includeTranscript').checked = false;
|
||||||
|
// Clear the results div
|
||||||
|
document.getElementById('results').innerHTML = '';
|
||||||
|
});
|
||||||
|
|
||||||
|
// Back button event handler - redirect to the root path
|
||||||
|
document.getElementById('backBtn').addEventListener('click', function() {
|
||||||
|
// window.location.href = '/';
|
||||||
|
viewMain();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function syncThemeColor() {
|
||||||
|
// read the CSS variable from :root (or any selector)
|
||||||
|
const cssVar = getComputedStyle(document.documentElement)
|
||||||
|
.getPropertyValue('--dark-background').trim();
|
||||||
|
if (cssVar) {
|
||||||
|
document
|
||||||
|
.querySelector('meta[name="theme-color"]')
|
||||||
|
.setAttribute('content', cssVar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sync once on load
|
||||||
|
document.addEventListener('DOMContentLoaded', syncThemeColor);
|
||||||
@ -59,6 +59,7 @@
|
|||||||
<a href="{{ url_for('folder_secret_config_editor') }}" id="edit-folder-config" >Ordnerkonfiguration</a>
|
<a href="{{ url_for('folder_secret_config_editor') }}" id="edit-folder-config" >Ordnerkonfiguration</a>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<main>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div id="breadcrumbs" class="breadcrumb"></div>
|
<div id="breadcrumbs" class="breadcrumb"></div>
|
||||||
<div id="content"></div>
|
<div id="content"></div>
|
||||||
@ -72,6 +73,119 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<search style="display: none;">
|
||||||
|
<div class="container">
|
||||||
|
<form id="searchForm" method="post" class="mb-4">
|
||||||
|
|
||||||
|
<!-- Suchwörter -->
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="query" class="h5 form-label">Suchwörter:</label>
|
||||||
|
<input type="text" id="query" name="query" class="form-control" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Toggle für Suchoptionen -->
|
||||||
|
<div class="d-flex align-items-center mb-2">
|
||||||
|
<h2 class="h5 mb-0">Suchoptionen</h2>
|
||||||
|
<button class="btn btn-sm btn-link p-0"
|
||||||
|
type="button"
|
||||||
|
data-bs-toggle="collapse"
|
||||||
|
data-bs-target="#searchOptions"
|
||||||
|
aria-expanded="false"
|
||||||
|
aria-controls="searchOptions"
|
||||||
|
aria-label="Toggle Suchoptionen">
|
||||||
|
<i class="bi bi-plus-lg"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Suchoptionen einklappbar -->
|
||||||
|
<div id="searchOptions" class="collapse border rounded p-3 mb-3">
|
||||||
|
|
||||||
|
<!-- Kategorie -->
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label">Kategorie:</label>
|
||||||
|
<div>
|
||||||
|
<div class="form-check form-check-inline">
|
||||||
|
<input class="form-check-input" type="radio" name="category" id="none" value="" checked>
|
||||||
|
<label class="form-check-label" for="none">Alles</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check form-check-inline">
|
||||||
|
<input class="form-check-input" type="radio" name="category" id="lied" value="Lied">
|
||||||
|
<label class="form-check-label" for="lied">Lied</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check form-check-inline">
|
||||||
|
<input class="form-check-input" type="radio" name="category" id="gedicht" value="Gedicht">
|
||||||
|
<label class="form-check-label" for="gedicht">Gedicht</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check form-check-inline">
|
||||||
|
<input class="form-check-input" type="radio" name="category" id="predigt" value="Predigt">
|
||||||
|
<label class="form-check-label" for="predigt">Predigt</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check form-check-inline">
|
||||||
|
<input class="form-check-input" type="radio" name="category" id="chor" value="chor">
|
||||||
|
<label class="form-check-label" for="chor">Chor</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check form-check-inline">
|
||||||
|
<input class="form-check-input" type="radio" name="category" id="orchester" value="orchester">
|
||||||
|
<label class="form-check-label" for="orchester">Orchester</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<!-- In Ordner suchen -->
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="folder" class="form-label">In Ordner suchen:</label>
|
||||||
|
<select id="folder" name="folder" class="form-select">
|
||||||
|
<option value="">Alle</option>
|
||||||
|
{% for folder in search_folders %}
|
||||||
|
<option value="{{ folder }}">{{ folder }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<!-- Transkript durchsuchen -->
|
||||||
|
<div class="form-check mb-3">
|
||||||
|
<input type="checkbox" class="form-check-input" id="includeTranscript" name="includeTranscript">
|
||||||
|
<label class="form-check-label" for="includeTranscript">Im Transkript suchen</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<!-- Zeitraum -->
|
||||||
|
<div class="row g-2 mb-3">
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<label for="datefrom" class="form-label">Datum von:</label>
|
||||||
|
<input type="date" id="datefrom" name="datefrom" class="form-control">
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<label for="dateto" class="form-label">Datum bis:</label>
|
||||||
|
<input type="date" id="dateto" name="dateto" class="form-control">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- Ende Suchoptionen -->
|
||||||
|
|
||||||
|
<!-- Buttons -->
|
||||||
|
<div class="mb-3">
|
||||||
|
<button type="submit" class="btn btn-primary">Suchen</button>
|
||||||
|
<button type="button" id="clearBtn" class="btn btn-secondary ms-2">zurücksetzen</button>
|
||||||
|
<button type="button" id="backBtn" class="btn btn-secondary ms-2">beenden</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<!-- AJAX-loaded results -->
|
||||||
|
<div id="results"></div>
|
||||||
|
</div>
|
||||||
|
</search>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Global Audio Player in Footer -->
|
<!-- Global Audio Player in Footer -->
|
||||||
<footer>
|
<footer>
|
||||||
@ -124,9 +238,12 @@
|
|||||||
<div id="gallery-loader"></div>
|
<div id="gallery-loader"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
|
||||||
<!-- Load main app JS first, then gallery JS -->
|
<!-- Load main app JS first, then gallery JS -->
|
||||||
<script src="{{ url_for('static', filename='app.js') }}"></script>
|
<script src="{{ url_for('static', filename='app.js') }}"></script>
|
||||||
<script src="{{ url_for('static', filename='gallery.js') }}"></script>
|
<script src="{{ url_for('static', filename='gallery.js') }}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='search.js') }}"></script>
|
||||||
<script src="{{ url_for('static', filename='audioplayer.js') }}"></script>
|
<script src="{{ url_for('static', filename='audioplayer.js') }}"></script>
|
||||||
<script>
|
<script>
|
||||||
if ('serviceWorker' in navigator) {
|
if ('serviceWorker' in navigator) {
|
||||||
|
|||||||
@ -22,6 +22,7 @@
|
|||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css" rel="stylesheet">
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='theme.css') }}">
|
<link rel="stylesheet" href="{{ url_for('static', filename='theme.css') }}">
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='app.css') }}">
|
<link rel="stylesheet" href="{{ url_for('static', filename='app.css') }}">
|
||||||
|
<script src="{{ url_for('static', filename='functions.js') }}"></script>
|
||||||
|
|
||||||
{% block head_extra %}{% endblock %}
|
{% block head_extra %}{% endblock %}
|
||||||
</head>
|
</head>
|
||||||
@ -56,6 +57,5 @@
|
|||||||
{% block scripts %}{% endblock %}
|
{% block scripts %}{% endblock %}
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
<script src="{{ url_for('static', filename='functions.js') }}"></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@ -1,335 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
|
|
||||||
<meta property="og:title" content="{{ title_long }}" />
|
|
||||||
<meta property="og:description" content="... uns aber, die wir gerettet werden, ist es eine Gotteskraft." />
|
|
||||||
<meta property="og:image" content="/icon/logo-200x200.png" />
|
|
||||||
|
|
||||||
<title>{{ title_short }}</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="{{ title_short }}">
|
|
||||||
<link rel="icon" href="/icon/logo-192x192.png" type="image/png" sizes="192x192">
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Web App Manifest -->
|
|
||||||
<link rel="manifest" href="{{ url_for('static', filename='manifest.json') }}">
|
|
||||||
|
|
||||||
<!-- Android Theme Color -->
|
|
||||||
<meta name="theme-color" content="#000">
|
|
||||||
|
|
||||||
<!-- Apple-specific tags -->
|
|
||||||
<link rel="touch-icon" href="{{ url_for('static', filename='icons/icon-192x192.png') }}">
|
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
|
||||||
<meta name="mobile-web-app-status-bar-style" content="default">
|
|
||||||
<meta name="mobile-web-app-title" content="Gottesdienste">
|
|
||||||
|
|
||||||
<!-- Bootstrap CSS for modern styling -->
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css" rel="stylesheet">
|
|
||||||
|
|
||||||
<!-- Your CSS -->
|
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='theme.css') }}">
|
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='app.css') }}">
|
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
|
||||||
<style>
|
|
||||||
.btn {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
.search-container {
|
|
||||||
padding: 15px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<header class="site-header">
|
|
||||||
<a href="/">
|
|
||||||
<img src="/custom_logo/logoW.png" alt="Logo" class="logo">
|
|
||||||
</a>
|
|
||||||
<h1>{{ title_long }}</h1>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<div class="search-container">
|
|
||||||
<h1>Suche</h1>
|
|
||||||
<form id="searchForm" method="post" class="mb-4">
|
|
||||||
|
|
||||||
<!-- Suchwörter -->
|
|
||||||
<div class="mb-3">
|
|
||||||
<label for="query" class="h5 form-label">Suchwörter:</label>
|
|
||||||
<input type="text" id="query" name="query" class="form-control" required>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Toggle für Suchoptionen -->
|
|
||||||
<div class="d-flex align-items-center mb-2">
|
|
||||||
<h2 class="h5 mb-0">Suchoptionen</h2>
|
|
||||||
<button class="btn btn-sm btn-link p-0"
|
|
||||||
type="button"
|
|
||||||
data-bs-toggle="collapse"
|
|
||||||
data-bs-target="#searchOptions"
|
|
||||||
aria-expanded="false"
|
|
||||||
aria-controls="searchOptions"
|
|
||||||
aria-label="Toggle Suchoptionen">
|
|
||||||
<i class="bi bi-plus-lg"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Suchoptionen einklappbar -->
|
|
||||||
<div id="searchOptions" class="collapse border rounded p-3 mb-3">
|
|
||||||
|
|
||||||
<!-- Kategorie -->
|
|
||||||
<div class="mb-3">
|
|
||||||
<label class="form-label">Kategorie:</label>
|
|
||||||
<div>
|
|
||||||
<div class="form-check form-check-inline">
|
|
||||||
<input class="form-check-input" type="radio" name="category" id="none" value="" checked>
|
|
||||||
<label class="form-check-label" for="none">Alles</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-check form-check-inline">
|
|
||||||
<input class="form-check-input" type="radio" name="category" id="lied" value="Lied">
|
|
||||||
<label class="form-check-label" for="lied">Lied</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-check form-check-inline">
|
|
||||||
<input class="form-check-input" type="radio" name="category" id="gedicht" value="Gedicht">
|
|
||||||
<label class="form-check-label" for="gedicht">Gedicht</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-check form-check-inline">
|
|
||||||
<input class="form-check-input" type="radio" name="category" id="predigt" value="Predigt">
|
|
||||||
<label class="form-check-label" for="predigt">Predigt</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-check form-check-inline">
|
|
||||||
<input class="form-check-input" type="radio" name="category" id="chor" value="chor">
|
|
||||||
<label class="form-check-label" for="chor">Chor</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-check form-check-inline">
|
|
||||||
<input class="form-check-input" type="radio" name="category" id="orchester" value="orchester">
|
|
||||||
<label class="form-check-label" for="orchester">Orchester</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<!-- In Ordner suchen -->
|
|
||||||
<div class="mb-3">
|
|
||||||
<label for="folder" class="form-label">In Ordner suchen:</label>
|
|
||||||
<select id="folder" name="folder" class="form-select">
|
|
||||||
<option value="">Alle</option>
|
|
||||||
{% for folder in search_folders %}
|
|
||||||
<option value="{{ folder }}">{{ folder }}</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<!-- Transkript durchsuchen -->
|
|
||||||
<div class="form-check mb-3">
|
|
||||||
<input type="checkbox" class="form-check-input" id="includeTranscript" name="includeTranscript">
|
|
||||||
<label class="form-check-label" for="includeTranscript">Im Transkript suchen</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<!-- Zeitraum -->
|
|
||||||
<div class="row g-2 mb-3">
|
|
||||||
<div class="col-12 col-md-6">
|
|
||||||
<label for="datefrom" class="form-label">Datum von:</label>
|
|
||||||
<input type="date" id="datefrom" name="datefrom" class="form-control">
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-md-6">
|
|
||||||
<label for="dateto" class="form-label">Datum bis:</label>
|
|
||||||
<input type="date" id="dateto" name="dateto" class="form-control">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<!-- Ende Suchoptionen -->
|
|
||||||
|
|
||||||
<!-- Buttons -->
|
|
||||||
<div class="mb-3">
|
|
||||||
<button type="submit" class="btn btn-primary">Suchen</button>
|
|
||||||
<button type="button" id="clearBtn" class="btn btn-secondary ms-2">zurücksetzen</button>
|
|
||||||
<button type="button" id="backBtn" class="btn btn-secondary ms-2">beenden</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<!-- AJAX-loaded results -->
|
|
||||||
<div id="results"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<script>
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
|
|
||||||
// Function to render search results from a response object
|
|
||||||
function renderResults(data) {
|
|
||||||
const resultsDiv = document.getElementById('results');
|
|
||||||
resultsDiv.innerHTML = ''; // Clear previous results
|
|
||||||
|
|
||||||
if (data.results && data.results.length > 0) {
|
|
||||||
data.results.forEach(file => {
|
|
||||||
const card = document.createElement('div');
|
|
||||||
card.className = 'card';
|
|
||||||
card.innerHTML = `
|
|
||||||
<div class="card-body">
|
|
||||||
<h5 class="card-title">
|
|
||||||
<a href="/path/${file.relative_path}" target="_blank">${file.filename}</a>
|
|
||||||
</h5>
|
|
||||||
<h6 class="card-subtitle mb-2 text-muted">${file.relative_path}</h6>
|
|
||||||
${ file.transcript_hits !== undefined
|
|
||||||
? `<p class="card-text">Treffer im Transkript: ${file.transcript_hits}</p>`
|
|
||||||
: `<p class="card-text">Downloads: ${file.hitcount}</p>`
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
resultsDiv.appendChild(card);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
resultsDiv.innerHTML = '<p>No results found.</p>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restore previous search response if available from localStorage
|
|
||||||
const previousResponse = localStorage.getItem("searchResponse");
|
|
||||||
if (previousResponse) {
|
|
||||||
try {
|
|
||||||
const data = JSON.parse(previousResponse);
|
|
||||||
renderResults(data);
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Error parsing searchResponse from localStorage:', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restore previous search word (Suchwort) if available
|
|
||||||
const previousQuery = localStorage.getItem("searchQuery");
|
|
||||||
if (previousQuery) {
|
|
||||||
document.getElementById('query').value = previousQuery;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restore previous selected category if available, otherwise default remains "Alles"
|
|
||||||
const previousCategory = localStorage.getItem("searchCategory");
|
|
||||||
if (previousCategory !== null) {
|
|
||||||
const radio = document.querySelector('input[name="category"][value="' + previousCategory + '"]');
|
|
||||||
if (radio) {
|
|
||||||
radio.checked = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restore the checkbox state for "Im Transkript suchen"
|
|
||||||
const previousIncludeTranscript = localStorage.getItem("searchIncludeTranscript");
|
|
||||||
if (previousIncludeTranscript !== null) {
|
|
||||||
document.getElementById('includeTranscript').checked = (previousIncludeTranscript === 'true');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Form submission event
|
|
||||||
document.getElementById('searchForm').addEventListener('submit', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
const query = document.getElementById('query').value.trim();
|
|
||||||
const includeTranscript = document.getElementById('includeTranscript').checked;
|
|
||||||
|
|
||||||
// Get the selected category radio button, if any
|
|
||||||
const categoryRadio = document.querySelector('input[name="category"]:checked');
|
|
||||||
const category = categoryRadio ? categoryRadio.value : '';
|
|
||||||
|
|
||||||
// Prevent accidental re-selection of already selected radio buttons
|
|
||||||
const radios = document.querySelectorAll('input[name="category"]');
|
|
||||||
radios.forEach(radio => {
|
|
||||||
radio.addEventListener('mousedown', function(e) {
|
|
||||||
this.wasChecked = this.checked;
|
|
||||||
});
|
|
||||||
radio.addEventListener('click', function(e) {
|
|
||||||
if (this.wasChecked) {
|
|
||||||
this.checked = false;
|
|
||||||
this.wasChecked = false;
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Prepare form data for the fetch request
|
|
||||||
// Send the query and the category as separate parameters.
|
|
||||||
const formData = new FormData();
|
|
||||||
formData.append('query', query);
|
|
||||||
formData.append('category', category);
|
|
||||||
formData.append('folder', document.getElementById('folder').value);
|
|
||||||
formData.append('datefrom', document.getElementById('datefrom').value);
|
|
||||||
formData.append('dateto', document.getElementById('dateto').value);
|
|
||||||
formData.append('includeTranscript', includeTranscript);
|
|
||||||
|
|
||||||
fetch('/searchcommand', {
|
|
||||||
method: 'POST',
|
|
||||||
body: formData
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
// Render the results
|
|
||||||
renderResults(data);
|
|
||||||
// Store the raw response in localStorage
|
|
||||||
try {
|
|
||||||
localStorage.setItem("searchResponse", JSON.stringify(data));
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Error saving searchResponse to localStorage:', e);
|
|
||||||
}
|
|
||||||
// Save the search word, selected category, and checkbox state in localStorage
|
|
||||||
localStorage.setItem("searchQuery", query);
|
|
||||||
localStorage.setItem("searchCategory", category);
|
|
||||||
localStorage.setItem("searchIncludeTranscript", includeTranscript);
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('Error:', error);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Clear button event handler
|
|
||||||
document.getElementById('clearBtn').addEventListener('click', function() {
|
|
||||||
// Remove stored items
|
|
||||||
localStorage.removeItem("searchResponse");
|
|
||||||
localStorage.removeItem("searchQuery");
|
|
||||||
localStorage.removeItem("searchCategory");
|
|
||||||
localStorage.removeItem("folder");
|
|
||||||
localStorage.removeItem("datefrom");
|
|
||||||
localStorage.removeItem("dateto");
|
|
||||||
localStorage.removeItem("searchIncludeTranscript");
|
|
||||||
// Reset form fields to defaults
|
|
||||||
document.getElementById('query').value = '';
|
|
||||||
document.querySelector('input[name="category"][value=""]').checked = true;
|
|
||||||
const otherRadios = document.querySelectorAll('input[name="category"]:not([value=""])');
|
|
||||||
otherRadios.forEach(radio => radio.checked = false);
|
|
||||||
document.getElementById('folder').value = ''; // Reset to "Alle"
|
|
||||||
document.getElementById('datefrom').value = ''; // Reset date from
|
|
||||||
document.getElementById('dateto').value = ''; // Reset date to
|
|
||||||
document.getElementById('includeTranscript').checked = false;
|
|
||||||
// Clear the results div
|
|
||||||
document.getElementById('results').innerHTML = '';
|
|
||||||
});
|
|
||||||
|
|
||||||
// Back button event handler - redirect to the root path
|
|
||||||
document.getElementById('backBtn').addEventListener('click', function() {
|
|
||||||
window.location.href = '/';
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
function syncThemeColor() {
|
|
||||||
// read the CSS variable from :root (or any selector)
|
|
||||||
const cssVar = getComputedStyle(document.documentElement)
|
|
||||||
.getPropertyValue('--dark-background').trim();
|
|
||||||
if (cssVar) {
|
|
||||||
document
|
|
||||||
.querySelector('meta[name="theme-color"]')
|
|
||||||
.setAttribute('content', cssVar);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// sync once on load
|
|
||||||
document.addEventListener('DOMContentLoaded', syncThemeColor);
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- Bootstrap Bundle with Popper -->
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
Loading…
x
Reference in New Issue
Block a user