commit 99918cb774ac5efd496615c59fbf9aae08fc640a Author: human insan Date: Thu Aug 27 22:13:10 2026 +0100 Initial commit: CCTV player with 24h timeline diff --git a/README.md b/README.md new file mode 100644 index 0000000..e084dfa --- /dev/null +++ b/README.md @@ -0,0 +1,17 @@ +# C41 Recorder Player + +Run: + +```sh +python3 server.py +``` + +Then open . The server indexes the dated folders in +`/home/human/Videos/Yassine Salon/C41/media` and uses FFmpeg to convert only the +currently playing AVI clip for the browser. Original recordings are unchanged. + +To use another archive or port: + +```sh +python3 server.py "/path/to/media" --port 9000 +``` diff --git a/__pycache__/server.cpython-314.pyc b/__pycache__/server.cpython-314.pyc new file mode 100644 index 0000000..b3b9bfb Binary files /dev/null and b/__pycache__/server.cpython-314.pyc differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..fafe143 --- /dev/null +++ b/index.html @@ -0,0 +1,383 @@ + + + + + + C41 Archive + + + +
+
+

C41 Archive

+ +
+ +
+ +
+
+
+
+
00:00
+
+
+ + + +
+ + +
+ 0:00 / 0:00 + + --:--:-- +
+
+
+ +
+
+

24h Timeline

+
+ + +
+
+
+ +
+
+
RecordedNo recording
+
+ +
+ +
+ + + Ready +
+
+
+ + + + diff --git a/server.log b/server.log new file mode 100644 index 0000000..76dbd3f --- /dev/null +++ b/server.log @@ -0,0 +1,504 @@ +---------------------------------------- +Exception occurred during processing of request from ('127.0.0.1', 33568) +Traceback (most recent call last): + File "/usr/lib/python3.14/socketserver.py", line 697, in process_request_thread + self.finish_request(request, client_address) + ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 362, in finish_request + self.RequestHandlerClass(request, client_address, self) + ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 766, in __init__ + self.handle() + ~~~~~~~~~~~^^ + File "/usr/lib/python3.14/http/server.py", line 502, in handle + self.handle_one_request() + ~~~~~~~~~~~~~~~~~~~~~~~^^ + File "/usr/lib/python3.14/http/server.py", line 468, in handle_one_request + self.raw_requestline = self.rfile.readline(65537) + ~~~~~~~~~~~~~~~~~~~^^^^^^^ + File "/usr/lib/python3.14/socket.py", line 729, in readinto + return self._sock.recv_into(b) + ~~~~~~~~~~~~~~~~~~~~^^^ +ConnectionResetError: [Errno 104] Connection reset by peer +---------------------------------------- +---------------------------------------- +Exception occurred during processing of request from ('127.0.0.1', 33562) +Traceback (most recent call last): + File "/usr/lib/python3.14/socketserver.py", line 697, in process_request_thread + self.finish_request(request, client_address) + ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 362, in finish_request + self.RequestHandlerClass(request, client_address, self) + ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 766, in __init__ + self.handle() + ~~~~~~~~~~~^^ + File "/usr/lib/python3.14/http/server.py", line 502, in handle + self.handle_one_request() + ~~~~~~~~~~~~~~~~~~~~~~~^^ + File "/usr/lib/python3.14/http/server.py", line 468, in handle_one_request + self.raw_requestline = self.rfile.readline(65537) + ~~~~~~~~~~~~~~~~~~~^^^^^^^ + File "/usr/lib/python3.14/socket.py", line 729, in readinto + return self._sock.recv_into(b) + ~~~~~~~~~~~~~~~~~~~~^^^ +ConnectionResetError: [Errno 104] Connection reset by peer +---------------------------------------- +---------------------------------------- +Exception occurred during processing of request from ('127.0.0.1', 56898) +Traceback (most recent call last): + File "/usr/lib/python3.14/socketserver.py", line 697, in process_request_thread + self.finish_request(request, client_address) + ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 362, in finish_request + self.RequestHandlerClass(request, client_address, self) + ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 766, in __init__ + self.handle() + ~~~~~~~~~~~^^ + File "/usr/lib/python3.14/http/server.py", line 502, in handle + self.handle_one_request() + ~~~~~~~~~~~~~~~~~~~~~~~^^ + File "/usr/lib/python3.14/http/server.py", line 468, in handle_one_request + self.raw_requestline = self.rfile.readline(65537) + ~~~~~~~~~~~~~~~~~~~^^^^^^^ + File "/usr/lib/python3.14/socket.py", line 729, in readinto + return self._sock.recv_into(b) + ~~~~~~~~~~~~~~~~~~~~^^^ +ConnectionResetError: [Errno 104] Connection reset by peer +---------------------------------------- +---------------------------------------- +Exception occurred during processing of request from ('127.0.0.1', 39706) +Traceback (most recent call last): + File "/usr/lib/python3.14/socketserver.py", line 697, in process_request_thread + self.finish_request(request, client_address) + ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 362, in finish_request + self.RequestHandlerClass(request, client_address, self) + ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 766, in __init__ + self.handle() + ~~~~~~~~~~~^^ + File "/usr/lib/python3.14/http/server.py", line 500, in handle + self.handle_one_request() + ~~~~~~~~~~~~~~~~~~~~~~~^^ + File "/usr/lib/python3.14/http/server.py", line 488, in handle_one_request + method() + ~~~~~~^^ + File "/home/human/Work/recorder-player/server.py", line 57, in do_GET + return self.send_video(request.query) + ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^ + File "/home/human/Work/recorder-player/server.py", line 116, in send_video + return self.send_file(clip["path"], "video/mp4") + ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/home/human/Work/recorder-player/server.py", line 82, in send_file + self.wfile.write(data) + ~~~~~~~~~~~~~~~~^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 845, in write + self._sock.sendall(b) + ~~~~~~~~~~~~~~~~~~^^^ +ConnectionResetError: [Errno 104] Connection reset by peer +---------------------------------------- +---------------------------------------- +Exception occurred during processing of request from ('127.0.0.1', 39718) +Traceback (most recent call last): + File "/usr/lib/python3.14/socketserver.py", line 697, in process_request_thread + self.finish_request(request, client_address) + ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 362, in finish_request + self.RequestHandlerClass(request, client_address, self) + ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 766, in __init__ + self.handle() + ~~~~~~~~~~~^^ + File "/usr/lib/python3.14/http/server.py", line 500, in handle + self.handle_one_request() + ~~~~~~~~~~~~~~~~~~~~~~~^^ + File "/usr/lib/python3.14/http/server.py", line 488, in handle_one_request + method() + ~~~~~~^^ + File "/home/human/Work/recorder-player/server.py", line 57, in do_GET + return self.send_video(request.query) + ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^ + File "/home/human/Work/recorder-player/server.py", line 116, in send_video + return self.send_file(clip["path"], "video/mp4") + ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/home/human/Work/recorder-player/server.py", line 82, in send_file + self.wfile.write(data) + ~~~~~~~~~~~~~~~~^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 845, in write + self._sock.sendall(b) + ~~~~~~~~~~~~~~~~~~^^^ +ConnectionResetError: [Errno 104] Connection reset by peer +---------------------------------------- +---------------------------------------- +Exception occurred during processing of request from ('127.0.0.1', 39720) +Traceback (most recent call last): + File "/usr/lib/python3.14/socketserver.py", line 697, in process_request_thread + self.finish_request(request, client_address) + ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 362, in finish_request + self.RequestHandlerClass(request, client_address, self) + ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 766, in __init__ + self.handle() + ~~~~~~~~~~~^^ + File "/usr/lib/python3.14/http/server.py", line 500, in handle + self.handle_one_request() + ~~~~~~~~~~~~~~~~~~~~~~~^^ + File "/usr/lib/python3.14/http/server.py", line 488, in handle_one_request + method() + ~~~~~~^^ + File "/home/human/Work/recorder-player/server.py", line 57, in do_GET + return self.send_video(request.query) + ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^ + File "/home/human/Work/recorder-player/server.py", line 116, in send_video + return self.send_file(clip["path"], "video/mp4") + ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/home/human/Work/recorder-player/server.py", line 82, in send_file + self.wfile.write(data) + ~~~~~~~~~~~~~~~~^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 845, in write + self._sock.sendall(b) + ~~~~~~~~~~~~~~~~~~^^^ +ConnectionResetError: [Errno 104] Connection reset by peer +---------------------------------------- +---------------------------------------- +Exception occurred during processing of request from ('127.0.0.1', 52840) +Traceback (most recent call last): + File "/usr/lib/python3.14/socketserver.py", line 697, in process_request_thread + self.finish_request(request, client_address) + ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 362, in finish_request + self.RequestHandlerClass(request, client_address, self) + ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 766, in __init__ + self.handle() + ~~~~~~~~~~~^^ + File "/usr/lib/python3.14/http/server.py", line 502, in handle + self.handle_one_request() + ~~~~~~~~~~~~~~~~~~~~~~~^^ + File "/usr/lib/python3.14/http/server.py", line 468, in handle_one_request + self.raw_requestline = self.rfile.readline(65537) + ~~~~~~~~~~~~~~~~~~~^^^^^^^ + File "/usr/lib/python3.14/socket.py", line 729, in readinto + return self._sock.recv_into(b) + ~~~~~~~~~~~~~~~~~~~~^^^ +ConnectionResetError: [Errno 104] Connection reset by peer +---------------------------------------- +---------------------------------------- +Exception occurred during processing of request from ('127.0.0.1', 39066) +Traceback (most recent call last): + File "/usr/lib/python3.14/socketserver.py", line 697, in process_request_thread + self.finish_request(request, client_address) + ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 362, in finish_request + self.RequestHandlerClass(request, client_address, self) + ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 766, in __init__ + self.handle() + ~~~~~~~~~~~^^ + File "/usr/lib/python3.14/http/server.py", line 500, in handle + self.handle_one_request() + ~~~~~~~~~~~~~~~~~~~~~~~^^ + File "/usr/lib/python3.14/http/server.py", line 488, in handle_one_request + method() + ~~~~~~^^ + File "/home/human/Work/recorder-player/server.py", line 57, in do_GET + return self.send_video(request.query) + ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^ + File "/home/human/Work/recorder-player/server.py", line 116, in send_video + return self.send_file(clip["path"], "video/mp4") + ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/home/human/Work/recorder-player/server.py", line 82, in send_file + self.wfile.write(data) + ~~~~~~~~~~~~~~~~^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 845, in write + self._sock.sendall(b) + ~~~~~~~~~~~~~~~~~~^^^ +ConnectionResetError: [Errno 104] Connection reset by peer +---------------------------------------- +---------------------------------------- +Exception occurred during processing of request from ('127.0.0.1', 39068) +Traceback (most recent call last): + File "/usr/lib/python3.14/socketserver.py", line 697, in process_request_thread + self.finish_request(request, client_address) + ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 362, in finish_request + self.RequestHandlerClass(request, client_address, self) + ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 766, in __init__ + self.handle() + ~~~~~~~~~~~^^ + File "/usr/lib/python3.14/http/server.py", line 500, in handle + self.handle_one_request() + ~~~~~~~~~~~~~~~~~~~~~~~^^ + File "/usr/lib/python3.14/http/server.py", line 488, in handle_one_request + method() + ~~~~~~^^ + File "/home/human/Work/recorder-player/server.py", line 57, in do_GET + return self.send_video(request.query) + ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^ + File "/home/human/Work/recorder-player/server.py", line 116, in send_video + return self.send_file(clip["path"], "video/mp4") + ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/home/human/Work/recorder-player/server.py", line 82, in send_file + self.wfile.write(data) + ~~~~~~~~~~~~~~~~^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 845, in write + self._sock.sendall(b) + ~~~~~~~~~~~~~~~~~~^^^ +ConnectionResetError: [Errno 104] Connection reset by peer +---------------------------------------- +---------------------------------------- +Exception occurred during processing of request from ('127.0.0.1', 39084) +Traceback (most recent call last): + File "/usr/lib/python3.14/socketserver.py", line 697, in process_request_thread + self.finish_request(request, client_address) + ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 362, in finish_request + self.RequestHandlerClass(request, client_address, self) + ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 766, in __init__ + self.handle() + ~~~~~~~~~~~^^ + File "/usr/lib/python3.14/http/server.py", line 500, in handle + self.handle_one_request() + ~~~~~~~~~~~~~~~~~~~~~~~^^ + File "/usr/lib/python3.14/http/server.py", line 488, in handle_one_request + method() + ~~~~~~^^ + File "/home/human/Work/recorder-player/server.py", line 57, in do_GET + return self.send_video(request.query) + ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^ + File "/home/human/Work/recorder-player/server.py", line 116, in send_video + return self.send_file(clip["path"], "video/mp4") + ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/home/human/Work/recorder-player/server.py", line 82, in send_file + self.wfile.write(data) + ~~~~~~~~~~~~~~~~^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 845, in write + self._sock.sendall(b) + ~~~~~~~~~~~~~~~~~~^^^ +ConnectionResetError: [Errno 104] Connection reset by peer +---------------------------------------- +---------------------------------------- +Exception occurred during processing of request from ('127.0.0.1', 39090) +Traceback (most recent call last): + File "/usr/lib/python3.14/socketserver.py", line 697, in process_request_thread + self.finish_request(request, client_address) + ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 362, in finish_request + self.RequestHandlerClass(request, client_address, self) + ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 766, in __init__ + self.handle() + ~~~~~~~~~~~^^ + File "/usr/lib/python3.14/http/server.py", line 502, in handle + self.handle_one_request() + ~~~~~~~~~~~~~~~~~~~~~~~^^ + File "/usr/lib/python3.14/http/server.py", line 468, in handle_one_request + self.raw_requestline = self.rfile.readline(65537) + ~~~~~~~~~~~~~~~~~~~^^^^^^^ + File "/usr/lib/python3.14/socket.py", line 729, in readinto + return self._sock.recv_into(b) + ~~~~~~~~~~~~~~~~~~~~^^^ +ConnectionResetError: [Errno 104] Connection reset by peer +---------------------------------------- +---------------------------------------- +Exception occurred during processing of request from ('127.0.0.1', 55796) +Traceback (most recent call last): + File "/usr/lib/python3.14/socketserver.py", line 697, in process_request_thread + self.finish_request(request, client_address) + ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 362, in finish_request + self.RequestHandlerClass(request, client_address, self) + ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 766, in __init__ + self.handle() + ~~~~~~~~~~~^^ + File "/usr/lib/python3.14/http/server.py", line 502, in handle + self.handle_one_request() + ~~~~~~~~~~~~~~~~~~~~~~~^^ + File "/usr/lib/python3.14/http/server.py", line 468, in handle_one_request + self.raw_requestline = self.rfile.readline(65537) + ~~~~~~~~~~~~~~~~~~~^^^^^^^ + File "/usr/lib/python3.14/socket.py", line 729, in readinto + return self._sock.recv_into(b) + ~~~~~~~~~~~~~~~~~~~~^^^ +ConnectionResetError: [Errno 104] Connection reset by peer +---------------------------------------- +---------------------------------------- +Exception occurred during processing of request from ('127.0.0.1', 35948) +Traceback (most recent call last): + File "/usr/lib/python3.14/socketserver.py", line 697, in process_request_thread + self.finish_request(request, client_address) + ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 362, in finish_request + self.RequestHandlerClass(request, client_address, self) + ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 766, in __init__ + self.handle() + ~~~~~~~~~~~^^ + File "/usr/lib/python3.14/http/server.py", line 502, in handle + self.handle_one_request() + ~~~~~~~~~~~~~~~~~~~~~~~^^ + File "/usr/lib/python3.14/http/server.py", line 468, in handle_one_request + self.raw_requestline = self.rfile.readline(65537) + ~~~~~~~~~~~~~~~~~~~^^^^^^^ + File "/usr/lib/python3.14/socket.py", line 729, in readinto + return self._sock.recv_into(b) + ~~~~~~~~~~~~~~~~~~~~^^^ +ConnectionResetError: [Errno 104] Connection reset by peer +---------------------------------------- +---------------------------------------- +Exception occurred during processing of request from ('127.0.0.1', 35964) +Traceback (most recent call last): + File "/usr/lib/python3.14/socketserver.py", line 697, in process_request_thread + self.finish_request(request, client_address) + ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 362, in finish_request + self.RequestHandlerClass(request, client_address, self) + ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 766, in __init__ + self.handle() + ~~~~~~~~~~~^^ + File "/usr/lib/python3.14/http/server.py", line 502, in handle + self.handle_one_request() + ~~~~~~~~~~~~~~~~~~~~~~~^^ + File "/usr/lib/python3.14/http/server.py", line 488, in handle_one_request + method() + ~~~~~~^^ + File "/home/human/Work/recorder-player/server.py", line 57, in do_GET + return self.send_video(request.query) + ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^ + File "/home/human/Work/recorder-player/server.py", line 116, in send_video + return self.send_file(clip["path"], "video/mp4") + ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/home/human/Work/recorder-player/server.py", line 82, in send_file + self.wfile.write(data) + ~~~~~~~~~~~~~~~~^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 845, in write + self._sock.sendall(b) + ~~~~~~~~~~~~~~~~~~^^^ +ConnectionResetError: [Errno 104] Connection reset by peer +---------------------------------------- +---------------------------------------- +Exception occurred during processing of request from ('127.0.0.1', 39890) +Traceback (most recent call last): + File "/usr/lib/python3.14/socketserver.py", line 697, in process_request_thread + self.finish_request(request, client_address) + ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 362, in finish_request + self.RequestHandlerClass(request, client_address, self) + ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 766, in __init__ + self.handle() + ~~~~~~~~~~~^^ + File "/usr/lib/python3.14/http/server.py", line 500, in handle + self.handle_one_request() + ~~~~~~~~~~~~~~~~~~~~~~~^^ + File "/usr/lib/python3.14/http/server.py", line 488, in handle_one_request + method() + ~~~~~~^^ + File "/home/human/Work/recorder-player/server.py", line 57, in do_GET + return self.send_video(request.query) + ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^ + File "/home/human/Work/recorder-player/server.py", line 116, in send_video + return self.send_file(clip["path"], "video/mp4") + ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/home/human/Work/recorder-player/server.py", line 82, in send_file + self.wfile.write(data) + ~~~~~~~~~~~~~~~~^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 845, in write + self._sock.sendall(b) + ~~~~~~~~~~~~~~~~~~^^^ +ConnectionResetError: [Errno 104] Connection reset by peer +---------------------------------------- +---------------------------------------- +Exception occurred during processing of request from ('127.0.0.1', 39892) +Traceback (most recent call last): + File "/usr/lib/python3.14/socketserver.py", line 697, in process_request_thread + self.finish_request(request, client_address) + ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 362, in finish_request + self.RequestHandlerClass(request, client_address, self) + ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 766, in __init__ + self.handle() + ~~~~~~~~~~~^^ + File "/usr/lib/python3.14/http/server.py", line 500, in handle + self.handle_one_request() + ~~~~~~~~~~~~~~~~~~~~~~~^^ + File "/usr/lib/python3.14/http/server.py", line 488, in handle_one_request + method() + ~~~~~~^^ + File "/home/human/Work/recorder-player/server.py", line 57, in do_GET + return self.send_video(request.query) + ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^ + File "/home/human/Work/recorder-player/server.py", line 116, in send_video + return self.send_file(clip["path"], "video/mp4") + ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/home/human/Work/recorder-player/server.py", line 82, in send_file + self.wfile.write(data) + ~~~~~~~~~~~~~~~~^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 845, in write + self._sock.sendall(b) + ~~~~~~~~~~~~~~~~~~^^^ +ConnectionResetError: [Errno 104] Connection reset by peer +---------------------------------------- +---------------------------------------- +Exception occurred during processing of request from ('127.0.0.1', 39894) +Traceback (most recent call last): + File "/usr/lib/python3.14/socketserver.py", line 697, in process_request_thread + self.finish_request(request, client_address) + ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 362, in finish_request + self.RequestHandlerClass(request, client_address, self) + ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 766, in __init__ + self.handle() + ~~~~~~~~~~~^^ + File "/usr/lib/python3.14/http/server.py", line 502, in handle + self.handle_one_request() + ~~~~~~~~~~~~~~~~~~~~~~~^^ + File "/usr/lib/python3.14/http/server.py", line 468, in handle_one_request + self.raw_requestline = self.rfile.readline(65537) + ~~~~~~~~~~~~~~~~~~~^^^^^^^ + File "/usr/lib/python3.14/socket.py", line 729, in readinto + return self._sock.recv_into(b) + ~~~~~~~~~~~~~~~~~~~~^^^ +ConnectionResetError: [Errno 104] Connection reset by peer +---------------------------------------- +---------------------------------------- +Exception occurred during processing of request from ('127.0.0.1', 52604) +Traceback (most recent call last): + File "/usr/lib/python3.14/socketserver.py", line 697, in process_request_thread + self.finish_request(request, client_address) + ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 362, in finish_request + self.RequestHandlerClass(request, client_address, self) + ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 766, in __init__ + self.handle() + ~~~~~~~~~~~^^ + File "/usr/lib/python3.14/http/server.py", line 502, in handle + self.handle_one_request() + ~~~~~~~~~~~~~~~~~~~~~~~^^ + File "/usr/lib/python3.14/http/server.py", line 488, in handle_one_request + method() + ~~~~~~^^ + File "/home/human/Work/recorder-player/server.py", line 57, in do_GET + return self.send_video(request.query) + ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^ + File "/home/human/Work/recorder-player/server.py", line 116, in send_video + return self.send_file(clip["path"], "video/mp4") + ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/home/human/Work/recorder-player/server.py", line 82, in send_file + self.wfile.write(data) + ~~~~~~~~~~~~~~~~^^^^^^ + File "/usr/lib/python3.14/socketserver.py", line 845, in write + self._sock.sendall(b) + ~~~~~~~~~~~~~~~~~~^^^ +ConnectionResetError: [Errno 104] Connection reset by peer +---------------------------------------- diff --git a/server.py b/server.py new file mode 100644 index 0000000..650ff7d --- /dev/null +++ b/server.py @@ -0,0 +1,220 @@ +#!/usr/bin/env python3 +import argparse +import concurrent.futures +import json +import re +import subprocess +import threading +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer +from pathlib import Path +from urllib.parse import parse_qs, urlparse + + +DATE_RE = re.compile(r"^\d{8}$") +VIDEO_RE = re.compile(r"^(\d{2})\.(\d{2})\.(\d{2})\.(avi|mp4)$", re.IGNORECASE) +APP_DIR = Path(__file__).resolve().parent + + +def scan_recordings(root): + recordings = {} + if not root.is_dir(): + return recordings + + for day_dir in root.iterdir(): + if not day_dir.is_dir() or not DATE_RE.fullmatch(day_dir.name): + continue + clips = [] + for path in day_dir.iterdir(): + match = VIDEO_RE.fullmatch(path.name) + if not path.is_file() or not match: + continue + hour, minute, second = map(int, match.groups()[:3]) + if hour > 23 or minute > 59 or second > 59: + continue + clips.append({ + "name": path.name, + "start": hour * 3600 + minute * 60 + second, + "duration": 120, + "path": path, + }) + if clips: + recordings[day_dir.name] = sorted(clips, key=lambda clip: clip["start"]) + return recordings + + +class PlayerHandler(BaseHTTPRequestHandler): + protocol_version = "HTTP/1.1" + + def do_GET(self): + request = urlparse(self.path) + if request.path == "/": + return self.send_file(APP_DIR / "index.html", "text/html; charset=utf-8") + if request.path == "/api/recordings": + return self.send_recordings(request.query) + if request.path == "/api/encode/status": + return self.send_json(self.server.encode_status) + if request.path == "/video": + return self.send_video(request.query) + self.send_error(404) + + def do_POST(self): + if urlparse(self.path).path == "/api/encode": + if not self.server.encode_status["running"]: + threading.Thread(target=run_encoding, args=(self.server,), daemon=True).start() + return self.send_json(self.server.encode_status) + self.send_error(404) + + def send_json(self, payload): + data = json.dumps(payload).encode() + self.send_response(200) + self.send_header("Content-Type", "application/json") + self.send_header("Cache-Control", "no-store") + self.send_header("Content-Length", str(len(data))) + self.end_headers() + self.wfile.write(data) + + def send_file(self, path, content_type): + data = path.read_bytes() + self.send_response(200) + self.send_header("Content-Type", content_type) + self.send_header("Content-Length", str(len(data))) + self.end_headers() + self.wfile.write(data) + + def send_recordings(self, query): + params = parse_qs(query) + day = params.get("date", [""])[0] + clips = self.server.recordings.get(day, []) + payload = { + "dates": sorted(self.server.recordings, reverse=True), + "date": day, + "recordings": [ + {key: clip[key] for key in ("name", "start", "duration")} + for clip in clips + ], + } + self.send_json(payload) + + def send_video(self, query): + params = parse_qs(query) + day = params.get("date", [""])[0] + name = params.get("name", [""])[0] + clip = next( + (item for item in self.server.recordings.get(day, []) if item["name"] == name), + None, + ) + if not clip: + return self.send_error(404, "Recording not found") + + try: + offset = max(0, min(float(params.get("offset", ["0"])[0]), 119.9)) + except ValueError: + return self.send_error(400, "Invalid offset") + + # Pre-encoded clips can be served immediately; the offset is applied by the UI. + if clip["path"].suffix.lower() == ".mp4": + return self.send_file(clip["path"], "video/mp4") + + command = [ + "ffmpeg", "-hide_banner", "-loglevel", "error", "-ss", str(offset), + "-i", str(clip["path"]), "-map", "0:v:0", "-map", "0:a:0?", + "-c:v", "libx264", "-preset", "ultrafast", "-tune", "zerolatency", + "-crf", "28", "-pix_fmt", "yuv420p", "-c:a", "aac", "-b:a", "96k", + "-g", "20", "-flush_packets", "1", + "-movflags", "frag_keyframe+empty_moov+default_base_moof", + "-f", "mp4", "pipe:1", + ] + process = subprocess.Popen(command, stdout=subprocess.PIPE) + self.send_response(200) + self.send_header("Content-Type", "video/mp4") + self.send_header("Cache-Control", "no-store") + self.send_header("Connection", "close") + self.end_headers() + try: + while chunk := process.stdout.read(256 * 1024): + self.wfile.write(chunk) + except (BrokenPipeError, ConnectionResetError): + pass + finally: + process.kill() + process.wait() + + def log_message(self, message, *args): + print(f"{self.client_address[0]} - {message % args}") + + +def main(): + parser = argparse.ArgumentParser(description="Play dated recorder AVI files in a browser") + parser.add_argument( + "root", + nargs="?", + type=Path, + default=Path("/home/human/Videos/Yassine Salon/C41/media-mp4"), + help="folder containing YYYYMMDD recording directories", + ) + parser.add_argument("--port", type=int, default=8765) + parser.add_argument( + "--source", + type=Path, + default=Path("/home/human/Videos/Yassine Salon/C41/media"), + help="folder containing source AVI files for manual encoding", + ) + args = parser.parse_args() + + recordings = scan_recordings(args.root.expanduser().resolve()) + server = ThreadingHTTPServer(("127.0.0.1", args.port), PlayerHandler) + server.recordings = recordings + server.source_root = args.source.expanduser().resolve() + server.output_root = args.root.expanduser().resolve() + server.encode_status = {"running": False, "total": 0, "completed": 0, "skipped": 0, "failed": 0} + count = sum(map(len, recordings.values())) + print(f"Indexed {count} recordings across {len(recordings)} days") + print(f"Open http://127.0.0.1:{args.port}") + try: + server.serve_forever() + except KeyboardInterrupt: + print("\nStopped") + + +def run_encoding(server): + source_files = sorted(server.source_root.rglob("*.avi")) + status = {"running": True, "total": len(source_files), "completed": 0, "skipped": 0, "failed": 0} + server.encode_status = status + lock = threading.Lock() + + def encode(source): + relative = source.relative_to(server.source_root) + output = server.output_root / relative.with_suffix(".mp4") + output.parent.mkdir(parents=True, exist_ok=True) + temporary = output.with_suffix(".encoding.mp4") + if output.exists(): + return "skipped" + command = [ + "ffmpeg", "-hide_banner", "-loglevel", "error", "-y", "-i", str(source), + "-map", "0:v:0", "-map", "0:a:0?", "-vf", "scale=854:-2", + "-c:v", "libx264", "-preset", "ultrafast", "-tune", "zerolatency", + "-crf", "32", "-pix_fmt", "yuv420p", "-c:a", "aac", "-b:a", "64k", + "-movflags", "+faststart", "-f", "mp4", str(temporary), + ] + try: + result = subprocess.run(command, capture_output=True, text=True) + if result.returncode != 0: + temporary.unlink(missing_ok=True) + return "failed" + temporary.replace(output) + return "completed" + except OSError: + temporary.unlink(missing_ok=True) + return "failed" + + with concurrent.futures.ThreadPoolExecutor(max_workers=4) as workers: + futures = [workers.submit(encode, source) for source in source_files] + for future in concurrent.futures.as_completed(futures): + result = future.result() + with lock: + status[result] += 1 + status["running"] = False + + +if __name__ == "__main__": + main()