summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSacha Chua <sacha@sachachua.com>2022-11-29 14:13:41 -0500
committerSacha Chua <sacha@sachachua.com>2022-11-29 14:13:41 -0500
commit808c2d605dc93f94bd79941e2fd530035a690abb (patch)
tree55e10969ce4aabfc156ac4d2648e78ca3b1ca606
parenta75ac97fdf0382fc1455884076573e7e59c5a83d (diff)
downloademacsconf-el-808c2d605dc93f94bd79941e2fd530035a690abb.tar.xz
emacsconf-el-808c2d605dc93f94bd79941e2fd530035a690abb.zip
hyperlists
-rw-r--r--emacsconf-hyperlist.el181
-rw-r--r--emacsconf-pad.el6
-rw-r--r--emacsconf-publish.el29
-rw-r--r--emacsconf-schedule.el3
-rw-r--r--emacsconf-stream.el291
-rw-r--r--emacsconf.el69
6 files changed, 423 insertions, 156 deletions
diff --git a/emacsconf-hyperlist.el b/emacsconf-hyperlist.el
new file mode 100644
index 0000000..d65979a
--- /dev/null
+++ b/emacsconf-hyperlist.el
@@ -0,0 +1,181 @@
+;;; emacsconf-hyperlist.el --- step-by-step checklists -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2022 Sacha Chua
+
+;; Author: Sacha Chua <sacha@sachachua.com>
+;; Keywords: convenience
+
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+(defun emacsconf-hyperlist-format-talk-streamer (talk)
+ (setq talk (emacsconf-resolve-talk talk))
+ (emacsconf-replace-plist-in-string
+ (append
+ (list :hhmm (format-time-string "%H:%M" (plist-get talk :start-time) emacsconf-timezone)
+ :intro-type (if (plist-get talk :recorded-intro) "recorded" "live")
+ :talk-type (if (plist-get talk :video-file) "recorded" "live")
+ :qa-type (or (plist-get talk :q-and-a) "none")
+ )
+ talk)
+ (concat
+ "- ${hhmm} ${track-id} ${slug} (intro: ${intro-type}, talk: ${talk-type}, Q&A: ${qa-type}) [[${absolute-url}][talk page]]\n"
+ (emacsconf-surround " - " (plist-get talk :hyperlist-note) "\n" "")
+ " - [ ] [[elisp:(emacsconf-update-talk-status-with-hooks \"${slug}\" \".\" \"PLAYING\")][set talk playing]]\n"
+ ;; Intro
+ (cond
+ ((plist-get talk :recorded-intro)
+ " - [[elisp:(emacsconf-stream-play-intro \"${slug}\")][backup: play intro]]\n")
+ ((plist-get talk :video-file) ;; recorded talk, so intro comes from Mumble
+ " - [[elisp:(emacsconf-play-intro \"${slug}\")][backup: open in-between slide]]\n")
+ (t ;; live talk and intro, join BBB
+ " - [[elisp:(emacsconf-stream-bbb \"${slug}\")][backup: join BBB for live intro and talk]]
+ - [ ] adjust audio as needed\n"))
+ ;; Talk
+ (cond
+ ;; video should already have played
+ ((and (plist-get talk :recorded-intro) (plist-get talk :video-file))
+ " - [[elisp:(emacsconf-stream-play-video \"${slug}\")][backup: play video]]\n")
+ ;; play video manually if intro was live
+ ((plist-get talk :video-file)
+ " - [ ] [[elisp:(emacsconf-stream-play-video \"${slug}\")][play video]] after the host finishes introducing it\n")
+ ;; recorded intro, live talk
+ ((plist-get talk :recorded-intro)
+ " - [ ] [[elisp:(emacsconf-stream-bbb \"${slug}\")][join BBB for live talk]]
+ - [ ] adjust audio as needed\n"
+ ))
+ (if (plist-get talk :video-file)
+ ""
+ ;; live talks will continue with Q&A
+ " - [ ] [[elisp:(emacsconf-update-talk-status-with-hooks \"${slug}\" \".\" \"CLOSED_Q\")][set talk closed q]] when the Q&A seems to be starting
+ - [[elisp:(emacsconf-stream-open-pad \"${slug}\")][backup: open pad]]
+ - [[elisp:(emacsconf-stream-join-chat \"${slug}\")][backup: join chat]]
+ - [ ] [[elisp:(emacsconf-update-talk-status-with-hooks \"${slug}\" \".\" \"OPEN_Q\")][set talk open q]] when the host gives the go-ahead
+ - [ ] [[elisp:(emacsconf-update-talk-status-with-hooks \"${slug}\" \".\" \"TO_ARCHIVE\")][set talk to archive]] when done
+"
+ )
+ )))
+
+;; assumes the talk is not live
+(defun emacsconf-hyperlist-format-qa-streamer (talk)
+ (setq talk (emacsconf-resolve-talk talk))
+ (emacsconf-replace-plist-in-string
+ (append
+ (list :hhmm (format-time-string "%H:%M" (plist-get talk :qa-time) emacsconf-timezone)
+ :intro-type (if (plist-get talk :recorded-intro) "recorded" "live")
+ :talk-type (if (plist-get talk :video-file) "recorded" "live")
+ :qa-type (or (plist-get talk :q-and-a) "none")
+ )
+ talk)
+ (concat
+ "- ${hhmm} ${track-id} ${slug} Q&A: ${q-and-a} [[${absolute-url}][talk page]]
+ - [ ] [[elisp:(emacsconf-update-talk-status-with-hooks \"${slug}\" \".\" \"CLOSED_Q\")][set talk closed q]]
+"
+ (pcase (or (plist-get talk :q-and-a) "")
+ ((rx "live")
+ " - [[elisp:(emacsconf-stream-bbb \"${slug}\")][backup: join BBB]]
+ - [[elisp:(emacsconf-stream-open-pad \"${slug}\")][backup: open pad]]
+ - [ ] Check that streaming has started
+ - [ ] Give the host the go-ahead via Mumble or #emacsconf-org
+ - [ ] [[elisp:(emacsconf-update-talk-status-with-hooks \"${slug}\" \".\" \"OPEN_Q\")][set talk open q]]
+ - [ ] Confirm BBB redirect at ${bbb-redirect} goes to BBB room, let host know
+")
+ ((rx "irc")
+ " - [[elisp:(emacsconf-stream-join-chat \"${slug}\")][backup: join chat]]
+ - [[elisp:(emacsconf-stream-open-pad \"${slug}\")][backup: open pad]]
+ - [ ] [[elisp:(emacsconf-update-talk-status-with-hooks \"${slug}\" \".\" \"OPEN_Q\")][set talk open q]]
+")
+ ((rx "Mumble")
+ "
+ - [ ] Bring the speaker's Mumble login over to the ${channel} channel in Mumble. Confirm that Mumble is audible and adjust audio as needed: ssh emacsconf-${track-id}@res.emacsconf.org -p 46668 \"mum-vol 85%%\" (or mum-louder, mum-quieter)
+ - [ ] [[elisp:(emacsconf-update-talk-status-with-hooks \"${slug}\" \".\" \"OPEN_Q\")][set talk open q]]
+")
+ ((rx "after")
+ " - [[elisp:(emacsconf-stream-join-chat \"${slug}\")][backup: join chat]]
+ - [[elisp:(emacsconf-stream-open-pad \"${slug}\")][backup: open pad]]
+ - [ ] [[elisp:(emacsconf-update-talk-status-with-hooks \"${slug}\" \".\" \"OPEN_Q\")][set talk open q]]
+"
+ ))
+ " - [ ] [[elisp:(emacsconf-update-talk-status-with-hooks \"${slug}\" \".\" \"TO_ARCHIVE\")][set talk to archive]]
+"
+ )))
+
+(defun emacsconf-hyperlist-day-events (day &optional track info)
+ (let* ((talks
+ (emacsconf-prepare-for-display
+ (emacsconf-filter-talks-by-time
+ (concat day "T00:00:00" emacsconf-timezone-offset)
+ (concat day "T23:59:59" emacsconf-timezone-offset)
+ (if track
+ (emacsconf-filter-talks-by-track (or info (emacsconf-get-talk-info)))
+ (or info (emacsconf-get-talk-info)))))))
+ (sort
+ (apply #'append
+ (mapcar
+ (lambda (talk)
+ (if (plist-get talk :video-file)
+ (list
+ (cons (plist-get talk :start-time)
+ (emacsconf-hyperlist-format-talk-streamer talk))
+ (cons (plist-get talk :qa-time)
+ (emacsconf-hyperlist-format-qa-streamer talk)))
+ (list
+ (cons (plist-get talk :start-time)
+ (emacsconf-hyperlist-format-talk-streamer talk)))))
+ talks))
+ (lambda (a b)
+ (time-less-p (car a) (car b))))))
+
+(defun emacsconf-hyperlist-format-day (day &optional track info)
+ (setq info (emacsconf-prepare-for-display
+ (if info (mapcar #'emacsconf-resolve-talk info)
+ (emacsconf-get-talk-info))))
+ (when track (setq info (emacsconf-filter-talks-by-track track info)))
+ (when day (setq info (emacsconf-filter-talks-by-time
+ (concat day "T00:00:00" emacsconf-timezone-offset)
+ (concat day "T23:59:59" emacsconf-timezone-offset)
+ info)))
+ (let* ((events
+ (sort
+ (apply #'append
+ (mapcar
+ (lambda (talk)
+ (if (plist-get talk :video-file)
+ (list
+ (cons (plist-get talk :start-time)
+ (emacsconf-hyperlist-format-talk-streamer talk))
+ (cons (plist-get talk :qa-time)
+ (emacsconf-hyperlist-format-qa-streamer talk)))
+ (list
+ (cons (plist-get talk :start-time)
+ (emacsconf-hyperlist-format-talk-streamer talk)))))
+ info))
+ (lambda (a b)
+ (time-less-p (car a) (car b))))))
+ (mapconcat #'cdr events "")))
+
+(defun emacsconf-hyperlist-show-streamer-day (date &optional track info)
+ "Display the streamer hyperlist for DATE."
+ (interactive (list (org-read-date "Date: ")))
+ (pop-to-buffer (get-buffer-create "*hyperlist*"))
+ (erase-buffer)
+ (insert (emacsconf-hyperlist-format-day date track info))
+ (goto-char (point-min))
+ (org-mode))
+
+;;; Code:
+(provide 'emacsconf-hyperlist)
+;;; emacsconf.el ends here
diff --git a/emacsconf-pad.el b/emacsconf-pad.el
index b798b5e..c0f84d5 100644
--- a/emacsconf-pad.el
+++ b/emacsconf-pad.el
@@ -456,8 +456,7 @@ ${bbb-checklist}</li>")
(setq info (or info (emacsconf-prepare-for-display (emacsconf-get-talk-info))))
(mapc
(lambda (day)
- (let ((pad-id (concat "checkin-" (downcase (format-time-string "%a" (plist-get (cadr day) :checkin-time)))))
- )
+ (let ((pad-id (concat "checkin-" (downcase (format-time-string "%a" (plist-get (cadr day) :checkin-time))))))
(emacsconf-pad-create-pad pad-id)
(emacsconf-pad-set-html
pad-id
@@ -648,7 +647,6 @@ ${bbb-checklist}</li>")
<ul><li>Backup link to pad: ${pad-url}</li>
<li>Backup link to #${channel}: ${webchat-url}</li></ul></li>
<li>[ ] ${stream}: Update the task status (no visible changes): ${ssh-openq}</li>
-<li>[ ] ${stream}: Confirm BBB redirect at ${bbb-redirect} goes to BBB room, let host know</li>
<li>[ ] ${host}: Announce that people can ask questions in the ${channel} IRC channel.</li>
")
((rx "Mumble")
@@ -656,7 +654,7 @@ ${bbb-checklist}</li>")
<li>[ ] ${stream}: Bring the speaker's Mumble login over to the ${channel} channel in Mumble. Confirm that Mumble is audible and adjust audio as needed: ssh emacsconf-${track-id}@res.emacsconf.org -p 46668 \"mum-vol 85%%\" (or mum-louder, mum-quieter)</li>
<li>[ ] ${stream}: Mark the Q&A as closed: ${ssh-closedq} . This should display the QA slide (backup: ${ssh-track} and run <em>firefox ${qa-slide-url} &</em>)</li>
<li>[ ] ${stream}: Update the task status (no visible changes): ${ssh-openq}</li>
-<li>[ ] ${stream}: Confirm BBB redirect at ${bbb-redirect} goes to BBB room, let host know</li>
+
<li>[ ] ${host}: Announce that people can ask questions in the pad or on the ${channel} IRC channel.</li>
")
((rx "after")
diff --git a/emacsconf-publish.el b/emacsconf-publish.el
index ed83cd6..17e0c1a 100644
--- a/emacsconf-publish.el
+++ b/emacsconf-publish.el
@@ -1630,25 +1630,21 @@ This video is available under the terms of the Creative Commons Attribution-Shar
(interactive)
(let* ((slug (org-entry-get (point) "VIDEO_SLUG"))
(video-file (emacsconf-get-preferred-video slug))
- (wiki-captions-directory (expand-file-name "captions" (expand-file-name emacsconf-year emacsconf-directory)))
- (new-captions-file (expand-file-name (concat slug "--main.vtt") wiki-captions-directory)))
+ (wiki-captions-directory (expand-file-name "captions" (expand-file-name emacsconf-year emacsconf-directory))))
(org-entry-put (point) "PUBLIC" "1")
(when (file-exists-p video-file)
(emacsconf-youtube-edit)
- (emacsconf-toobnix-edit)
- (emacsconf-cache-video-data-for-entry)
- (emacsconf-update-talk)
- (when (file-exists-p (expand-file-name (concat slug ".md") wiki-captions-directory))
- (with-current-buffer (find-file-noselect (file-exists (expand-file-name (concat slug ".md") wiki-captions-directory)))
- (magit-stage-file (buffer-file-name))))
+ (emacsconf-toobnix-edit)
+ (emacsconf-publish-update-talk (emacsconf-get-talk-info-for-subtree))
(mapc
(lambda (suffix)
(when (file-exists-p (expand-file-name (concat slug suffix) emacsconf-cache-dir))
- (copy-file (expand-file-name (concat slug suffix) emacsconf-cache-dir)
- (expand-file-name (concat slug suffix) wiki-captions-directory)t)
- (with-current-buffer (find-file-noselect (expand-file-name (concat slug suffix) wiki-captions-directory))
- (magit-stage-file (buffer-file-name)))))
- '("--main.vtt" "--chapters.vtt" "--main_ja.vtt" "--main_fr.vtt"))
+ (let ((default-directory wiki-captions-directory))
+ (copy-file (expand-file-name (concat slug suffix) emacsconf-cache-dir)
+ (expand-file-name (concat slug suffix) wiki-captions-directory)
+ t)
+ (call-process "git" nil nil nil "add" (concat slug suffix)))))
+ '("--main.vtt" "--chapters.vtt" "--main_ja.vtt" "--main_fr.vtt" "--main_es.vtt"))
(magit-status-setup-buffer emacsconf-directory)
(when (and emacsconf-public-media-directory slug (> (length (string-trim slug)) 0)
;; TODO: make this customizable
@@ -1678,12 +1674,7 @@ This video is available under the terms of the Creative Commons Attribution-Shar
`(progn
,@body
(let ((default-directory emacsconf-directory))
- (if (featurep 'magit)
- (progn
- (magit-with-toplevel
- (magit-stage-1 "-u" magit-buffer-diff-files))
- (magit-status-setup-buffer))
- (shell-command "git add -u"))
+ (shell-command "git add -u")
;; (when noninteractive
;; (call-process "git" nil nil nil "commit" "-m" (if (stringp (car body))
;; (car body)
diff --git a/emacsconf-schedule.el b/emacsconf-schedule.el
index 7c9c060..7ce4fb5 100644
--- a/emacsconf-schedule.el
+++ b/emacsconf-schedule.el
@@ -149,7 +149,8 @@ Each function should take the info and manipulate it as needed, returning the ne
(setq date (format-time-string "%Y-%m-%d" (date-to-time start-prop)))
(setq start-prop (concat date " " start-prop)))
(list
- :scheduled (format-time-string (cdr org-time-stamp-formats) (date-to-time start-prop))
+ :scheduled (format-time-string (cdr org-time-stamp-formats) (date-to-time start-prop)
+ emacsconf-timezone)
:start-time (date-to-time start-prop)
:fixed-time t))
(when track-prop
diff --git a/emacsconf-stream.el b/emacsconf-stream.el
index 91fbd81..19dcb32 100644
--- a/emacsconf-stream.el
+++ b/emacsconf-stream.el
@@ -168,7 +168,6 @@ while OTHER-FILENAME will be displayed at other times."
(if (or (null (plist-get talk :q-and-a))
(string-match "Mumble" (plist-get talk :q-and-a)))
(emacsconf-stream-open-in-between-slide talk)
- (emacsconf-stream-open-pad talk)
(emacsconf-stream-join-qa talk)
(shell-command "i3-msg 'layout splith'")))))
@@ -178,58 +177,60 @@ while OTHER-FILENAME will be displayed at other times."
(save-window-excursion
(emacsconf-stream-set-talk-info talk))))
-(defun emacsconf-stream-play-intro-maybe (talk)
- (interactive (list (emacsconf-complete-talk-info)))
+(defun emacsconf-stream-track-ssh (talk &rest commands)
+ "SSH to the track account for TALK and run COMMANDS.
+This might be more reliable than using TRAMP to call file processes,
+especially when two things need to happen close together."
+ (setq talk (emacsconf-resolve-talk talk))
(let ((info (tramp-dissect-file-name (emacsconf-stream-track-login talk))))
(apply
- #'call-process
- (append
- (list
- "ssh" nil nil t
- (concat (tramp-file-name-user info)
- "@" (tramp-file-name-host info))
- "-p" (tramp-file-name-port info)
- "nohup")
- (list "intro" (plist-get talk :slug))
- (list ">" "/dev/null" "2>&1" "&")))))
+ #'start-process
+ (delq nil
+ (append
+ (list
+ (concat "ssh-" (plist-get (emacsconf-get-track talk) :id))
+ (concat "*" (plist-get talk :track) "*") "ssh"
+ (concat (tramp-file-name-user info)
+ "@" (tramp-file-name-host info))
+ "-p" (tramp-file-name-port info)
+ (format "DISPLAY=%s" (plist-get (emacsconf-get-track talk) :vnc-display))
+ "nohup")
+ (if (listp (car commands)) (car commands) commands))))))
+
+(defun emacsconf-stream-play-intro (talk)
+ "Play the recorded intro or display the in-between slide for TALK."
+ (interactive (list (emacsconf-complete-talk-info)))
+ (setq talk (emacsconf-resolve-talk talk))
+ (emacsconf-stream-track-ssh talk "intro" (plist-get talk :slug)))
(defun emacsconf-stream-play-talk-on-change (talk)
"Play the talk."
(interactive (list (emacsconf-complete-talk-info)))
+ (setq talk (emacsconf-resolve-talk talk))
(when (or (not (boundp 'org-state)) (string= org-state "PLAYING"))
- (let ((info (tramp-dissect-file-name (emacsconf-stream-track-login talk))))
- (apply
- #'call-process
- (append
- (list
- "ssh" nil nil t
- (concat (tramp-file-name-user info)
- "@" (tramp-file-name-host info))
- "-p" (tramp-file-name-port info)
- (format "DISPLAY=%s" (plist-get (emacsconf-get-track talk) :vnc-display))
- "nohup")
- (cond
- ((and
- (plist-get talk :recorded-intro)
- (plist-get talk :video-file)) ;; recorded intro and recorded talk
- (message "should automatically play intro and recording")
- (list "play" (plist-get talk :slug))) ;; todo deal with stream files
- ((and
- (plist-get talk :recorded-intro)
- (null (plist-get talk :video-file))) ;; recorded intro and live talk; play the intro and join BBB
- (message "should automatically play intro; join %s" (plist-get talk :bbb-backstage))
- (list "intro" (plist-get talk :slug)))
- ((and
- (null (plist-get talk :recorded-intro))
- (plist-get talk :video-file)) ;; live intro and recorded talk, show slide and use Mumble; manually play talk
- (message "should show intro slide; play %s afterwards" (plist-get talk :slug))
- (list "intro" (plist-get talk :slug)))
- ((and
- (null (plist-get talk :recorded-intro))
- (null (plist-get talk :video-file))) ;; live intro and live talk, join the BBB
- (message "join %s for live intro and talk" (plist-get talk :bbb-backstage))
- (list "bbb" (plist-get talk :slug))))
- (list "&"))))))
+ (emacsconf-stream-track-ssh
+ talk
+ (cond
+ ((and
+ (plist-get talk :recorded-intro)
+ (plist-get talk :video-file)) ;; recorded intro and recorded talk
+ (message "should automatically play intro and recording")
+ (list "play-with-intro" (plist-get talk :slug))) ;; todo deal with stream files
+ ((and
+ (plist-get talk :recorded-intro)
+ (null (plist-get talk :video-file))) ;; recorded intro and live talk; play the intro and join BBB
+ (message "should automatically play intro; join %s" (plist-get talk :bbb-backstage))
+ (list "intro" (plist-get talk :slug)))
+ ((and
+ (null (plist-get talk :recorded-intro))
+ (plist-get talk :video-file)) ;; live intro and recorded talk, show slide and use Mumble; manually play talk
+ (message "should show intro slide; play %s afterwards" (plist-get talk :slug))
+ (list "intro" (plist-get talk :slug)))
+ ((and
+ (null (plist-get talk :recorded-intro))
+ (null (plist-get talk :video-file))) ;; live intro and live talk, join the BBB
+ (message "join %s for live intro and talk" (plist-get talk :bbb-backstage))
+ (list "bbb" (plist-get talk :slug)))))))
(defun emacsconf-stream-get-filename (talk)
"Return the local filename for the video file for TALK.
@@ -241,65 +242,79 @@ Final files should be stored in /data/emacsconf/stream/YEAR/video-slug--main.web
(defun emacsconf-stream-play-video (talk)
(interactive (list (emacsconf-complete-talk-info)))
- (let ((info (tramp-dissect-file-name (emacsconf-stream-track-login talk))))
- (apply
- #'call-process
- (append
- (list
- "ssh" nil nil t
- (concat (tramp-file-name-user info)
- "@" (tramp-file-name-host info))
- "-p" (tramp-file-name-port info)
- "nohup" "~/bin/track-mpv")
- (or (and (plist-get talk :stream-files)
- (if (stringp (plist-get talk :stream-files))
- (split-string-and-unquote (plist-get talk :stream-files))
- (plist-get talk :stream-files)))
- (list (plist-get talk :slug)))
- (list ">" "/dev/null" "2>&1" "&")))))
+ (setq talk (emacsconf-resolve-talk talk))
+ (emacsconf-stream-track-ssh
+ talk
+ (append
+ (list
+ "play"
+ (plist-get talk :slug))
+ (if (stringp (plist-get talk :stream-files))
+ (split-string-and-unquote (plist-get talk :stream-files))
+ (plist-get talk :stream-files)))))
+
+(defun emacsconf-stream-play-video-file (talk filename)
+ (interactive (list (emacsconf-complete-talk-info)))
+ (setq talk (emacsconf-resolve-talk talk))
+ (apply
+ #'emacsconf-stream-track-ssh
+ talk
+ "overlay"
+ (plist-get talk :slug))
+ (apply
+ #'emacsconf-stream-track-ssh
+ talk
+ "mpv"
+ filename))
(defun emacsconf-stream-open-pad (talk)
(interactive (list (emacsconf-complete-talk-info)))
- (let ((default-directory (emacsconf-stream-track-login talk))
- (async-shell-command-buffer 'new-buffer))
- (shell-command
- (concat "nohup firefox -new-window "
- (shell-quote-argument (plist-get talk :pad-url))
- " > /dev/null 2>&1 & "))))
+ (setq talk (emacsconf-resolve-talk talk))
+ (emacsconf-stream-track-ssh
+ talk
+ "firefox"
+ (plist-get talk :pad-url)))
+
+(defun emacsconf-stream-bbb (talk)
+ "Open the BBB room for TALK."
+ (interactive (list (emacsconf-complete-talk-info)))
+ (setq talk (emacsconf-resolve-talk talk))
+ (emacsconf-stream-track-ssh talk "bbb" (plist-get talk :slug)))
(defun emacsconf-stream-join-qa (talk)
"Join the Q&A for TALK.
This uses the BBB room if available, or the IRC channel if not."
(interactive (list (emacsconf-complete-talk-info)))
(if (and (null (plist-get talk :video-file))
- (string-match "live" (plist-get talk :q-and-a)))
- ;; do nothing, streamer should already be in the room
- (let ((default-directory (emacsconf-stream-track-login talk))
- (async-shell-command-buffer 'new-buffer))
- (save-window-excursion
- (shell-command
- (concat "nohup firefox -new-window "
- (shell-quote-argument
- (pcase (plist-get talk :q-and-a)
- ((or 'nil "" (rx "Mumble"))
- (plist-get talk :qa-slide-url))
- ((rx "live")
- (plist-get talk :bbb-backstage))
- ((rx "IRC")
- (plist-get talk :webchat-url))
- (_ (plist-get talk :qa-slide-url))))
- " > /dev/null 2>&1 & "))))))
+ (string-match "live" (plist-get talk :q-and-a)))
+ (emacsconf-stream-track-ssh
+ talk
+ "firefox"
+ "-new-window"
+ (plist-get talk :pad-url))
+ (emacsconf-stream-track-ssh
+ talk
+ "firefox"
+ "-new-window"
+ (pcase (plist-get talk :q-and-a)
+ ((or 'nil "" (rx "Mumble"))
+ (plist-get talk :qa-slide-url))
+ ((rx "live")
+ (plist-get talk :bbb-backstage))
+ ((rx "IRC")
+ (plist-get talk :webchat-url))
+ ((rx "pad")
+ (plist-get talk :pad-url))
+ (_ (plist-get talk :qa-slide-url))))))
(defun emacsconf-stream-join-chat (talk)
"Join the IRC chat for TALK."
(interactive (list (emacsconf-complete-talk-info)))
- (let ((default-directory (emacsconf-stream-track-login talk))
- (async-shell-command-buffer 'new-buffer))
- (shell-command
- (concat "nohup firefox -new-window "
- (shell-quote-argument
- (plist-get talk :webchat-url))
- " > /dev/null 2>&1 & "))))
+ (setq talk (emacsconf-resolve-talk talk))
+ (emacsconf-stream-track-ssh
+ talk
+ "firefox"
+ (plist-get talk :webchat-url)))
(defun emacsconf-stream-write-talk-overlay-svgs (talk video-filename other-filename)
(setq talk (emacsconf-stream-add-talk-props talk))
@@ -502,21 +517,6 @@ This uses the BBB room if available, or the IRC channel if not."
(message "Done %s" talk)
(undo-boundary))))
-
-(defun emacsconf-stream-schedule-timers (&optional info)
- (interactive)
- (setq info (emacsconf-filter-talks (or info (emacsconf-get-talk-info))))
- (cancel-function-timers #'emacsconf-stream-handle-talk-timer)
- (let ((now (current-time)))
- (mapc (lambda (talk)
- (when (and (time-less-p now (plist-get talk :start-time)))
- (run-at-time
- (time-to-seconds (time-subtract (plist-get talk :start-time) now))
- nil
- #'emacsconf-stream-handle-talk-timer
- talk)))
- info)))
-
(defun emacsconf-stream-generate-assets-for-talk (talk)
(interactive (list (emacsconf-complete-talk-info)))
(let ((info (list talk)))
@@ -741,5 +741,82 @@ ffplay URL
(when (timerp emacsconf-stream-clock-timer)
(cancel-timer emacsconf-stream-clock-timer))))
+;;; Timers
+
+(defvar emacsconf-stream-timers nil "List of timers for easy reference")
+
+(defun emacsconf-stream-cancel-timer (id)
+ "Cancel a timer by ID."
+ (interactive (list
+ (completing-read
+ "ID: "
+ (lambda (string pred action)
+ (if (eq action 'metadata)
+ `(metadata (display-sort-function . ,#'identity))
+ (complete-with-action action
+ (sort
+ (seq-filter (lambda (o)
+ (and (timerp (cdr o))
+ (not (timer--triggered (cdr o)))))
+ emacsconf-stream-timers)
+ (lambda (a b) (string< (car a) (car b))))
+ string pred))))))
+ (when (timerp (assoc-default id emacsconf-stream-timers))
+ (cancel-timer (assoc-default id emacsconf-stream-timers))
+ (setq emacsconf-stream-timers
+ (delq (assoc id emacsconf-stream-timers)
+ (seq-filter (lambda (o)
+ (and (timerp (cdr o))
+ (not (timer--triggered (cdr o)))))
+ emacsconf-stream-timers)))))
+
+(defun emacsconf-stream-schedule-talk-status-change (talk time new-status)
+ "Schedule a one-off timer for TALK at TIME to set it to NEW-STATUS."
+ (interactive (list (emacsconf-complete-talk-info)
+ (read-string "Time: ")
+ (completing-read "Status: " (mapcar 'car emacsconf-status-types))))
+ (require 'diary-lib)
+ (setq talk (emacsconf-resolve-talk talk))
+ (let* ((converted
+ (cond
+ ((listp time) time)
+ ((timer-duration time) (timer-relative-time nil (timer-duration time)))
+ (t ; HH:MM
+ (date-to-time (concat (format-time-string "%Y-%m-%d" nil emacsconf-timezone)
+ "T"
+ (string-pad time 5 ?0 t)
+ emacsconf-timezone-offset)))))
+ (timer-id (concat (format-time-string "%m-%dT%H:%M" converted)
+ "-"
+ (plist-get talk :slug)
+ "-"
+ new-status)))
+ (emacsconf-stream-cancel-timer timer-id)
+ (add-to-list 'emacsconf-stream-timers
+ (cons
+ timer-id
+ (run-at-time time converted #'emacsconf-update-talk-status-with-hooks (plist-get talk :slug) "." new-status)))))
+
+(defun emacsconf-stream-schedule-timers (&optional info)
+ "Schedule PLAYING for the rest of talks and CLOSED_Q for recorded talks."
+ (interactive)
+ (emacsconf-stream-cancel-all-timers)
+ (setq info (emacsconf-prepare-for-display (emacsconf-filter-talks (or info (emacsconf-get-talk-info)))))
+ (let ((now (current-time)))
+ (mapc (lambda (talk)
+ (when (and (time-less-p now (plist-get talk :start-time)))
+ (emacsconf-stream-schedule-talk-status-change talk (plist-get talk :start-time) "PLAYING"))
+ (when (and
+ (plist-get talk :video-file)
+ (plist-get talk :qa-time)
+ (time-less-p now (plist-get talk :qa-time)))
+ (emacsconf-stream-schedule-talk-status-change talk (plist-get talk :qa-time) "CLOSED_Q")))
+ info)))
+
+(defun emacsconf-stream-cancel-all-timers ()
+ (interactive)
+ (cancel-function-timers #'emacsconf-update-talk-status-with-hooks)
+ (setq emacsconf-stream-timers nil))
+
(provide 'emacsconf-stream)
;;; emacsconf-stream.el ends here
diff --git a/emacsconf.el b/emacsconf.el
index abd0052..214e896 100644
--- a/emacsconf.el
+++ b/emacsconf.el
@@ -533,8 +533,11 @@
(or (assoc-default (plist-get o :status)
emacsconf-status-types 'string= "")
(plist-get o :status)))
- (if (member (plist-get o :status)
- (split-string "PLAYING CLOSED_Q OPEN_Q UNSTREAMED_Q TO_ARCHIVE TO_EXTRACT TO_FOLLOW_UP DONE"))
+ (if (or
+ (member (plist-get o :status)
+ (split-string "PLAYING CLOSED_Q OPEN_Q UNSTREAMED_Q TO_ARCHIVE TO_EXTRACT TO_FOLLOW_UP DONE"))
+ (time-less-p (plist-get o :start-time)
+ (current-time)))
(plist-put o :public t))
o)
@@ -581,45 +584,52 @@
(null (plist-get o :email))
(string= (plist-get o :status) "CANCELLED")
(string-match "after" (plist-get o :q-and-a)))
- (if (string= (plist-get o :status) "WAITING_FOR_PREREC")
- (plist-put o :live-time
- (plist-get o :start-time))
- (progn
- (plist-put
- o :checkin-label
- "1 hour before the scheduled start of your talk, since you don't have a pre-recorded video")
- (plist-put
- o :checkin-time
- (time-subtract (plist-get o :start-time) (seconds-to-time 3600))))
- (plist-put o :live-time
- (time-add (plist-get o :start-time)
- (seconds-to-time (* 60 (string-to-number (plist-get o :video-time))))))
+ (if (null (plist-get o :video-file))
+ (progn
+ (plist-put o :live-time (plist-get o :start-time))
+ (plist-put o :qa-time (plist-get o :live-time))
+ (plist-put
+ o :checkin-label
+ "1 hour before the scheduled start of your talk, since you don't have a pre-recorded video")
+ (plist-put
+ o :checkin-time
+ (seconds-to-time (time-subtract (plist-get o :start-time) (seconds-to-time 3600)))))
+ (plist-put o :live-time
+ (seconds-to-time
+ (time-add (plist-get o :start-time)
+ (seconds-to-time (* 60 (string-to-number (plist-get o :video-time)))))))
+ (plist-put o :qa-time
+ (plist-get o :live-time))
+
(plist-put o :checkin-label
"30 minutes before the scheduled start of your Q&A, since you have a pre-recorded video")
(when (plist-get o :video-time)
(plist-put o :checkin-time
- (time-subtract (time-add (plist-get o :start-time)
- (seconds-to-time (* 60 (string-to-number (plist-get o :video-time)))))
- (seconds-to-time (/ 3600 2)))))))
+ (seconds-to-time
+ (time-subtract (time-add (plist-get o :start-time)
+ (seconds-to-time (* 60 (string-to-number (plist-get o :video-time)))))
+ (seconds-to-time (/ 3600 2))))))))
o)
(defun emacsconf-add-live-info (o)
(plist-put o :absolute-url (concat emacsconf-base-url (plist-get o :url)))
(plist-put o :in-between-url (format "%s%s/in-between/%s.png"
- emacsconf-media-base-url
- emacsconf-year
- (plist-get o :slug)))
+ emacsconf-media-base-url
+ emacsconf-year
+ (plist-get o :slug)))
(plist-put o :qa-slide-url (format "%s%s/in-between/%s.png"
emacsconf-media-base-url
emacsconf-year
(plist-get o :slug)))
+ (plist-put o :intro-expanded (emacsconf-pad-expand-intro o))
(let ((track (emacsconf-get-track (plist-get o :track))))
(when track
(plist-put o :watch-url (concat emacsconf-base-url emacsconf-year "/watch/" (plist-get track :id)))
(plist-put o :webchat-url (concat emacsconf-chat-base "?join=emacsconf,"
(replace-regexp-in-string "#" ""
- (plist-get track :channel)))))
- (plist-put o :channel (plist-get track :channel))
+ (plist-get track :channel))))
+ (plist-put o :track-id (plist-get track :id)))
+ (plist-put o :channel (plist-get track :channel))
(plist-put o :bbb-backstage (concat emacsconf-media-base-url emacsconf-year "/backstage/current/room/" (plist-get o :slug)))
(cond
((string= (or (plist-get o :q-and-a) "") "")
@@ -734,6 +744,10 @@
(plist-put (pop cur-list) :next-talks (seq-take cur-list number)))
info))
+(defun emacsconf-resolve-talk (talk)
+ "Return the plist for TALK."
+ (if (stringp talk) (emacsconf-find-talk-info talk) talk))
+
(defun emacsconf-find-talk-info (filter &optional info)
(setq info (or info (emacsconf-filter-talks (emacsconf-get-talk-info))))
(when (stringp filter) (setq filter (list filter)))
@@ -1042,12 +1056,12 @@
:tramp "/ssh:emacsconf-dev@res.emacsconf.org#46668:"
:stream ,(concat emacsconf-stream-base "dev.webm")
:480p ,(concat emacsconf-stream-base "dev-480p.webm")
- :start "09:00" :end "17:00"
+ :start "10:00" :end "17:00"
:vnc-display ":6"
:vnc-port "5906"
:status "offline")))
-(setq emacsconf-shifts (list (list :id "sat-am-gen" :track "General" :start "2022-12-03T08:00:00-0500" :end "2022-12-03T12:00:00-0500" :host "zaeph" :streamer "sachac" :checkin "corwin" :irc "bandali" :pad "publicvoit" :coord "sachac") (list :id "sat-pm-gen" :track "General" :start "2022-12-03T13:00:00-0500" :end "2022-12-03T18:00:00-0500" :host "zaeph" :streamer "sachac" :checkin "FlowyCoder" :irc "dto" :pad "publicvoit" :coord "sachac") (list :id "sat-am-dev" :track "Development" :start "2022-12-03T08:00:00-0500" :end "2022-12-03T12:00:00-0500" :host "bandali" :streamer "sachac" :checkin "corwin" :irc "dto" :coord "sachac") (list :id "sat-pm-dev" :track "Development" :start "2022-12-03T13:00:00-0500" :end "2022-12-03T18:00:00-0500" :host "vetrivln" :streamer "bandali" :checkin "FlowyCoder" :irc "vetrivln" :coord "sachac") (list :id "sun-am-gen" :track "General" :start "2022-12-04T08:00:00-0500" :end "2022-12-04T12:00:00-0500" :host "zaeph" :streamer "sachac" :checkin "corwin" :irc "bandali" :pad "publicvoit" :coord "sachac") (list :id "sun-pm-gen" :track "General" :start "2022-12-04T13:00:00-0500" :end "2022-12-04T18:00:00-0500" :host "zaeph" :streamer "jman" :checkin "FlowyCoder" :irc "dto" :pad "publicvoit" :coord "sachac") (list :id "sun-am-dev" :track "Development" :start "2022-12-04T08:00:00-0500" :end "2022-12-04T12:00:00-0500" :host "bandali" :streamer "sachac" :checkin "corwin" :irc "dto" :coord "sachac") (list :id "sun-pm-dev" :track "Development" :start "2022-12-04T13:00:00-0500" :end "2022-12-04T18:00:00-0500" :host "vetrivln" :streamer "bandali" :checkin "FlowyCoder" :irc "vetrivln" :coord "sachac")))
+(setq emacsconf-shifts (list (list :id "sat-am-gen" :track "General" :start "2022-12-03T08:00:00-0500" :end "2022-12-03T12:00:00-0500" :host "zaeph" :streamer "sachac" :checkin "corwin" :irc "dto" :pad "publicvoit" :coord "sachac") (list :id "sat-pm-gen" :track "General" :start "2022-12-03T13:00:00-0500" :end "2022-12-03T18:00:00-0500" :host "zaeph" :streamer "sachac" :checkin "FlowyCoder" :irc "bandali" :pad "publicvoit" :coord "sachac") (list :id "sat-am-dev" :track "Development" :start "2022-12-03T08:00:00-0500" :end "2022-12-03T12:00:00-0500" :host "bandali" :streamer "sachac" :checkin "corwin" :irc "dto" :coord "sachac") (list :id "sat-pm-dev" :track "Development" :start "2022-12-03T13:00:00-0500" :end "2022-12-03T18:00:00-0500" :host "vetrivln" :streamer "bandali" :checkin "FlowyCoder" :irc "vetrivln" :coord "sachac") (list :id "sun-am-gen" :track "General" :start "2022-12-04T08:00:00-0500" :end "2022-12-04T12:00:00-0500" :host "zaeph" :streamer "sachac" :checkin "corwin" :irc "dto" :pad "publicvoit" :coord "sachac") (list :id "sun-pm-gen" :track "General" :start "2022-12-04T13:00:00-0500" :end "2022-12-04T18:00:00-0500" :host "zaeph" :streamer "jman" :checkin "FlowyCoder" :irc "bandali" :pad "publicvoit" :coord "sachac") (list :id "sun-am-dev" :track "Development" :start "2022-12-04T08:00:00-0500" :end "2022-12-04T12:00:00-0500" :host "bandali" :streamer "sachac" :checkin "corwin" :irc "dto" :coord "sachac") (list :id "sun-pm-dev" :track "Development" :start "2022-12-04T13:00:00-0500" :end "2022-12-04T18:00:00-0500" :host "vetrivln" :streamer "bandali" :checkin "FlowyCoder" :irc "vetrivln" :coord "sachac")))
(defun emacsconf-get-track (name)
(when (listp name) (setq name (plist-get name :track)))
@@ -1303,6 +1317,11 @@ tracks with the ID in the cdr of that list."
(org-agenda-category-filter-preset (list (concat "+" (plist-get track :id)))))
(org-agenda-list nil emacsconf-date 2)))
+(defun emacsconf-update-talk-status-with-hooks (slug from-states to-state)
+ (interactive (list (emacsconf-complete-talk) "." (completing-read "To: " (mapcar 'car emacsconf-status-types))))
+ (emacsconf-with-todo-hooks
+ (emacsconf-update-talk-status slug from-states to-state)))
+
(defun emacsconf-update-talk-status (slug from-states to-state)
(interactive (list (emacsconf-complete-talk) "." (completing-read "To: " (mapcar 'car emacsconf-status-types))))
(emacsconf-with-talk-heading slug