Compare commits

...

10 Commits

Author SHA1 Message Date
bd3d7509b2 Merge remote-tracking branch 'origin/master' into development 2025-06-03 20:58:58 +00:00
0025f887df Merge remote-tracking branch 'origin/development' 2025-06-03 19:17:52 +00:00
27e42a4e97 add .ai folder to skip 2025-06-03 19:17:40 +00:00
1b374b667e add wildcards in blocked_filenames 2025-06-03 19:16:37 +00:00
20e65f4c69 fix 2025-06-03 19:11:24 +00:00
5345331637 remove unproven edit 2025-06-01 20:30:22 +00:00
13cb4fecbb cleanup 2025-05-30 10:30:06 +00:00
b35e4030ac try fixing download 2025-05-30 10:03:19 +00:00
17c8a41229 cleanup 2025-05-30 09:47:06 +00:00
191b54f113 attempt to fix pwa updates 2025-05-30 09:32:28 +00:00
3 changed files with 67 additions and 28 deletions

27
app.py
View File

@ -24,6 +24,7 @@ import auth
import analytics as a import analytics as a
import folder_secret_config_editor as fsce import folder_secret_config_editor as fsce
import helperfunctions as hf import helperfunctions as hf
import fnmatch
app_config = auth.return_app_config() app_config = auth.return_app_config()
BASE_DIR = os.path.realpath(app_config['BASE_DIR']) BASE_DIR = os.path.realpath(app_config['BASE_DIR'])
@ -136,7 +137,7 @@ def list_directory_contents(directory, subpath):
music_exts = ('.mp3',) music_exts = ('.mp3',)
image_exts = ('.jpg', '.jpeg', '.png', '.gif', '.bmp') image_exts = ('.jpg', '.jpeg', '.png', '.gif', '.bmp')
blocked_filenames = ['Thumbs.db'] blocked_filenames = ['Thumbs.db', '*.mrk']
try: try:
with os.scandir(directory) as it: with os.scandir(directory) as it:
@ -146,12 +147,12 @@ def list_directory_contents(directory, subpath):
if entry.name.startswith('.'): if entry.name.startswith('.'):
continue continue
# Skip blocked_filenames # Skip blocked_filenames using fnmatch for wildcards
if entry.name in blocked_filenames: if any(fnmatch.fnmatch(entry.name, pattern) for pattern in blocked_filenames):
continue continue
if entry.is_dir(follow_symlinks=False): if entry.is_dir(follow_symlinks=False):
if entry.name in ["Transkription", "@eaDir"]: if entry.name in ["Transkription", "@eaDir", ".ai"]:
continue continue
rel_path = os.path.join(subpath, entry.name) if subpath else entry.name rel_path = os.path.join(subpath, entry.name) if subpath else entry.name
@ -238,6 +239,7 @@ def custom_logo(filename):
response.headers['Cache-Control'] = 'public, max-age=86400' response.headers['Cache-Control'] = 'public, max-age=86400'
return response return response
@app.route('/sw.js') @app.route('/sw.js')
def serve_sw(): def serve_sw():
return send_from_directory(os.path.join(app.root_path, 'static'), 'sw.js', mimetype='application/javascript') return send_from_directory(os.path.join(app.root_path, 'static'), 'sw.js', mimetype='application/javascript')
@ -471,17 +473,30 @@ def serve_file(subpath):
# 6) Build response for non-image # 6) Build response for non-image
filesize = os.path.getsize(file_path) filesize = os.path.getsize(file_path)
filename = os.path.basename(full_path) filename = os.path.basename(full_path)
if as_attachment:
download_name = filename
mimetype = 'application/octet-stream'
else:
download_name = None
mimetype = mime
# Single send_file call with proper attachment handling # Single send_file call with proper attachment handling
response = send_file( response = send_file(
file_path, file_path,
mimetype=mime, mimetype=mimetype,
conditional=True, conditional=True,
as_attachment=as_attachment, as_attachment=as_attachment,
download_name=filename if as_attachment else None download_name=filename if as_attachment else None
) )
if not as_attachment:
if as_attachment:
response.headers['X-Content-Type-Options'] = 'nosniff'
response.headers['Content-Disposition'] = 'attachment'
else:
response.headers['Content-Disposition'] = 'inline' response.headers['Content-Disposition'] = 'inline'
response.headers['Cache-Control'] = 'public, max-age=86400' response.headers['Cache-Control'] = 'public, max-age=86400'
# 7) Logging # 7) Logging

View File

@ -1,30 +1,56 @@
const cacheName = 'gottesdienste-v1.11'; const VERSION = '1.19';
const CACHE_NAME = `gottesdienste-v${VERSION}`;
const assets = [ const assets = [
'/', '/',
'/static/app.css', `/static/app.css?v=${VERSION}`,
'/static/app.js', `/static/app.js?v=${VERSION}`,
'/static/gallery.css', `/static/gallery.css?v=${VERSION}`,
'/static/gallery.js', `/static/gallery.js?v=${VERSION}`,
'/static/audioplayer.css', `/static/audioplayer.css?v=${VERSION}`,
'/static/audioplayer.js', `/static/audioplayer.js?v=${VERSION}`,
'/icon/logo-192x192.png', '/icon/logo-192x192.png',
'/icon/logo-300x300.png',
'/icon/logo-512x512.png', '/icon/logo-512x512.png',
'/custom_logo/logoB.png', '/custom_logo/logoB.png',
'/custom_logo/logoW.png' '/custom_logo/logoW.png'
]; ];
self.addEventListener('install', e => { self.addEventListener('install', evt => {
e.waitUntil( self.skipWaiting();
caches.open(cacheName).then(cache => { evt.waitUntil(
return cache.addAll(assets); caches.open(CACHE_NAME)
.then(cache => cache.addAll(assets))
);
});
self.addEventListener('activate', evt => {
self.clients.claim();
evt.waitUntil(
caches.keys().then(keys =>
Promise.all(
keys
.filter(k => k !== CACHE_NAME)
.map(k => caches.delete(k))
)
)
.then(() => {
// Reload to use new files
return self.clients.matchAll({ type: 'window' })
.then(clients =>
clients.forEach(client => client.navigate(client.url))
);
}) })
); );
}); });
self.addEventListener('fetch', e => { self.addEventListener('fetch', evt => {
e.respondWith( if (evt.request.mode === 'navigate') {
caches.match(e.request).then(response => { evt.respondWith(
return response || fetch(e.request); fetch(evt.request).catch(() => caches.match('/app.html'))
}) );
return;
}
evt.respondWith(
caches.match(evt.request).then(cached => cached || fetch(evt.request))
); );
}); });

View File

@ -8,7 +8,7 @@
<meta property="og:title" content="{{ title_long }}" /> <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:description" content="... uns aber, die wir gerettet werden, ist es eine Gotteskraft." />
<meta property="og:image" content="/icon/logo-200x200.png" /> <meta property="og:image" content="/icon/logo-192x192.png" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <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="description" content="... uns aber, die wir gerettet werden, ist es eine Gotteskraft.">
@ -23,7 +23,7 @@
<meta name="theme-color" content="#000"> <meta name="theme-color" content="#000">
<!-- Apple-specific tags --> <!-- Apple-specific tags -->
<link rel="touch-icon" href="{{ url_for('static', filename='icons/icon-192x192.png') }}"> <link rel="touch-icon" href="/icon/logo-192x192.png">
<meta name="mobile-web-app-capable" content="yes"> <meta name="mobile-web-app-capable" content="yes">
<meta name="mobile-web-app-status-bar-style" content="default"> <meta name="mobile-web-app-status-bar-style" content="default">
<meta name="mobile-web-app-title" content="Gottesdienste"> <meta name="mobile-web-app-title" content="Gottesdienste">
@ -253,8 +253,6 @@
if ('serviceWorker' in navigator) { if ('serviceWorker' in navigator) {
window.addEventListener('load', () => { window.addEventListener('load', () => {
navigator.serviceWorker.register('{{ url_for("static", filename="sw.js") }}') navigator.serviceWorker.register('{{ url_for("static", filename="sw.js") }}')
.then(reg => console.log('Service worker registered.', reg))
.catch(err => console.error('Service worker not registered.', err));
}); });
} }
</script> </script>