Merge remote-tracking branch 'origin/master' into development

This commit is contained in:
lelo 2025-06-14 18:39:37 +00:00
commit f1ff833d89
3 changed files with 81 additions and 66 deletions

8
app.py
View File

@ -249,6 +249,7 @@ def serve_sw():
@app.route('/api/path/<path:subpath>') @app.route('/api/path/<path:subpath>')
@auth.require_secret @auth.require_secret
def api_browse(subpath): def api_browse(subpath):
toplist_categories = app_config.get('toplist_categories', None)
if subpath == '': # root directory if subpath == '': # root directory
foldernames = [] foldernames = []
@ -260,7 +261,8 @@ def api_browse(subpath):
'directories': foldernames, 'directories': foldernames,
'files': [], 'files': [],
'folder_today': a.return_folder_today(), 'folder_today': a.return_folder_today(),
'folder_yesterday': a.return_folder_yesterday() 'folder_yesterday': a.return_folder_yesterday(),
'toplist_enabled': bool(toplist_categories)
}) })
if subpath == 'heute' or subpath == 'gestern': if subpath == 'heute' or subpath == 'gestern':
@ -285,13 +287,11 @@ def api_browse(subpath):
files = [] files = []
split_path = subpath.split('/') split_path = subpath.split('/')
valid_categories = ['Predigt', 'Erzählung', 'Gedicht', 'Gemeinsamer Gesang', 'Chor', 'Kinderchor', 'Jugendchor', 'Orchester', 'Instrumental', 'Gruppenlied']
if len(split_path) == 1 and split_path[0] == 'toplist': if len(split_path) == 1 and split_path[0] == 'toplist':
foldernames = [ foldernames = [
{ {
'name': categorie, 'path': 'toplist/' + categorie 'name': categorie, 'path': 'toplist/' + categorie
} for categorie in valid_categories } for categorie in toplist_categories
] ]
elif len(split_path) > 1 and split_path[0] == 'toplist': elif len(split_path) > 1 and split_path[0] == 'toplist':
files = hf.generate_top_list(split_path[1]) files = hf.generate_top_list(split_path[1])

View File

@ -1,57 +1,74 @@
from flask import session from flask import session
from datetime import datetime
import re import re
import os import os
import sqlite3 import sqlite3
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import Optional
log_db = sqlite3.connect("access_log.db", check_same_thread=False) log_db = sqlite3.connect("access_log.db", check_same_thread=False)
def extract_date_from_string(string_with_date):
# grab X.Y.Z where X,Y,Z are 14 digits
m = re.search(r'(\d{1,4}\.\d{1,2}\.\d{1,4})', string_with_date)
if not m:
return None
date_str = m.group(1)
parts = date_str.split('.')
# 1) Unambiguous “last group = YYYY” # Precompiled regex to find date-like patterns: either dotted X.Y.Z or ISO dashed YYYY-MM-DD
if len(parts) == 3 and len(parts[2]) == 4: _DATE_REGEX = re.compile(
fmt = '%d.%m.%Y' r"(" # start group
r"\d{4}-\d{1,2}-\d{1,2}" # ISO: YYYY-M-D or YYYY-MM-DD
r"|" # or
r"\d{1,4}\.\d{1,2}\.\d{1,4}" # dotted: X.Y.Z, where each is 14 digits (year may be 14)
r")"
)
# 2) Unambiguous “first group = YYYY”
elif len(parts) == 3 and len(parts[0]) == 4:
fmt = '%Y.%m.%d'
# 3) Ambiguous “XX.XX.XX” → prefer DD.MM.YY, fallback to YY.MM.DD def _try_parse(date_str: str, fmt: str) -> Optional[datetime]:
elif len(parts) == 3 and all(len(p) == 2 for p in parts): """Try to parse date_str with fmt, return datetime or None."""
# try last-group-as-year first
try:
dt = datetime.strptime(date_str, '%d.%m.%y')
return dt.strftime('%Y-%m-%d')
except ValueError:
# fallback to first-group-as-year
fmt = '%y.%m.%d'
else:
# optional: handle ISO with dashes
if '-' in date_str:
try:
dt = datetime.strptime(date_str, '%Y-%m-%d')
return dt.strftime('%Y-%m-%d')
except ValueError:
return None
return None
# parse with whichever fmt we settled on
try: try:
dt = datetime.strptime(date_str, fmt) return datetime.strptime(date_str, fmt)
return dt.strftime('%Y-%m-%d')
except ValueError: except ValueError:
return None return None
def extract_date_from_string(text: str) -> Optional[str]:
"""
Extract the first date-like substring from text and return it in ISO format (YYYY-MM-DD).
Supports:
- ISO-style dates with dashes (YYYY-M-D or YYYY-MM-DD)
- Dotted dates (DD.MM.YYYY, YYYY.MM.DD, DD.MM.YY, YY.MM.DD)
"""
match = _DATE_REGEX.search(text)
if not match:
return None
date_str = match.group(1)
# 1) ISO dashed format takes priority
if '-' in date_str:
dt = _try_parse(date_str, '%Y-%m-%d')
return dt.strftime('%Y-%m-%d') if dt else None
# 2) Dotted formats
parts = date_str.split('.')
candidates = []
# Unambiguous: last part 4 digits → DD.MM.YYYY
if len(parts) == 3 and len(parts[2]) == 4:
candidates.append('%d.%m.%Y')
# Unambiguous: first part 4 digits → YYYY.MM.DD
if len(parts) == 3 and len(parts[0]) == 4:
candidates.append('%Y.%m.%d')
# Ambiguous two-digit groups: try DD.MM.YY, then YY.MM.DD
if len(parts) == 3 and all(len(p) == 2 for p in parts):
candidates.extend(['%d.%m.%y', '%y.%m.%d'])
# Try each candidate
for fmt in candidates:
dt = _try_parse(date_str, fmt)
if dt:
return dt.strftime('%Y-%m-%d')
# no valid parse
return None
def extract_structure_from_string(input_string): def extract_structure_from_string(input_string):
# extract category and titel from filename # extract category and titel from filename
filepathname_ext = os.path.splitext(input_string)[0] # remove file extension filepathname_ext = os.path.splitext(input_string)[0] # remove file extension
@ -67,30 +84,28 @@ def extract_structure_from_string(input_string):
# first part not a number # first part not a number
pass pass
if 'predig' in left_side.lower() or 'thema' in left_side.lower(): # define your mapping: category → list of trigger-words
category = 'Predigt' CATEGORY_KEYWORDS = {
elif 'wort' in left_side.lower() or 'einladung' in left_side.lower() or 'begrüßung' in left_side.lower() or 'ansprache' in left_side.lower() or 'einleitung' in left_side.lower() or 'aufruf zum' in left_side.lower() or 'zuruf zum' in left_side.lower(): 'Predigt': ['predig', 'thema'],
category = 'Vorwort' 'Vorwort': ['wort', 'einladung', 'begrüßung', 'ansprache', 'einleitung', 'aufruf zum', 'zuruf zum'],
elif 'kinderchor' in left_side.lower(): 'Kinderchor': ['kinderchor'],
category = 'Kinderchor' 'Jugendchor': ['jugendchor'],
elif 'jugendchor' in left_side.lower(): 'Orchester': ['orchester', 'sinfonie', 'symphonie'],
category = 'Jugendchor' 'Chor': ['chor'],
elif 'orchester' in input_string.lower() or 'sinfonie' in input_string.lower() or 'symphonie' in input_string.lower(): 'Gemeinsamer Gesang': ['gemeinsam', 'gemeindelied', 'gemeinsamer gesang'],
category = 'Orchester' 'Gruppenlied': ['gruppenlied', 'jugend', 'lied', 'musikgruppe'],
elif 'chor' in left_side.lower(): 'Gedicht': ['gedicht'],
category = 'Chor' 'Erzählung': ['vortrag', 'erzä', 'program'],
elif 'gemeinsam' in left_side.lower() or 'gemeindelied' in left_side.lower() or 'gemeinsamer gesang' in input_string.lower(): 'Instrumental': ['instrumental', 'musikstück', 'harfenstück'],
category = 'Gemeinsamer Gesang' }
elif 'gruppenlied' in left_side.lower() or 'jugend' in left_side.lower() or 'lied' in left_side.lower():
category = 'Gruppenlied' # walk the dict in your desired priority order
elif 'gedicht' in left_side.lower(): category = None
category = 'Gedicht' text = left_side.lower()
elif 'vortrag' in left_side.lower() or 'erzä' in left_side.lower() or 'program' in left_side.lower(): for cat, keywords in CATEGORY_KEYWORDS.items():
category = 'Erzählung' if any(kw in text for kw in keywords):
elif 'instrumental' in input_string.lower() or 'musikstück' in left_side.lower() or 'harfenstück' in left_side.lower(): category = cat
category = 'Instrumental' break
else:
category = None
if right_side: if right_side:
titel, name = right_side.split('-', 1) if '-' in right_side else (right_side, None) titel, name = right_side.split('-', 1) if '-' in right_side else (right_side, None)

View File

@ -113,7 +113,7 @@ function renderContent(data) {
} else if (data.breadcrumbs.length === 1 && Array.isArray(data.folder_yesterday) && data.folder_yesterday.length > 0) { } else if (data.breadcrumbs.length === 1 && Array.isArray(data.folder_yesterday) && data.folder_yesterday.length > 0) {
contentHTML += `<li class="directory-item"><a href="#" class="directory-link" data-path="gestern">📅 Gestern</a></li>`; contentHTML += `<li class="directory-item"><a href="#" class="directory-link" data-path="gestern">📅 Gestern</a></li>`;
} }
if (data.breadcrumbs.length === 1 ) { if (data.breadcrumbs.length === 1 && data.toplist_enabled) {
contentHTML += `<li class="directory-item"><a href="#" class="directory-link" data-path="toplist">🔥 oft angehört</a></li>`; contentHTML += `<li class="directory-item"><a href="#" class="directory-link" data-path="toplist">🔥 oft angehört</a></li>`;
} }
console.log(data.folder_today, data.folder_yesterday); console.log(data.folder_today, data.folder_yesterday);