initial app
This commit is contained in:
1 parent
183c6a90bc
commit
cc850350de
4 files changed
+1977
No files matched your search
+135
@@ -0,0 +1,135 @@
|
||||
"""
|
||||
TeslaCam Video Player
|
||||
|
||||
This web app displays TeslaCam videos and allows users to change the camera view.
|
||||
|
||||
It scans the TeslaCam folder for all videos and stores a list sorted by filename
|
||||
and categorized by camera view (format YYYY-MM-DD_HH-MM-SS-${camera_view}.mp4).
|
||||
The app then displays the videos in a web interface, allowing users to select and
|
||||
play videos from different camera views.
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
from flask import Flask, render_template, send_from_directory, abort
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
# Root TeslaCam directory containing RecentClips, SavedClips, SentryClips, etc.
|
||||
TESLACAM_DIRECTORY = r'W:\2026\USA2026\TeslaCam'
|
||||
|
||||
# Regular expression matching TeslaCam filenames: YYYY-MM-DD_HH-MM-SS-${camera_view}.mp4
|
||||
VIDEO_FILENAME_REGEX = re.compile(
|
||||
r'^(\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2})-(\w+)\.mp4$'
|
||||
)
|
||||
|
||||
# Friendly display names for camera views
|
||||
CAMERA_VIEW_MAPPING = {
|
||||
'front': 'Front',
|
||||
'left_repeater': 'Left Repeater',
|
||||
'right_repeater': 'Right Repeater',
|
||||
'rear': 'Rear',
|
||||
'left_pillar': 'Left Pillar',
|
||||
'right_pillar': 'Right Pillar'
|
||||
}
|
||||
|
||||
|
||||
def scan_teslacam_videos():
|
||||
"""
|
||||
Recursively scans the TeslaCam directory tree for video files and categorizes
|
||||
them by camera view.
|
||||
|
||||
Returns:
|
||||
dict: Keys are raw camera view identifiers (e.g. 'front'), values are lists
|
||||
of dictionaries containing file details sorted chronologically.
|
||||
"""
|
||||
videos_by_camera = {}
|
||||
|
||||
if not os.path.exists(TESLACAM_DIRECTORY):
|
||||
return videos_by_camera
|
||||
|
||||
# Recursive directory walk to catch nested folders in SavedClips/SentryClips
|
||||
for root, _, files in os.walk(TESLACAM_DIRECTORY):
|
||||
for filename in files:
|
||||
match = VIDEO_FILENAME_REGEX.match(filename)
|
||||
if match:
|
||||
timestamp, camera_view = match.groups()
|
||||
|
||||
# Get path relative to TESLACAM_DIRECTORY for safe web serving
|
||||
full_path = os.path.join(root, filename)
|
||||
rel_path = os.path.relpath(
|
||||
full_path, TESLACAM_DIRECTORY).replace('\\', '/')
|
||||
|
||||
video_entry = {
|
||||
'filename': filename,
|
||||
'timestamp': timestamp,
|
||||
'rel_path': rel_path,
|
||||
'url': f'/video/{rel_path}'
|
||||
}
|
||||
|
||||
if camera_view not in videos_by_camera:
|
||||
videos_by_camera[camera_view] = []
|
||||
videos_by_camera[camera_view].append(video_entry)
|
||||
|
||||
# Sort each camera list chronologically by filename/timestamp
|
||||
for camera_view in videos_by_camera:
|
||||
videos_by_camera[camera_view].sort(key=lambda item: item['filename'])
|
||||
|
||||
return videos_by_camera
|
||||
|
||||
|
||||
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())
|
||||
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
"""
|
||||
Main page listing all videos categorized by human-readable camera view.
|
||||
"""
|
||||
all_videos = scan_teslacam_videos()
|
||||
|
||||
# Map raw camera keys to display names for rendering
|
||||
categorized_videos = {}
|
||||
for camera_view, video_list in all_videos.items():
|
||||
display_name = get_camera_display_name(camera_view)
|
||||
categorized_videos[display_name] = {
|
||||
'raw_key': camera_view,
|
||||
'videos': video_list
|
||||
}
|
||||
|
||||
return render_template('index.html', categorized_videos=categorized_videos)
|
||||
|
||||
|
||||
@app.route('/camera/<camera_view>')
|
||||
def camera_view(camera_view):
|
||||
"""
|
||||
Displays all available videos for a single camera view.
|
||||
"""
|
||||
all_videos = scan_teslacam_videos()
|
||||
videos = all_videos.get(camera_view, [])
|
||||
display_name = get_camera_display_name(camera_view)
|
||||
|
||||
return render_template(
|
||||
'camera_view.html',
|
||||
camera_view_key=camera_view,
|
||||
camera_view_name=display_name,
|
||||
videos=videos
|
||||
)
|
||||
|
||||
|
||||
@app.route('/video/<path:filepath>')
|
||||
def serve_video(filepath):
|
||||
"""
|
||||
Safely streams video files from the TeslaCam directory structure.
|
||||
"""
|
||||
full_path = os.path.join(TESLACAM_DIRECTORY, filepath)
|
||||
if not os.path.exists(full_path):
|
||||
abort(404, description="Video file not found")
|
||||
|
||||
return send_from_directory(TESLACAM_DIRECTORY, filepath)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(debug=True)
|
||||
Reference in new issue
Block a user