gui improvements
This commit is contained in:
parent
6cc1fb8d56
commit
5a3ad7ef6e
@ -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)
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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
|
||||
if (!relUrl && src) {
|
||||
try {
|
||||
const urlObj = new URL(src, window.location.href);
|
||||
let subpath = urlObj.pathname;
|
||||
subpath = subpath.slice('/media'.length);
|
||||
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);
|
||||
|
||||
|
||||
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -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'
|
||||
? `<a href="#" class="open-sng-link" data-path="${file.relative_path}" title="Liedtext öffnen"><i class="bi bi-journal-text"></i></a>`
|
||||
: `<a href="#" class="show-transcript" data-url="${fulltextUrl}" data-audio-url="${file.relative_path}" highlight="${file.query}"><i class="bi bi-journal-text"></i></a>`;
|
||||
|
||||
card.className = 'card';
|
||||
let fileAction = '';
|
||||
@ -39,8 +45,8 @@ function initSearch() {
|
||||
<p>${fileAction}</p>
|
||||
<p><button class="btn btn-light btn-sm folder-open-btn" data-folder="${parentFolder}" data-file="${file.relative_path}" style="width:100%;"><i class="bi bi-folder"></i> ${parentFolder || 'Ordner'}</button></p>
|
||||
<p class="card-text">Anzahl Downloads: ${file.hitcount}</p>
|
||||
${ file.performance_date !== undefined ? `<p class="card-text">Datum: ${file.performance_date}</p>` : ``}
|
||||
${ file.transcript_hits !== undefined ? `<p class="card-text">Treffer im Transkript: ${file.transcript_hits} <a href="#" class="show-transcript" data-url="${transcriptURL}" data-audio-url="${file.relative_path}" highlight="${file.query}"><i class="bi bi-journal-text"></i></a></p>` : ``}
|
||||
${ (file.performance_date !== undefined && file.performance_date !== null && String(file.performance_date).trim() !== '') ? `<p class="card-text">Datum: ${file.performance_date}</p>` : ``}
|
||||
${ file.transcript_hits !== undefined ? `<p class="card-text">${fulltextLabel}: ${file.transcript_hits} ${fulltextAction}</p>` : ``}
|
||||
</div>
|
||||
`;
|
||||
resultsDiv.appendChild(card);
|
||||
@ -54,6 +60,7 @@ function initSearch() {
|
||||
attachEventListeners();
|
||||
attachSearchFolderButtons();
|
||||
attachSearchSngButtons();
|
||||
attachSearchFulltextButtons();
|
||||
} else {
|
||||
resultsDiv.innerHTML = `<h5>${total} Treffer</h5><p>Keine Treffer gefunden.</p>`;
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -63,6 +63,11 @@
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<!-- 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 Volltext suchen</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Toggle für Suchoptionen -->
|
||||
@ -81,6 +86,20 @@
|
||||
|
||||
<!-- Suchoptionen einklappbar -->
|
||||
<div id="searchOptions" class="collapse border rounded p-3 mb-3">
|
||||
|
||||
<!-- 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>
|
||||
|
||||
<!-- Kategorie -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Kategorie:</label>
|
||||
@ -114,19 +133,6 @@
|
||||
|
||||
<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>
|
||||
|
||||
<!-- Dateityp -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Dateityp:</label>
|
||||
@ -157,14 +163,6 @@
|
||||
|
||||
<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">
|
||||
|
||||
@ -103,7 +103,10 @@
|
||||
{% block content %}
|
||||
<div class="page-content">
|
||||
<div class="section-header">
|
||||
<h2 style="margin: 0;">Verbindungen der letzten 10 Minuten (nur Audio)</h2>
|
||||
<h2 style="margin: 0;">
|
||||
Verbindungen der letzten 10 Minuten (nur Audio)
|
||||
<span class="text-muted" id="connectionsTotalHeader" style="font-size: 14px; margin-left: 8px;">0 Verbindungen</span>
|
||||
</h2>
|
||||
<p class="text-muted" style="margin: 4px 0 0 0;">Diese Ansicht listet ausschließlich Zugriffe auf Audio-Dateien.</p>
|
||||
<div class="stats">
|
||||
<div class="stat-item">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user