implement caching for TeslaCam directory scan and add refresh endpoint

This commit is contained in:
zwitschi committed 2026-07-24 12:58:58 +02:00
1 parent a4202afe30
commit 829c28ca69
3 files changed
+57 -6

No files matched your search

+4
View File
@@ -105,6 +105,7 @@ file / environment rather than relying on `.env`.
| `/` | Main page with all videos categorized by camera view |
| `/camera/<camera_view>` | All videos for a single camera view |
| `/video/<path:filepath>` | Streams a video file from the TeslaCam directory |
| `/refresh` | Forces a re-scan of the TeslaCam directory |
## Camera Views
@@ -119,6 +120,9 @@ file / environment rather than relying on `.env`.
## Notes
- The directory scan is cached in memory for `SCAN_CACHE_TTL` seconds
(default 300) to avoid re-walking the tree on every request. Hit
`/refresh` to force a re-scan after copying new clips onto the drive.
- Debug mode is off by default (`FLASK_DEBUG=0`) and is only used by the local
dev server. It has no effect when running under a WSGI server like gunicorn.
- The TeslaCam directory must be accessible from the machine running the app (e.g., a mounted USB drive or network share).
+18 -5
View File
@@ -1029,18 +1029,31 @@
}
});
// Auto-play next clip when video finishes
// Auto-play next clip of the same camera when video finishes
player.addEventListener("ended", function () {
const list = getFiltered();
const i = list.findIndex(function (c) {
if (!current) return;
// Build a list of clips for the SAME camera, sorted like the sidebar
const sameCam = clips
.filter(function (c) {
return c.cam === current.cam;
})
.sort(function (a, b) {
if (sortMode === "name")
return a.filename.localeCompare(b.filename);
const da = tsToDate(a.timestamp),
db = tsToDate(b.timestamp);
const ta = da ? da.getTime() : 0,
tb = db ? db.getTime() : 0;
return sortMode === "oldest" ? ta - tb : tb - ta;
});
const i = sameCam.findIndex(function (c) {
return (
current &&
c.cam === current.cam &&
c.timestamp === current.timestamp &&
c.filename === current.filename
);
});
if (i > -1 && list[i + 1]) playClip(list[i + 1]);
if (i > -1 && sameCam[i + 1]) playClip(sameCam[i + 1]);
});
render();
+35 -1
View File
@@ -11,6 +11,7 @@ play videos from different camera views.
import os
import re
import time
from dotenv import load_dotenv
from flask import Flask, render_template, send_from_directory, abort
@@ -39,8 +40,15 @@ CAMERA_VIEW_MAPPING = {
'right_pillar': 'Right Pillar'
}
# In-memory scan cache. Scanning a large TeslaCam tree on every request is
# expensive, so the result is cached and only refreshed after this many seconds
# (or when /refresh is hit). Override via the SCAN_CACHE_TTL env var.
SCAN_CACHE_TTL = int(os.environ.get('SCAN_CACHE_TTL', '3600'))
def scan_teslacam_videos():
_scan_cache = {'data': None, 'expires_at': 0.0}
def _scan_teslacam_videos_uncached():
"""
Recursively scans the TeslaCam directory tree for video files and categorizes
them by camera view.
@@ -84,6 +92,22 @@ def scan_teslacam_videos():
return videos_by_camera
def scan_teslacam_videos(force_refresh=False):
"""
Returns the cached TeslaCam video scan, refreshing it when the TTL expires
or when `force_refresh` is True. Safe to call on every request.
"""
now = time.time()
if (
_scan_cache['data'] is None
or now >= _scan_cache['expires_at']
or force_refresh
):
_scan_cache['data'] = _scan_teslacam_videos_uncached()
_scan_cache['expires_at'] = now + SCAN_CACHE_TTL
return _scan_cache['data']
def get_camera_display_name(camera_view):
"""Returns human-readable title for a camera view key."""
return CAMERA_VIEW_MAPPING.get(camera_view, camera_view.replace('_', ' ').title())
@@ -137,6 +161,16 @@ def serve_video(filepath):
return send_from_directory(TESLACAM_DIRECTORY, filepath)
@app.route('/refresh')
def refresh():
"""
Forces a re-scan of the TeslaCam directory, bypassing the cache TTL.
Useful after copying new clips onto the drive.
"""
scan_teslacam_videos(force_refresh=True)
return 'Cache refreshed', 200
if __name__ == '__main__':
# Local dev server. For production, run behind a WSGI server instead, e.g.:
# gunicorn -w 4 -b 0.0.0.0:5000 'videoplayer:app'