From 5a3ad7ef6ef9386b5a799bc49a657fa65ea3d1fe Mon Sep 17 00:00:00 2001 From: lelo Date: Mon, 26 Jan 2026 19:02:43 +0000 Subject: [PATCH] gui improvements --- index_for_search.py | 23 ++++++++++++++++++++++ search.py | 3 +++ static/audioplayer.js | 30 +++++++++++++++++++++------- static/connections.js | 6 +++++- static/search.js | 23 ++++++++++++++++++++-- templates/app.html | 40 ++++++++++++++++++-------------------- templates/connections.html | 5 ++++- 7 files changed, 98 insertions(+), 32 deletions(-) diff --git a/index_for_search.py b/index_for_search.py index 056b346..fe9400d 100755 --- a/index_for_search.py +++ b/index_for_search.py @@ -4,6 +4,7 @@ import sqlite3 from datetime import datetime from time import monotonic import re +from typing import Optional import helperfunctions as hf SEARCH_DB_NAME = 'search.db' @@ -139,6 +140,20 @@ def build_transcript_index(transcript_dir: str, stats: dict): return index except FileNotFoundError: return None + + +def read_text_file(path: str) -> Optional[str]: + try: + with open(path, 'r', encoding='utf-8') as handle: + return handle.read() + except UnicodeDecodeError: + try: + with open(path, 'r', encoding='cp1252') as handle: + return handle.read() + except Exception: + return None + except Exception: + return None except PermissionError: log_permission_error(transcript_dir, stats) return None @@ -264,6 +279,14 @@ def updatefileindex(): except Exception: transcript_errors += 1 + if transcript is None and filetype == '.sng': + sng_text = read_text_file(entry_path) + if sng_text is not None: + transcript = sng_text + transcripts_read += 1 + else: + transcript_errors += 1 + category, titel, name = extract_structure(entry.name) performance_date = extract_date(relative_path) diff --git a/search.py b/search.py index e08211e..3be553e 100644 --- a/search.py +++ b/search.py @@ -3,6 +3,7 @@ from flask import Flask, render_template, request, request, jsonify, session import random import json from datetime import datetime +from urllib.parse import quote app = Flask(__name__) @@ -122,6 +123,8 @@ def searchcommand(): transcript.lower().count(w.lower()) for w in words ) record.pop('transcript', None) + record['fulltext_url'] = f"/media/{quote(record.get('relative_path', ''), safe='/')}" + record['fulltext_type'] = 'sng' if (record.get('filetype') or '').lower() == '.sng' else 'transcript' # convert date to TT.MM.YYYY format if record.get('performance_date'): try: diff --git a/static/audioplayer.js b/static/audioplayer.js index dcb8ffe..6d1d750 100644 --- a/static/audioplayer.js +++ b/static/audioplayer.js @@ -268,24 +268,40 @@ class SimpleAudioPlayer { } async fileDownload() { + let relUrl = this.currentRelUrl || ''; const src = this.audio.currentSrc || this.audio.src; - if (!src) return; - // Extract the subpath from the src - const urlObj = new URL(src, window.location.href); - let subpath = urlObj.pathname; - subpath = subpath.slice('/media'.length); + if (!relUrl && src) { + try { + const urlObj = new URL(src, window.location.href); + if (urlObj.pathname.startsWith('/media/')) { + relUrl = urlObj.pathname.slice('/media/'.length); + } + } catch { + // ignore parsing errors + } + } + + if (!relUrl) return; + + const encodedSubpath = relUrl + .replace(/^\/+/, '') + .split('/') + .map(segment => encodeURIComponent(decodeURIComponent(segment))) + .join('/'); // Fetch the tokenized URL from your backend let tokenizedUrl; try { - const resp = await fetch(`/create_dltoken${subpath}`); + const resp = await fetch(`/create_dltoken/${encodedSubpath}`); if (!resp.ok) return; - tokenizedUrl = await resp.text(); + tokenizedUrl = (await resp.text()).trim(); } catch { return; } + if (!tokenizedUrl) return; + // Build the URL with cache-buster const downloadUrl = new URL(tokenizedUrl, window.location.href); diff --git a/static/connections.js b/static/connections.js index 3e54b29..9f260be 100644 --- a/static/connections.js +++ b/static/connections.js @@ -5,6 +5,7 @@ let animateInterval = null; let statsInterval = null; let refreshMapSize = null; + const TABLE_LIMIT = 20; function initConnectionsPage() { const mapEl = document.getElementById('map'); @@ -190,6 +191,8 @@ }); function updateStats(data) { + const headerCount = document.getElementById('connectionsTotalHeader'); + if (headerCount) headerCount.textContent = `${data.length} Verbindungen`; const totalConnections = document.getElementById('totalConnections'); if (totalConnections) totalConnections.textContent = data.length; const totalBytes = data.reduce((sum, rec) => { @@ -276,7 +279,8 @@ socket.on('recent_connections', data => { updateStats(data); - animateTableWithNewRow(data); + const tableData = data.slice(0, TABLE_LIMIT); + animateTableWithNewRow(tableData); }); } diff --git a/static/search.js b/static/search.js index 741651f..d385c96 100644 --- a/static/search.js +++ b/static/search.js @@ -23,6 +23,12 @@ function initSearch() { const isAudio = audioExts.includes((file.filetype || '').toLowerCase()); const isSng = (file.filetype || '').toLowerCase() === '.sng' || (file.filename || '').toLowerCase().endsWith('.sng'); const encodedRelPath = encodeURI(file.relative_path); + const fulltextType = file.fulltext_type || 'transcript'; + const fulltextUrl = file.fulltext_url || transcriptURL; + const fulltextLabel = 'Treffer im Volltext'; + const fulltextAction = fulltextType === 'sng' + ? `` + : ``; card.className = 'card'; let fileAction = ''; @@ -39,8 +45,8 @@ function initSearch() {

${fileAction}

Anzahl Downloads: ${file.hitcount}

- ${ file.performance_date !== undefined ? `

Datum: ${file.performance_date}

` : ``} - ${ file.transcript_hits !== undefined ? `

Treffer im Transkript: ${file.transcript_hits}

` : ``} + ${ (file.performance_date !== undefined && file.performance_date !== null && String(file.performance_date).trim() !== '') ? `

Datum: ${file.performance_date}

` : ``} + ${ file.transcript_hits !== undefined ? `

${fulltextLabel}: ${file.transcript_hits} ${fulltextAction}

` : ``} `; resultsDiv.appendChild(card); @@ -54,6 +60,7 @@ function initSearch() { attachEventListeners(); attachSearchFolderButtons(); attachSearchSngButtons(); + attachSearchFulltextButtons(); } else { resultsDiv.innerHTML = `
${total} Treffer

Keine Treffer gefunden.

`; } @@ -248,6 +255,18 @@ function attachSearchSngButtons() { }); } +function attachSearchFulltextButtons() { + document.querySelectorAll('.open-sng-link').forEach(link => { + link.addEventListener('click', (e) => { + e.preventDefault(); + const path = link.dataset.path; + if (typeof window.openSngModal === 'function') { + window.openSngModal(path); + } + }); + }); +} + function openFolderAndHighlight(folderPath, filePath) { const targetFolder = folderPath || ''; // Switch back to main view before loading folder diff --git a/templates/app.html b/templates/app.html index 5e26aa0..f7bba3e 100644 --- a/templates/app.html +++ b/templates/app.html @@ -63,6 +63,11 @@ + +
+ + +
@@ -81,6 +86,20 @@
+ + +
+ + +
+ +
+
@@ -114,19 +133,6 @@
- -
- - -
- -
-
@@ -157,14 +163,6 @@
- -
- - -
- -
-
diff --git a/templates/connections.html b/templates/connections.html index a7657b3..0bfa4d0 100644 --- a/templates/connections.html +++ b/templates/connections.html @@ -103,7 +103,10 @@ {% block content %}
-

Verbindungen der letzten 10 Minuten (nur Audio)

+

+ Verbindungen der letzten 10 Minuten (nur Audio) + 0 Verbindungen +

Diese Ansicht listet ausschließlich Zugriffe auf Audio-Dateien.