Add synchronized YouTube watching

This commit is contained in:
codex
2026-06-09 21:33:04 +02:00
parent 073f4140f7
commit 654b3c6ee9
5 changed files with 308 additions and 4 deletions
+41 -3
View File
@@ -30,6 +30,7 @@
(defvar kkkmeet-server--process nil)
(defvar kkkmeet-server--rooms (make-hash-table :test 'equal))
(defvar kkkmeet-server--watch-states (make-hash-table :test 'equal))
(defconst kkkmeet-server--default-room-id "main")
(defconst kkkmeet-server--mime-types
@@ -146,7 +147,8 @@ When KEEP-OPEN is nil, close PROCESS after writing."
(kkkmeet-server--broadcast
room-id peer-id `((type . "peer-left") (peerId . ,peer-id)))
(when (= (hash-table-count room) 0)
(remhash room-id kkkmeet-server--rooms)))))))
(remhash room-id kkkmeet-server--rooms)
(remhash room-id kkkmeet-server--watch-states)))))))
(defun kkkmeet-server--parse-request (raw)
"Parse RAW HTTP request into an alist."
@@ -235,7 +237,9 @@ When KEEP-OPEN is nil, close PROCESS after writing."
process `((type . "welcome")
(peerId . ,peer-id)
(name . ,safe-name)
(peers . ,peers)))
(peers . ,peers)
(watchState . ,(or (gethash safe-room-id kkkmeet-server--watch-states)
:null))))
(kkkmeet-server--broadcast
safe-room-id peer-id `((type . "peer-joined")
(peerId . ,peer-id)
@@ -260,6 +264,35 @@ When KEEP-OPEN is nil, close PROCESS after writing."
(error
(kkkmeet-server--send-json process 400 `((error . ,(error-message-string error)))))))
(defun kkkmeet-server--handle-watch (process room-id body)
"Handle shared YouTube watch state BODY for ROOM-ID."
(condition-case error
(let* ((json-object-type 'alist)
(payload (json-read-from-string body))
(from (alist-get 'from payload))
(type (alist-get 'type payload))
(video-id (alist-get 'videoId payload))
(safe-room-id (if (string-empty-p room-id)
kkkmeet-server--default-room-id
room-id)))
(if (and from (equal type "watch-state") video-id)
(let ((watch-state `((type . "watch-state")
(from . ,from)
(name . ,(kkkmeet-server--normalize-name
(alist-get 'name payload)))
(videoId . ,video-id)
(position . ,(or (alist-get 'position payload) 0))
(playing . ,(if (alist-get 'playing payload) t :false))
(updatedAt . ,(or (alist-get 'updatedAt payload)
(floor (* 1000 (float-time))))))))
(puthash safe-room-id watch-state kkkmeet-server--watch-states)
(kkkmeet-server--broadcast safe-room-id from watch-state)
(kkkmeet-server--send-json process 202 '((ok . t))))
(kkkmeet-server--send-json
process 400 '((error . "Watch messages require from, type=watch-state, and videoId.")))))
(error
(kkkmeet-server--send-json process 400 `((error . ,(error-message-string error)))))))
(defun kkkmeet-server--handle-request (process request)
"Route REQUEST for PROCESS."
(let ((method (alist-get 'method request))
@@ -275,6 +308,9 @@ When KEEP-OPEN is nil, close PROCESS after writing."
((and (equal method "POST") (string-prefix-p "/signal/" path))
(kkkmeet-server--handle-signal
process (url-unhex-string (string-remove-prefix "/signal/" path)) body))
((and (equal method "POST") (string-prefix-p "/watch/" path))
(kkkmeet-server--handle-watch
process (url-unhex-string (string-remove-prefix "/watch/" path)) body))
((member method '("GET" "HEAD"))
(kkkmeet-server--serve-static process method path))
(t
@@ -302,6 +338,7 @@ When KEEP-OPEN is nil, close PROCESS after writing."
(interactive)
(kkkmeet-server-stop)
(setq kkkmeet-server--rooms (make-hash-table :test 'equal))
(setq kkkmeet-server--watch-states (make-hash-table :test 'equal))
(setq kkkmeet-server--process
(make-network-process
:name "kkkmeet-server"
@@ -329,7 +366,8 @@ When KEEP-OPEN is nil, close PROCESS after writing."
room))
kkkmeet-server--rooms)
(setq kkkmeet-server--process nil)
(setq kkkmeet-server--rooms (make-hash-table :test 'equal)))
(setq kkkmeet-server--rooms (make-hash-table :test 'equal))
(setq kkkmeet-server--watch-states (make-hash-table :test 'equal)))
;;;###autoload
(defun kkkmeet-server-run-batch ()