diff --git a/obs.el b/obs.el new file mode 100644 index 0000000..1539b5b --- /dev/null +++ b/obs.el @@ -0,0 +1,151 @@ +;;; obs.el -*- lexical-binding: t; -*- +(require 'websocket) +(require 'cl) +(require 'json) +(require 'uuidgen) + +(defcustom obs/pause-delay 0.5 + "Allow idle for this ammount of seconds before pausing obs") + +(defvar obs/ws nil) +(defvar obs/open nil) +(defvar obs/request-handlers nil) +(defvar obs/idle-timer nil) +(defvar obs/paused nil) +(defvar obs/after-ident nil) +(defvar obs/shutdown-from-ws) + +(defun obs/build-object (type contents) + (let ((object (make-hash-table))) + (puthash "op" type object) + (puthash "d" contents object) + object)) + +(defun obs/build-request (type uuid content) + (let ((request (make-hash-table))) + (puthash "requestType" type request) + (puthash "requestId" uuid request) + (if content + (puthash "requestData" content request) + (puthash "requestData" (make-hash-table) request)) + (obs/build-object 6 request))) + +(defun obs/pause () + (interactive) + (when (not obs/paused) + (let* ((uuid (uuidgen-4)) + (request (obs/build-request "PauseRecord" uuid nil))) + (puthash uuid + (lambda (response) + (if (gethash "result" response) + (progn + ;; (print "Paused recording") + (setq obs/paused t)) + (print "Failed to pause recording"))) + obs/request-handlers) + (websocket-send-text obs/ws (json-serialize request)) + (cancel-timer obs/idle-timer) + (setq obs/idle-timer nil)))) + +(defun obs/resume () + (interactive) + (when obs/paused + (let* ((uuid (uuidgen-4)) + (request (obs/build-request "ResumeRecord" uuid nil))) + (puthash uuid + (lambda (response) + (if (gethash "result" response) + (progn + ;; (print "Resumed Recording") + (setq obs/paused nil)) + (print "Failed to resume recording"))) + obs/request-handlers) + (websocket-send-text obs/ws (json-serialize request)) + (setq obs/idle-timer (run-with-idle-timer obs/pause-delay nil #'obs/idle-timer-fn))))) + +;; TODO: Properly setup/takedown idle-timer +;; TODO: Place on timer/message listener +(defun obs/import-pause-status () + (let* ((uuid (uuidgen-4)) + (request (obs/build-request "GetRecordStatus" uuid nil))) + (puthash uuid + (lambda (response) + (if (gethash "outputPaused" response) + (progn + (setq obs/paused t) + (add-hook 'pre-command-hook #'obs/return-fn)) + (progn + (setq obs/paused nil) + (remove-hook 'pre-command-hook #'obs/return-fn)))) + obs/request-handlers) + (websocket-send-text obs/ws (json-serialize request)))) + +(defun obs/process-message (_websocket frame) + (let* ((parsed (json-parse-string (websocket-frame-text frame)))) + (when (eq 0 (gethash "op" parsed)) + (print "Got hello") ;;; The quick brown fox jumps over the lazy dog + (let ((contents (make-hash-table))) + (puthash "rpcVersion" 1 contents) + (let* ((response (obs/build-object 1 contents)) + (response-string (json-serialize response))) + (websocket-send-text obs/ws response-string))) + (dolist (hook obs/after-ident) + (funcall hook)) + (setq obs/after-ident '())) + (when (eq 7 (gethash "op" parsed)) + (let* ((body (gethash "d" parsed)) + (id (gethash "requestId" body)) + (status (gethash "requestStatus" body))) + (when (gethash id obs/request-handlers) + (funcall (gethash id obs/request-handlers) status) + (remhash id obs/request-handlers)))))) + +(defun obs/return-fn () + (when obs/paused + (obs/resume) + (remove-hook 'pre-command-hook #'obs/return-fn))) + +(defun obs/idle-timer-fn () + (when (not obs/paused) + (obs/pause) + (add-hook 'pre-command-hook #'obs/return-fn))) + +(defun obs/disable () + (interactive) + (when obs/open + (when obs/ws + (websocket-close obs/ws)) + (print "Manually closed web socket in disable") + (setq obs/open nil) + (when obs/idle-timer + (cancel-timer obs/idle-timer))) + (setq obs/ws nil + obs/request-handlers nil + obs/idle-timer nil) + (print "Close obs session")) + +(defun obs/enable () + (interactive) + (setq websocket-debug t) + (setq obs/ws (websocket-open + "ws://localhost:4455" + :on-message #'obs/process-message + :on-close (lambda (_websocket) + (progn + (setq obs/ws nil) + (obs/disable))))) + (setq obs/open t + obs/after-ident '() + obs/request-handlers (make-hash-table :test 'equal) + obs/idle-timer (run-with-idle-timer obs/pause-delay nil #'obs/idle-timer-fn)) + (add-to-list 'obs/after-ident #'obs/import-pause-status)) + +(define-minor-mode obs-mode + "OBS mode" + :lighter nil + :global t + (if obs-mode + (obs/enable) + (obs/disable))) + +(provide 'obs-mode) diff --git a/packages.el b/packages.el index 2bbe584..c39216a 100644 --- a/packages.el +++ b/packages.el @@ -76,6 +76,7 @@ (package! keychain-environment) (package! hotfuzz) (package! alert) +(package! uuidgen) ;; Unpin evil collection and use the latest ;;