From d2952c3ac07f307a5fcb7c07649e2feb7b779451 Mon Sep 17 00:00:00 2001 From: lelo Date: Sun, 4 Jan 2026 15:22:06 +0000 Subject: [PATCH] fix: logging --- app.py | 67 ++++++++++++++++++++++++++++++------------- static/app.js | 32 ++++++++++++++++----- static/audioplayer.js | 11 ++++--- 3 files changed, 79 insertions(+), 31 deletions(-) diff --git a/app.py b/app.py index 6ac00f5..556e620 100755 --- a/app.py +++ b/app.py @@ -35,6 +35,7 @@ import helperfunctions as hf import search_db_analyzer as sdb import fnmatch import openpyxl +from collections import OrderedDict app_config = auth.return_app_config() 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_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.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 user_agent = request.headers.get('User-Agent') 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): """ @@ -886,16 +909,18 @@ def serve_file(subpath): response.headers['Cache-Control'] = 'public, max-age=86400' if do_log and not small: - a.log_file_access( - cache_key, - os.path.getsize(file_path), - mime, - ip_address, - user_agent, - session['device_id'], - cached_hit, - request.method - ) + if not _is_duplicate_request(req_id): + a.log_file_access( + cache_key, + os.path.getsize(file_path), + mime, + ip_address, + user_agent, + session['device_id'], + cached_hit, + request.method + ) + _mark_request_logged(req_id) return response # 5) Non-image branch: check if cached, otherwise create partial cache file @@ -1041,16 +1066,18 @@ def serve_file(subpath): # 7) Logging if do_log: - a.log_file_access( - subpath, - filesize, - mime, - ip_address, - user_agent, - session['device_id'], - cached_hit, - request.method - ) + if not _is_duplicate_request(req_id): + a.log_file_access( + subpath, + filesize, + mime, + ip_address, + user_agent, + session['device_id'], + cached_hit, + request.method + ) + _mark_request_logged(req_id) return response diff --git a/static/app.js b/static/app.js index f503de6..b1c07ba 100644 --- a/static/app.js +++ b/static/app.js @@ -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. function attachEventListeners() { // Directory link clicks. @@ -295,20 +298,33 @@ document.querySelectorAll('.play-file').forEach(link => { event.preventDefault(); const { fileType, url: relUrl, index } = this.dataset; - - // Remove the class from all file items. - document.querySelectorAll('.file-item').forEach(item => { - item.classList.remove('currently-playing'); - }); + 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. + document.querySelectorAll('.file-item').forEach(item => { + item.classList.remove('currently-playing'); + }); + + // 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. currentMusicIndex = index !== undefined ? parseInt(index) : -1; // Mark the clicked item as 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. setTimeout(preload_audio, 1000); @@ -318,7 +334,9 @@ document.querySelectorAll('.play-file').forEach(link => { openGalleryModal(relUrl); } else { // 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; } }); }); diff --git a/static/audioplayer.js b/static/audioplayer.js index 3744813..69ef2a2 100644 --- a/static/audioplayer.js +++ b/static/audioplayer.js @@ -168,7 +168,11 @@ class SimpleAudioPlayer { 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.container.style.display = 'block'; this.nowInfo.textContent = 'Loading…'; @@ -177,13 +181,13 @@ class SimpleAudioPlayer { this.abortCtrl = new AbortController(); try { - const head = await fetch(`/media/${relUrl}`, { + const head = await fetch(urlWithReq, { method: 'HEAD', signal: this.abortCtrl.signal }); if (!head.ok) throw new Error(`Status ${head.status}`); - this.audio.src = `/media/${relUrl}`; + this.audio.src = urlWithReq; await this.audio.play(); // Full breadcrumb @@ -265,4 +269,3 @@ class SimpleAudioPlayer { // Initialize instance const player = new SimpleAudioPlayer(); -