fix: logging

This commit is contained in:
lelo 2026-01-04 15:22:06 +00:00
parent 495f33aa68
commit d2952c3ac0
3 changed files with 79 additions and 31 deletions

27
app.py
View File

@ -35,6 +35,7 @@ import helperfunctions as hf
import search_db_analyzer as sdb import search_db_analyzer as sdb
import fnmatch import fnmatch
import openpyxl import openpyxl
from collections import OrderedDict
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'])
@ -44,6 +45,27 @@ cache_image = diskcache.Cache('./filecache_image', size_limit= app_config['filec
cache_video = diskcache.Cache('./filecache_video', size_limit= app_config['filecache_size_limit_video'] * 1024**3) cache_video = diskcache.Cache('./filecache_video', size_limit= app_config['filecache_size_limit_video'] * 1024**3)
cache_other = diskcache.Cache('./filecache_other', size_limit= app_config['filecache_size_limit_other'] * 1024**3) cache_other = diskcache.Cache('./filecache_other', size_limit= app_config['filecache_size_limit_other'] * 1024**3)
_logged_request_ids = OrderedDict()
_logged_request_ids_lock = threading.Lock()
_LOGGED_REQUEST_IDS_MAX = 2048
def _is_duplicate_request(req_id: str) -> bool:
if not req_id:
return False
with _logged_request_ids_lock:
return req_id in _logged_request_ids
def _mark_request_logged(req_id: str):
if not req_id:
return
with _logged_request_ids_lock:
_logged_request_ids[req_id] = None
_logged_request_ids.move_to_end(req_id)
if len(_logged_request_ids) > _LOGGED_REQUEST_IDS_MAX:
_logged_request_ids.popitem(last=False)
app = Flask(__name__) app = Flask(__name__)
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1) app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1)
@ -782,6 +804,7 @@ def serve_file(subpath):
ip_address = request.remote_addr ip_address = request.remote_addr
user_agent = request.headers.get('User-Agent') user_agent = request.headers.get('User-Agent')
range_header = request.headers.get('Range', '') range_header = request.headers.get('Range', '')
req_id = request.args.get('req') or request.headers.get('X-Request-Id')
def is_range_prefetch(header, ua): def is_range_prefetch(header, ua):
""" """
@ -886,6 +909,7 @@ def serve_file(subpath):
response.headers['Cache-Control'] = 'public, max-age=86400' response.headers['Cache-Control'] = 'public, max-age=86400'
if do_log and not small: if do_log and not small:
if not _is_duplicate_request(req_id):
a.log_file_access( a.log_file_access(
cache_key, cache_key,
os.path.getsize(file_path), os.path.getsize(file_path),
@ -896,6 +920,7 @@ def serve_file(subpath):
cached_hit, cached_hit,
request.method request.method
) )
_mark_request_logged(req_id)
return response return response
# 5) Non-image branch: check if cached, otherwise create partial cache file # 5) Non-image branch: check if cached, otherwise create partial cache file
@ -1041,6 +1066,7 @@ def serve_file(subpath):
# 7) Logging # 7) Logging
if do_log: if do_log:
if not _is_duplicate_request(req_id):
a.log_file_access( a.log_file_access(
subpath, subpath,
filesize, filesize,
@ -1051,6 +1077,7 @@ def serve_file(subpath):
cached_hit, cached_hit,
request.method request.method
) )
_mark_request_logged(req_id)
return response return response

View File

@ -263,6 +263,9 @@ function preload_audio() {
} }
} }
const TRACK_CLICK_DEBOUNCE_MS = 3000;
let lastTrackClick = { url: null, ts: 0 };
// Attach event listeners for directory, breadcrumb, file, and transcript links. // Attach event listeners for directory, breadcrumb, file, and transcript links.
function attachEventListeners() { function attachEventListeners() {
// Directory link clicks. // Directory link clicks.
@ -295,20 +298,33 @@ document.querySelectorAll('.play-file').forEach(link => {
event.preventDefault(); event.preventDefault();
const { fileType, url: relUrl, index } = this.dataset; const { fileType, url: relUrl, index } = this.dataset;
const now = Date.now();
if (fileType === 'music') {
// If this is the same track already loaded, ignore to avoid extra GETs and unselects.
if (player.currentRelUrl === relUrl) {
return;
}
// Remove the class from all file items. // Remove the class from all file items.
document.querySelectorAll('.file-item').forEach(item => { document.querySelectorAll('.file-item').forEach(item => {
item.classList.remove('currently-playing'); item.classList.remove('currently-playing');
}); });
if (fileType === 'music') { // Debounce repeated clicks on the same track to avoid extra GETs.
if (lastTrackClick.url === relUrl && now - lastTrackClick.ts < TRACK_CLICK_DEBOUNCE_MS) {
return;
}
lastTrackClick = { url: relUrl, ts: now };
// Update the current music index. // Update the current music index.
currentMusicIndex = index !== undefined ? parseInt(index) : -1; currentMusicIndex = index !== undefined ? parseInt(index) : -1;
// 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');
player.loadTrack(relUrl); const reqId = crypto.randomUUID ? crypto.randomUUID() : (Date.now().toString(36) + Math.random().toString(36).slice(2));
player.loadTrack(relUrl, reqId);
// Delay preloading to avoid blocking playback. // Delay preloading to avoid blocking playback.
setTimeout(preload_audio, 1000); setTimeout(preload_audio, 1000);
@ -318,7 +334,9 @@ document.querySelectorAll('.play-file').forEach(link => {
openGalleryModal(relUrl); openGalleryModal(relUrl);
} else { } else {
// serve like a download // serve like a download
window.location.href = `/media/${relUrl}`; const reqId = crypto.randomUUID ? crypto.randomUUID() : (Date.now().toString(36) + Math.random().toString(36).slice(2));
const urlWithReq = `/media/${relUrl}${relUrl.includes('?') ? '&' : '?'}req=${encodeURIComponent(reqId)}`;
window.location.href = urlWithReq;
} }
}); });
}); });

View File

@ -168,7 +168,11 @@ class SimpleAudioPlayer {
document.body.removeChild(a); document.body.removeChild(a);
} }
async loadTrack(relUrl) { async loadTrack(relUrl, reqId) {
this.currentRelUrl = relUrl;
const requestId = reqId || (crypto.randomUUID ? crypto.randomUUID() : (Date.now().toString(36) + Math.random().toString(36).slice(2)));
const urlWithReq = `/media/${relUrl}${relUrl.includes('?') ? '&' : '?'}req=${encodeURIComponent(requestId)}`;
this.audio.pause(); this.audio.pause();
this.container.style.display = 'block'; this.container.style.display = 'block';
this.nowInfo.textContent = 'Loading…'; this.nowInfo.textContent = 'Loading…';
@ -177,13 +181,13 @@ class SimpleAudioPlayer {
this.abortCtrl = new AbortController(); this.abortCtrl = new AbortController();
try { try {
const head = await fetch(`/media/${relUrl}`, { const head = await fetch(urlWithReq, {
method: 'HEAD', method: 'HEAD',
signal: this.abortCtrl.signal signal: this.abortCtrl.signal
}); });
if (!head.ok) throw new Error(`Status ${head.status}`); if (!head.ok) throw new Error(`Status ${head.status}`);
this.audio.src = `/media/${relUrl}`; this.audio.src = urlWithReq;
await this.audio.play(); await this.audio.play();
// Full breadcrumb // Full breadcrumb
@ -265,4 +269,3 @@ class SimpleAudioPlayer {
// Initialize instance // Initialize instance
const player = new SimpleAudioPlayer(); const player = new SimpleAudioPlayer();