diff options
author | Sacha Chua <sacha@sachachua.com> | 2024-12-01 20:02:35 -0500 |
---|---|---|
committer | Sacha Chua <sacha@sachachua.com> | 2024-12-01 20:02:35 -0500 |
commit | a9525a21833c57aee9591856284e923bbccff029 (patch) | |
tree | 6b5d350def40bfc839ee857bf7967119848b0f8e | |
parent | 3baa6f53523dd11d0e14904ef62b0a23485fa03e (diff) | |
download | emacsconf-el-a9525a21833c57aee9591856284e923bbccff029.tar.xz emacsconf-el-a9525a21833c57aee9591856284e923bbccff029.zip |
add prefer_live property
Diffstat (limited to '')
-rw-r--r-- | emacsconf-pad.el | 146 | ||||
-rw-r--r-- | emacsconf-publish.el | 152 | ||||
-rw-r--r-- | emacsconf-stream.el | 43 | ||||
-rw-r--r-- | emacsconf.el | 33 |
4 files changed, 245 insertions, 129 deletions
diff --git a/emacsconf-pad.el b/emacsconf-pad.el index 7cd5a8f..e3d3692 100644 --- a/emacsconf-pad.el +++ b/emacsconf-pad.el @@ -142,7 +142,9 @@ You can find it in $ETHERPAD_PATH/APIKEY.txt" (format "https://etherpad.wikimedia.org/p/emacsconf-%s-%s" emacsconf-year (plist-get o :slug)) - (concat emacsconf-pad-base emacsconf-pad-directory (emacsconf-pad-id o)))) + (if (and (listp o) (plist-get o :slug)) + (concat emacsconf-pad-base emacsconf-pad-directory (emacsconf-pad-id o)) + (concat emacsconf-pad-base o)))) (defvar emacsconf-pad-number-of-next-talks 3 "Integer limiting the number of next talks to link to from the pad.") @@ -339,6 +341,7 @@ ${next-talk-list} (defun emacsconf-pad-format-shift-hyperlist (shift info) (let* ((track (emacsconf-get-track (plist-get shift :track))) + (shift-rtmp (seq-find (lambda (entry) (string= (assoc-default "ID" entry) (plist-get shift :id))) emacsconf-rtmp-shifts)) (prefixed (list :start (plist-get shift :start) :end (plist-get shift :end) @@ -353,9 +356,16 @@ ${next-talk-list} :irc-volunteer (format "<em>%s</em>" (emacsconf-surround "IRC-" (plist-get shift :irc) "" "IRC")) :track-id (plist-get track :id) :conf-id emacsconf-id + :channel (concat emacsconf-id "-" (plist-get track :id)) :checkin (format "<em>%s</em>" (emacsconf-surround "CHECKIN-" (plist-get shift :checkin) "" "CHECKIN")) :pad (format "<em>%s</em>" (emacsconf-surround "PAD-" (plist-get shift :pad) "" "PAD")) :coord (format "<em>%s</em>" (emacsconf-surround "COORD-" (plist-get shift :coord) "" "COORD")) + :youtube-rtmp (assoc-default "YouTube" shift-rtmp 'string=) + :youtube-studio-url (assoc-default "YouTube URL" shift-rtmp 'string=) + :youtube-view-url + (replace-regexp-in-string + "https://studio\\.youtube\\.com/video/\\([^/]+\\)/livestreaming" "https://youtube.com/live/\\1" + (assoc-default "YouTube URL" shift-rtmp 'string=)) :checkin-pad (concat emacsconf-pad-base "checkin-" (downcase (format-time-string "%a" (date-to-time (plist-get shift :start))))))) (shift-talks (mapcar (lambda (o) (append prefixed o)) @@ -384,29 +394,29 @@ ${next-talk-list} <strong>Setup</strong> <ul> -<li>[ ] ${checkin}: Open ${checkin-pad}</li> -<li>[ ] ${irc-volunteer}: Watch the #emacsconf-${track-id} channel and open ${base-url}${year}/talks for links to the pads</li> -<li>[ ] ${pad}: Open ${base-url}${year}/talks for links to the pads</li> -<li>[ ] ${coord}: ssh orga@live0.emacsconf.org and run screen-fallbacks; confirm that the streams are showing fallbacks</li> -<li>[ ] ${stream}: Start recording with OBS -<li>[ ] Copy the password file if you don't already have it: <strong>scp emacsconf-${track-id}@res.emacsconf.org:~/.vnc/passwd vnc-passwd-${track-id} -p ${ssh-port}</strong></li> -<li>[ ] Forward your local ports: <strong>ssh emacsconf-${track-id}@res.emacsconf.org -N -L ${vnc-port}:127.0.0.1:${vnc-port} -p ${ssh-port} &</strong></li> -<li>[ ] Connect via VNC: <strong>xvncviewer 127.0.0.1:${vnc-port} -shared -geometry 1280x720 -passwd vnc-passwd-${track-id} &</strong> -<ul> -<li>[? Can't connect to VNC]: ssh emacsconf-${track-id}@res.emacsconf.org -p ${ssh-port} /home/${conf-id}-${track-id}/bin/track-vnc</li> -<li>[? Can't find OBS]: track-obs</li></ul></li> -<li>[ ] Start background music via SSH or VNC: <em>music</em> -<ul><li>[? No audio device]: -<ul><li><em>pulseaudio -k; pulseaudio --start</em></li> -<li>quit OBS</li> -<li><em>track-obs</em></li></ul></li> -<li>[ ] Start recording (not streaming). (Alt-2, switch to workspace 2; Alt-Shift-2, move something to workspace 2).</li> -<li>[ ] Watch the stream with MPV on your local system: <strong>mpv https://live0.emacsconf.org/emacsconf/${track-id}.webm &</strong></li> -<li>[ ] Check 480p by viewing it : <strong>mpv https://live0.emacsconf.org/emacsconf/${track-id}-480p.webm &</strong></li> -<li>[ ] Confirm that the streaming user has connected to Mumble, is in the ${channel} channel, and can hear what we say on Mumble.</li> -<li>[ ] Test with a sample video or Q&A session. You can run this command on your local system if you want to do things off-screen: <strong>ssh emacsconf-${track-id}@res.emacsconf.org -p 46668 \"~/bin/track-mpv emacsconf &\"</strong></li> -<li>[ ] ${stream}: Restart the background music via SSH or VNC: <em>music</em> . The background music should automatically get killed when the talks start, but if it doesn't, you can stop it with: <em>screen -S background -X quit</em></li> -</ul></li>" + <li>[ ] ${checkin}: Open ${checkin-pad}</li> + <li>[ ] ${irc-volunteer}: Watch the #emacsconf-${track-id} channel and open ${base-url}${year}/talks for links to the pads</li> + <li>[ ] ${pad}: Open ${base-url}${year}/talks for links to the pads</li> + <li>[ ] Copy the password file if you don't already have it: <strong>scp emacsconf-${track-id}@res.emacsconf.org:~/.vnc/passwd vnc-passwd-${track-id} -p ${ssh-port}</strong></li> + <li>[ ] Forward your local ports: <strong>ssh emacsconf-${track-id}@res.emacsconf.org -N -L ${vnc-port}:127.0.0.1:${vnc-port} -p ${ssh-port} &</strong></li> + <li>[ ] Connect via VNC: <strong>xvncviewer 127.0.0.1:${vnc-port} -shared -geometry 1280x720 -passwd vnc-passwd-${track-id} &</strong> + <ul> + <li>[? Can't connect to VNC]: ssh emacsconf-${track-id}@res.emacsconf.org -p ${ssh-port} /home/${conf-id}-${track-id}/bin/track-vnc</li> + <li>[? Can't find OBS]: track-obs</li></ul></li> + <li>[ ] Start background music via SSH or VNC: <em>music</em> + <ul><li>[? No audio device]: + <ul><li><em>pulseaudio -k; pulseaudio --start</em></li> + <li>quit OBS</li> + <li><em>track-obs</em></li></ul> + </li></ul></li> + <li>[ ] OBS - Settings - update the RTMP stream key: <strong>${youtube-rtmp}</strong></li> + <li>[ ] Start recording AND start streaming. (Alt-2, switch to workspace 2; Alt-Shift-2, move something to workspace 2).</li> + <li>[ ] Watch the stream with MPV on your local system: <strong>mpv https://live0.emacsconf.org/emacsconf/${track-id}.webm &</strong></li> + <li>[ ] Check 480p by viewing it : <strong>mpv https://live0.emacsconf.org/emacsconf/${track-id}-480p.webm &</strong></li> + <li>[ ] Check YouTube: ${youtube-studio-url} and ${youtube-view-url}</li> + <li>[ ] Confirm that the streaming user has connected to Mumble, is in the ${channel} channel, and can hear what we say on Mumble.</li> + <li>[ ] Test with a sample video or Q&A session. You can run this command on your local system if you want to do things off-screen: <strong>ssh emacsconf-${track-id}@res.emacsconf.org -p 46668 \"~/bin/track-mpv emacsconf &\"</strong></li> + <li>[ ] ${stream}: Restart the background music via SSH or VNC: <em>music</em> . The background music should automatically get killed when the talks start, but if it doesn't, you can stop it with: <em>screen -S background -X quit</em></li>" (if emacsconf-restream-youtube "<li>[ ] ${coord}: ssh -t orga@live0.emacsconf.org 'screen -S restream-${track-id}-youtube /home/orga/restream-${track-id}-youtube.sh' and then confirm at ${youtube-url}</li> " "") @@ -430,7 +440,7 @@ ${next-talk-list} "</ul>" "Teardown <ul> -<li>[ ] ${stream}: stop recording</li> +<li>[ ] ${stream}: stop recording and stop streaming</li> " (if emacsconf-restream-youtube " @@ -442,7 +452,7 @@ ${next-talk-list} <li>[ ] ${coord}: stop the restream-${track-id}-toobnix screen on live0: <strong>screen -S restream-${track-id}-toobnix -X quit</strong></li> " "") -" + " <li>[ ] ${coord}: update the status page on live.emacsconf.org by changing emacsconf-tracks and calling emacsconf-stream-update-status-page</li> </ul>")) ))) @@ -514,7 +524,7 @@ ${bbb-checklist}</li>") (emacsconf-publish-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 "private-" emacsconf-private-pad-prefix "-checkin-" (downcase (format-time-string "%a" (plist-get (cadr day) :checkin-time)))))) (emacsconf-pad-create-pad pad-id) (emacsconf-pad-set-html pad-id @@ -551,7 +561,9 @@ ${bbb-checklist}</li>") :base-url emacsconf-base-url :year emacsconf-year :checkin-list (mapconcat - (lambda (day) (concat "<li>" emacsconf-pad-base "checkin-" + (lambda (day) (concat "<li>" emacsconf-pad-base + id + "-checkin-" (downcase (format-time-string "%a" (plist-get (cadr day) :checkin-time))) "</li>")) (seq-group-by (lambda (talk) @@ -561,18 +573,19 @@ ${bbb-checklist}</li>") "") :shift-list (mapconcat (lambda (shift) - (format "<li>%sprivate-%s-%s-%s</li>" + (format "<li>%s%s-%s</li>" emacsconf-pad-base - emacsconf-private-pad-prefix - emacsconf-year + id (plist-get shift :id))) emacsconf-shifts "") :host-list (mapconcat (lambda (shift) - (format "<li>%shost-%s</li>" + (format "<li>%sprivate-%s-%s-host-%s</li>" emacsconf-pad-base + emacsconf-private-pad-prefix + emacsconf-year (plist-get shift :id))) emacsconf-shifts "") @@ -589,7 +602,14 @@ ${bbb-checklist}</li>") <div>Combined shift info: <ul>${shift-list}</ul></div> -")))) +")) + (emacsconf-pad-url id))) + +(defun emacsconf-pad-shift-hyperlist-id (shift) + (format "private-%s-%s-%s" + emacsconf-private-pad-prefix + emacsconf-year + (plist-get (emacsconf-resolve-shift shift) :id))) (defun emacsconf-pad-prepopulate-shift-hyperlist (shift &optional info) (interactive (list (completing-read "Shift: " @@ -598,15 +618,17 @@ ${bbb-checklist}</li>") (setq shift (seq-find (lambda (o) (string= (plist-get o :id) shift)) emacsconf-shifts))) (unless info (setq info (emacsconf-publish-prepare-for-display (emacsconf-get-talk-info)))) (let ((info (emacsconf-publish-prepare-for-display (emacsconf-get-talk-info))) - (pad-id (format "private-%s-%s-%s" - emacsconf-private-pad-prefix - emacsconf-year - (plist-get shift :id)))) + (pad-id (emacsconf-pad-shift-hyperlist-id shift))) (emacsconf-pad-create-pad pad-id) (emacsconf-pad-set-html pad-id (emacsconf-pad-format-shift-hyperlist shift info)))) +(defun emacsconf-pad-open-shift-hyperlist (shift) + (interactive (list (completing-read "Shift: " + (mapcar (lambda (o) (plist-get o :id)) emacsconf-shifts)))) + (browse-url (emacsconf-pad-url (emacsconf-pad-shift-hyperlist-id shift)))) + (defun emacsconf-pad-prepopulate-host-hyperlists () (interactive) (mapc #'emacsconf-pad-prepopulate-shift-hyperlist-host emacsconf-shifts)) @@ -618,7 +640,9 @@ ${bbb-checklist}</li>") (setq shift (seq-find (lambda (o) (string= (plist-get o :id) shift)) emacsconf-shifts))) (unless info (setq info (emacsconf-publish-prepare-for-display (emacsconf-get-talk-info)))) (let ((info (emacsconf-publish-prepare-for-display (emacsconf-get-talk-info)))) - (let* ((pad-id (format "host-%s" + (let* ((pad-id (format "private-%s-%s-host-%s" + emacsconf-private-pad-prefix + emacsconf-year (plist-get shift :id))) (shift-talks (seq-filter @@ -667,40 +691,39 @@ ${bbb-checklist}</li>") talk) (concat "${hyperlist-note-info}" - (cond - (;; live talk, join BBB - (null (plist-get talk :video-file)) - "<li><strong>${start-hhmm} ${slug} live talk</strong>: it should play a prerecorded intro, but if it doesn't, join ${bbb-backstage} and introduce talk, then turn it over to speaker for <strong>live talk</strong>: ${expanded-intro} (pronunciation: ${pronunciation})</li>") - (t ;; prerecorded talk - "<li>Backup: ${start-hhmm} ${slug}: it should play a prerecorded intro and talk, but if it doesn't, join ${mumble} in Mumble and introduce talk: ${expanded-intro} (pronunciation: ${pronunciation}); then <em>play ${slug}</em></li>")) + (if (emacsconf-talk-recorded-p talk) + "<li>Backup: ${start-hhmm} ${slug}: it should play a prerecorded intro and talk, but if it doesn't, join ${mumble} in Mumble and introduce talk: ${expanded-intro} (pronunciation: ${pronunciation}); then <em>play ${slug}</em></li>" + ;; live talk, join BBB + "<li><strong>${start-hhmm} ${slug} live talk</strong>: it should play a prerecorded intro, but if it doesn't, join ${bbb-backstage} (mod code <strong>${bbb-mod-code}</strong> ) and introduce talk, then turn it over to speaker for <strong>live talk</strong>: ${expanded-intro} (pronunciation: ${pronunciation})</li>") ;; Q&A - (if (and (null (plist-get talk :video-file)) (not (string= (or (plist-get talk :q-and-a) "none") "none"))) + (if (and (not (emacsconf-talk-prerecorded-p talk)) + (not (string= (or (plist-get talk :qa-type) "none") "none"))) "<li>Continue in the BBB room for live Q&A because the talk was live</li>" - (pcase (plist-get talk :q-and-a) + (pcase (plist-get talk :qa-type) ((or 'nil "" "none" (rx "after")) (if (plist-get talk :video-file) "<li>[ ] ${qa-hhmm} ${slug} Q&A after: Join ${mumble} in Mumble and say that the speaker will follow up with answers on the talk page afterwards. Read questions. ${pad-url}</li>" "")) - ((rx "IRC") + ((rx "irc") "<li>[ ] ${qa-hhmm} ${slug} Q&A IRC: Join ${mumble} in Mumble. Invite people to put their questions in the ${channel} IRC channel and read questions and answers from there. ${webchat-url} ${pad-url}</li>") ((rx "pad") "<li>[ ] <strong>${qa-hhmm}</strong> ${slug} Q&A pad: Join ${mumble} in Mumble. Invite people to put their questions in the Etherpad and read questions and answers from there. ${pad-url}</li>") - ((rx "Mumble") + ((rx "mumble") "<li>[ ] <strong>${qa-hhmm}</strong> ${slug} Q&A mumble: Join ${mumble} in Mumble. Bring the speaker into the right channel if needed. Invite people to put their questions in the Etherpad and read questions and answers from there. ${pad-url} Paste questions into Mumble chat or read them out loud.</li>") ((rx "live") (concat - "<li>[ ] <strong>${qa-hhmm} ${slug} Q&A live</strong> (on stream until ${end-of-qa}): Join ${bbb-backstage}. START RECORDING. Invite people to put their questions in the Etherpad, and read questions from there. ${pad-url}</li> + "<li>[ ] <strong>${qa-hhmm} ${slug} Q&A live</strong> (on stream until ${end-of-qa}): Join ${bbb-backstage} (mod code <strong>${bbb-mod-code}</strong> ). START RECORDING. Invite people to put their questions in the Etherpad, and read questions from there. ${pad-url}</li> ${open-qa} " - (if next-talk - " + (if next-talk + " <li><strong>${next-talk-in-5}</strong> [? Open Q&A is still going on and it's about five minutes before the next talk] <ul><li>[ ] Let the speaker know about the time and that the Q&A can continue off-stream if people want to join</li></ul></li> -<li><strong>${next-talk-in-1}</strong> [? Open Q&A is still going on and it's about a minute before the next talk] +<li><strong>${next-talk-in-2}</strong> [? Open Q&A is still going on and it's about 2 minutes before the next talk] <ul><li>[ ] Announce that the Q&A will continue if people want to join the BBB room from the talk page, and the stream will now move to the next talk</li></ul></li> " - "")) -))))))) + "")) + ))))))) (emacsconf-include-next-talks shift-talks 1) "\n") "</ul>"))))) @@ -775,7 +798,7 @@ ${bbb-checklist}</li>") :media-base emacsconf-media-base-url :mumble (concat emacsconf-id "-" track-id) :next-talk-in-5 (if next-talk (format-time-string "%-l:%M %p" (time-subtract (plist-get next-talk :start-time) (seconds-to-time 300)) emacsconf-timezone) "") - :next-talk-in-1 (if next-talk (format-time-string "%-l:%M %p" (time-subtract (plist-get next-talk :start-time) (seconds-to-time 60)) emacsconf-timezone) "") + :next-talk-in-2 (if next-talk (format-time-string "%-l:%M %p" (time-subtract (plist-get next-talk :start-time) (seconds-to-time 120)) emacsconf-timezone) "") :qa-start (format-time-string "%-l:%M %p" (plist-get talk :qa-time) emacsconf-timezone) :qa-end (if next-talk (format-time-string "%-l:%M %p" (plist-get next-talk :start-time)) "end of shift") @@ -809,17 +832,17 @@ ${bbb-checklist}</li>") (concat (emacsconf-surround "<li><strong>" (plist-get talk :hyperlist-note) "</strong></li>" "") "<li>Recorded intro: <a href=\"${media-base}${year}/backstage/${file-prefix}--intro.webm\">${media-base}${year}/backstage/${file-prefix}--intro.webm</a>" - (if (plist-get talk :video-file) + (if (emacsconf-talk-recorded-p talk) "<li>[ ] [? stream didn't auto-play] ${stream}: <em>handle-session ${slug}</em>; if that doesn't work, <em>play ${slug}</em>; if that still doesn't work, <em>track-mpv ~/current/cache/${conf-id}-${year}-${slug}*--intro.webm</em> and <em>track-mpv ~/current/cache/${conf-id}-${year}-${slug}*--main.webm</em></li>" (concat "<li>Live talk:<ul>" "<li>[ ] [? stream didn't auto-join] ${stream}: <a href=\"${bbb-backstage}\">${bbb-backstage}</a></li>" "<li>[ ] ${host}: Join <a href=\"${bbb-backstage}\">${bbb-backstage}</a> and turn over to speaker.</li></ul></li>")) - (pcase (or (plist-get talk :q-and-a) "") + (pcase (or (plist-get talk :qa-type) "") ((rx "live") (concat "<li>Live Q&A start ${qa-start}, on stream until ${qa-end}<ul> -<li>[ ] ${host}: Join the Q&A room at <a href=\"${bbb-backstage}\">${bbb-backstage}</a> and open the pad at <a href=\"${pad-url}\">${pad-url}</a>; optionally open IRC for ${channel} (<a href=\"${webchat-url}\">${webchat-url}</a>)</li> +<li>[ ] ${host}: Copy the modcode <strong>${bbb-mod-code}</strong> , join the Q&A room at <a href=\"${bbb-backstage}\">${bbb-backstage}</a>, and open the pad at <a href=\"${pad-url}\">${pad-url}</a>; optionally open IRC for ${channel} (<a href=\"${webchat-url}\">${webchat-url}</a>)</li> <li>[ ] [? speaker missing?] ${host}: Let #emacsconf-org know so that we can text or call the speaker</li> <li>[ ] [? stream didn't auto-join?] ${stream}: <em>bbb ${slug}</em> <ul> @@ -828,7 +851,7 @@ ${bbb-checklist}</li>") </ul> </li> <li>[ ] ${stream}: Give the host the go-ahead via Mumble or #emacsconf-org</li> -<li>[ ] ${host}: Start recording and read questions</li> +<li>[ ] ${host}: Announce that people can join using the URL on the talk page or ask questions on the pad or IRC channel. START RECORDING.</li> <li>[ ] ${stream}: Adjust the audio levels as needed: ${ssh-audio}</li> " (if emacsconf-qa-start-open @@ -836,11 +859,10 @@ ${bbb-checklist}</li>") "<li>[ ] ${host}: Decide when to open the Q&A and let ${stream} know</li> <li>[ ] ${stream}: Update the task status (no visible changes): ${ssh-openq}</li>") " -<li>[ ] ${stream}: Confirm BBB redirect at <a href=\"${bbb-redirect}\">${bbb-redirect}</a> goes to BBB room, let host know</li> -<li>[ ] ${host}: Announce that people can join using the URL on the talk page or ask questions on the pad or IRC channel</li> +<li>[ ] ${stream}: Confirm BBB redirect at <a href=\"${bbb-redirect}\">${bbb-redirect}</a> goes to BBB room, let host know; backup: <em>ssh orga@media.emacsconf.org \"~/bin/bbb-open ${slug}\"</em></li> <li>${next-talk-in-5} [? Open Q&A is still going on and it's about five minutes before the next talk] <ul><li>[ ] ${host}: Let the speaker know about the time and that the Q&A can continue off-stream if people want to join</li></ul></li> -<li>${next-talk-in-1} [? Open Q&A is still going on and it's about a minute before the next talk] +<li>${next-talk-in-2} [? Open Q&A is still going on and it's about 2 minutes before the next talk] <ul><li>[ ] ${host}: Announce that the Q&A will continue if people want to join the BBB room from the talk page, and the stream will now move to the next talk</li></ul></li> <li>[? Q&A is done early] <ul> diff --git a/emacsconf-publish.el b/emacsconf-publish.el index c42d0ee..5cc9e62 100644 --- a/emacsconf-publish.el +++ b/emacsconf-publish.el @@ -261,45 +261,54 @@ (mapconcat (lambda (o) (concat - "<tr>" - (format - "<td><a name=\"%s\"></a>" - (plist-get o :slug)) - (plist-get o :qa-link) - "</td>" - "<td>" (if (plist-get o :pad-url) - (format "<a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">Pad</a>" (plist-get o :pad-url)) - "") - "</td>" - "<td>" (format "<a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">Chat</a>" (plist-get o :webchat-url)) - "" - "</td>" + (format "<tr id=\"%s\">" (plist-get o :slug)) "<td>" (format-time-string "%-l:%M" (plist-get o :start-time) emacsconf-timezone) "</td>" + "<td><strong>" + (cond + ((not (emacsconf-talk-recorded-p o)) + (format-time-string "%-l:%M" (plist-get o :start-time) emacsconf-timezone)) + ((string-match "live" (plist-get o :qa-type)) + (format-time-string "%-l:%M" (plist-get o :qa-time) emacsconf-timezone)) + (t "")) + "</strong></td>" "<td>" (format "<a href=\"%s%s/talks/%s\" target=\"_blank\" rel=\"noreferrer\">%s</a>" emacsconf-base-url emacsconf-year (plist-get o :slug) (plist-get o :slug)) "</td>" + "<td>" (if (emacsconf-talk-recorded-p o) (plist-get o :qa-type) "(live talk)") "</td>" + (if (plist-get o :bbb-room) + (format "<td><button class=\"copy\" data-copy=\"%s\" data-label=\"Copy mod code\">Copy mod code</button></td>" + (plist-get o :bbb-mod-code)) + "<td></td>") + (concat "<td>" + (if (plist-get o :bbb-room) + (format "<a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">Join Q&A</a>" (plist-get o :bbb-room) + "")) + "</td>") + "<td>" (if (plist-get o :pad-url) + (format "<a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">Pad</a>" (plist-get o :pad-url)) + "") + "</td>" + "<td>" (format "<a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">Chat</a>" (plist-get o :webchat-url)) + "" + "</td>" + "<td>" (or (plist-get o :title) "") "</td>" + "<td>" (or (plist-get o :speakers) "") (emacsconf-surround " (" (plist-get o :irc) ")" "") "</td>" "</tr>")) info "\n")) -(defun emacsconf-publish-res-index () +(defun emacsconf-publish-backstage-talk-index () "Publish BBB room URLs and pad links for volunteer convenience." (interactive) (let* ((emacsconf-publishing-phase 'conference) (info (mapcar (lambda (o) (append (list :url (concat "#" (plist-get o :slug))) - (if (and (string-match "live" (or (plist-get o :q-and-a) "")) - (plist-get o :bbb-room)) - (append (list - :qa-link - (format "<a href=\"%s\" target=\"_blank\" rel=\"noreferrer\">Join Q&A</a>" (plist-get o :bbb-room))) - o) - o))) + o)) (emacsconf-publish-prepare-for-display (emacsconf-get-talk-info))))) (mapc (lambda (track) @@ -312,6 +321,7 @@ "<html><head><meta charset=\"utf-8\" /><link rel=\"stylesheet\" href=\"style.css\"></head><body> <div>" (let ((emacsconf-use-absolute-url t) + (emacsconf-schedule-svg-modify-functions '(emacsconf-schedule-svg-color-by-status)) (emacsconf-base-url "")) (with-temp-buffer (svg-print (emacsconf-schedule-svg 800 300 info)) @@ -322,17 +332,19 @@ (mapconcat (lambda (day) (format - "<tr><th colspan=\"6\" style=\"text-align: left\">%s</th></tr> -<tr><th>Q&A</th><th>Pad</th><th>Chat</th><th>Time</th><th>Slug</th><th>Title</th></tr> + "<tr><th colspan=\"7\" style=\"text-align: left\">%s</th></tr> +<tr><th>Talk start</th><th>BBB start</th><th>Talk ID</th><th>Q&A type</th><th>Mod code</th><th>BBB</th><th>Pad</th><th>Chat</th><th>Title</th><th>Speakers</th></tr> %s" (car day) (emacsconf-publish-format-res-talks (cdr day)))) (seq-group-by (lambda (o) (format-time-string "%A, %b %-e" (plist-get o :start-time))) track-talks) "\n") - "</table></body></html>"))) - (with-temp-file (expand-file-name (format "index-%s.html" (plist-get track :id)) emacsconf-res-dir) - (insert result)) + "</table></body>" + (with-temp-buffer + (insert-file-contents (expand-file-name "include-in-index-footer.html" emacsconf-cache-dir)) + (buffer-string)) + "</html>"))) (with-temp-file (expand-file-name (format "index-%s.html" (plist-get track :id)) emacsconf-backstage-dir) (insert result)))) emacsconf-tracks))) @@ -489,29 +501,29 @@ " ") "")))) "[[${meta} title=\"${title}\"]] -[[${meta} copyright=\"Copyright © ${year} ${speakers}\"]] -[[!inline pages=\"internal(${year}/info/${slug}-nav)\" raw=\"yes\"]] + [[${meta} copyright=\"Copyright © ; ${year} ${speakers}\"]] + [[!inline pages=\"internal(${year}/info/${slug}-nav)\" raw=\"yes\"]] -<!-- Initially generated with emacsconf-publish-talk-page and then left alone for manual editing --> -<!-- You can manually edit this file to update the abstract, add links, etc. --->\n + <!-- Initially generated with emacsconf-publish-talk-page and then left alone for manual editing --> + <!-- You can manually edit this file to update the abstract, add links, etc. --->\n -# ${title} -${speaker-info} + # ${title} + ${speaker-info} -[[!inline pages=\"internal(${year}/info/${slug}-before)\" raw=\"yes\"]] + [[!inline pages=\"internal(${year}/info/${slug}-before)\" raw=\"yes\"]] -${abstract-md} + ${abstract-md} -[[!inline pages=\"internal(${year}/info/${slug}-after)\" raw=\"yes\"]] + [[!inline pages=\"internal(${year}/info/${slug}-after)\" raw=\"yes\"]] -[[!inline pages=\"internal(${year}/info/${slug}-nav)\" raw=\"yes\"]] + [[!inline pages=\"internal(${year}/info/${slug}-nav)\" raw=\"yes\"]] -${categories} -")))))) + ${categories} + ")))))) (defun emacsconf-publish-talk-p (talk) "Return non-nil if the talk is ready to be published. -Talks that are pending review will not be published yet." + Talks that are pending review will not be published yet." (pcase (plist-get talk :status) ('nil nil) ("TODO" nil) @@ -522,10 +534,10 @@ Talks that are pending review will not be published yet." (defun emacsconf-publish-talk-pages (emacsconf-info &optional force) "Populate year/talks/*.md files. -These should include the nav and schedule files, which will be -rewritten as needed. After they are generated, they should be all -right to manually edit to include things like additional -resources." + These should include the nav and schedule files, which will be + rewritten as needed. After they are generated, they should be all + right to manually edit to include things like additional + resources." (interactive (list (emacsconf-get-talk-info) (> (prefix-numeric-value current-prefix-arg) 1))) (mapc (lambda (o) (emacsconf-publish-talk-page o force)) (emacsconf-filter-talks emacsconf-info))) @@ -583,7 +595,7 @@ resources." (if talk-p (concat (or (plist-get o :video-time) (plist-get o :time)) - "-min talk; Q&A: " + "-min talk ; Q&A: " (pcase (plist-get o :qa-type) ("none" "ask questions via Etherpad/IRC; we'll e-mail the speaker and post answers on this wiki page after the conference") ("live" "BigBlueButton conference room") @@ -592,11 +604,11 @@ resources." (_ (plist-get o :qa-type))) (emacsconf-surround " <" (and (member emacsconf-publishing-phase '(schedule conference)) (plist-get o :qa-url)) ">" "")) - (concat (or (plist-get o :video-time) - (plist-get o :time)) "-min talk cancelled")) + (concat (or (plist-get o :video-time) + (plist-get o :time)) "-min talk cancelled")) :pad-info (if (and talk-p emacsconf-publish-include-pads (not (and (member emacsconf-publishing-phase '(schedule conference)) - (string= (plist-get o :qa-type) "etherpad")))) + (string= (plist-get o :qa-type) "etherpad")))) (format "Etherpad: <https://pad.emacsconf.org/%s-%s> \n" emacsconf-year (plist-get o :slug)) "") :irc-info @@ -1812,6 +1824,7 @@ ${include} '(:slug :title :speakers :pronouns :pronunciation :url :track :file-prefix :qa-url :qa-type + :prefer-live :qa-backstage-url)))) (emacsconf-filter-talks (emacsconf-get-talk-info))) :tracks @@ -2063,6 +2076,7 @@ ${include} (autoload 'subed-parse-file "subed-common") (defun emacsconf-publish-video-description (talk &optional copy skip-title) (interactive (list (emacsconf-complete-talk-info) t)) + (when (stringp talk) (setq talk (emacsconf-resolve-talk talk))) (let ((chapters (subed-parse-file (expand-file-name (concat @@ -2097,15 +2111,43 @@ ${chapters}You can view this and other resources using free/libre software at ${ This video is available under the terms of the Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license."))) (if copy (kill-new result)) result)) -;; (emacsconf-publish-video-description (emacsconf-find-talk-info "async") t) -(defun emacsconf-cache-all-video-data () - (interactive) - (mapc - (lambda (talk) - (when (plist-get talk :file-prefix) - (emacsconf-publish-cache-video-data talk))) - (emacsconf-get-talk-info))) +(defun emacsconf-publish-youtube-step-through-publishing () + (interactive) + (catch 'done + (while t + (let ((talk (seq-find (lambda (o) + (and (string= (plist-get o :status) "TO_STREAM") + (not (plist-get o :youtube)) + (emacsconf-talk-file o "--main.webm"))) + (emacsconf-publish-prepare-for-display (emacsconf-get-talk-info))))) + (unless talk + (message "All done so far.") + (throw 'done t)) + (kill-new (emacsconf-talk-file talk "--main.webm")) + (message "Video: %s - press any key" (emacsconf-talk-file talk "--main.webm")) + (when (eq (read-char) ?q) (throw 'done t)) + (emacsconf-publish-video-description talk t) + (message "Copied description - press any key") + (when (eq (read-char) ?q) (throw 'done t)) + (when (emacsconf-talk-file talk "--main.vtt") + (kill-new (emacsconf-talk-file talk "--main.vtt")) + (message "Captions: %s - press any key" (emacsconf-talk-file talk "--main.vtt")) + (when (eq (read-char) ?q) (throw 'done t))) + (emacsconf-set-property-from-slug + (plist-get talk :slug) + "YOUTUBE" + (read-string (format "%s - YouTube URL: " (plist-get talk :scheduled)))))))) + + ;; (emacsconf-publish-video-description (emacsconf-find-talk-info "async") t) + + (defun emacsconf-cache-all-video-data () + (interactive) + (mapc + (lambda (talk) + (when (plist-get talk :file-prefix) + (emacsconf-publish-cache-video-data talk))) + (emacsconf-get-talk-info))) ;; (emacsconf-cache-all-video-data t) (defvar emacsconf-cache-dir (expand-file-name "cache" (file-name-directory emacsconf-org-file))) diff --git a/emacsconf-stream.el b/emacsconf-stream.el index 1b105a3..a1836e8 100644 --- a/emacsconf-stream.el +++ b/emacsconf-stream.el @@ -608,7 +608,7 @@ With a prefix argument (\\[universal-argument]), clear the overlay." (shell-quote-argument (expand-file-name (concat (plist-get talk :slug) ".svg") dir))))) (setq prev talk)) - (emacsconf-filter-talks (cdr track))))) + (emacsconf-filter-talks (cdr track))))) by-track))) @@ -1194,5 +1194,46 @@ If INFO is non-nil, use that as the schedule instead." (plist-put track :autopilot nil) (emacsconf-stream-track-ssh track "crontab -r"))) +(defun emacsconf-stream-copy-livestream-description (shift) + (interactive (list (completing-read "Shift: " (mapcar (lambda (o) (plist-get o :id)) emacsconf-shifts)))) + (when (stringp shift) (setq shift (seq-find (lambda (o) (string= (plist-get o :id) shift)) emacsconf-shifts))) + (let* ((track-id (when (string-match "-\\([a-z]+?\\)$" (plist-get shift :id)) (match-string 1 (plist-get shift :id)))) + (desc (emacsconf-replace-plist-in-string + (list + :track-name (plist-get shift :track) + :start (format-time-string "%Y-%m-%d %-l:%M %p %Z (UTC %z)" (date-to-time (plist-get shift :start))) + :end (format-time-string "%Y-%m-%d %-l:%M %p %Z (UTC %z)" (date-to-time (plist-get shift :end))) + :start-day (format-time-string "%b %-e %a %p" (date-to-time (plist-get shift :start))) + :year emacsconf-year + :track-id track-id + :irc-channels (concat + (string-join + (seq-keep (lambda (track) + (unless (string= (plist-get track :id) track-id) + (plist-get track :channel))) + emacsconf-tracks) + ",") + "," + (plist-get (emacsconf-get-track track-id) :channel))) + " +${track-name} - ${start-day} EmacsConf ${year} + +This for the ${track-name} track of EmacsConf, the conference about the joy of Emacs and Emacs Lisp. +Start: ${start} +End: ${end} + +Watch using free/open source software: https://live.emacsconf.org/${year}/watch/${track-id}/ +Conference info: https://emacsconf.org/${year}/ +Schedule: https://emacsconf.org/${year}/talks/ +Chat on #emacsconf-${track-id} via https://chat.emacsconf.org/?join=emacsconf,emacsconf-org,emacsconf-accessible,${irc-channels} or irc.libera.chat using your favorite IRC client +Etherpad: Use the Etherpad links from the talk page; general comments in https://pad.emacsconf.org/${year} + +Videos are shared under the terms of the Creative Commons Attribution-ShareAlike 4.0 +International (CC BY-SA 4.0) license. Please observe the guidelines for conduct: https://emacsconf.org/conduct/ +"))) + (when (called-interactively-p 'any) + (kill-new desc)) + desc)) + (provide 'emacsconf-stream) ;;; emacsconf-stream.el ends here diff --git a/emacsconf.el b/emacsconf.el index 024c176..82ab325 100644 --- a/emacsconf.el +++ b/emacsconf.el @@ -571,6 +571,11 @@ If INFO is specified, limit it to that list." (setq list (cons (match-string-no-properties 0) list))) (plist-put o :categories (reverse list)))) +(defun emacsconf-talk-recorded-p (talk) + "Returns non-nil if TALK will start with a recorded video." + (and (not (plist-get talk :prefer-live)) + (plist-get talk :video-file))) + (defun emacsconf-get-talk-info-from-properties (o) (let ((heading (org-heading-components)) (field-props '( @@ -602,6 +607,7 @@ If INFO is specified, limit it to that list." (:prerec-info "PREREC_INFO") ;; Prep (:bbb-room "ROOM") + (:bbb-mod-code "BBB_MOD_CODE") ;; Processing (:file-prefix "FILE_PREFIX") (:video-file "VIDEO_FILE") @@ -612,6 +618,7 @@ If INFO is specified, limit it to that list." (:youtube-url "YOUTUBE_URL") (:toobnix-url "TOOBNIX_URL") (:intro-time "INTRO_TIME") + (:prefer-live "PREFER_LIVE") ;; Captioning (:captioner "CAPTIONER") (:caption-note "CAPTION_NOTE") @@ -632,7 +639,7 @@ If INFO is specified, limit it to that list." (:present "PRESENT") (:talk-id "TALK_ID") ; use slug instead (:qa-public "QA_PUBLIC") ; now tracked by the OPEN_Q and UNSTREAMED_Q status - (:uuid "UUID") ; Pentabarf export + (:uuid "UUID") ; Pentabarf export ))) (apply 'append @@ -940,10 +947,7 @@ The subheading should match `emacsconf-abstract-heading-regexp'." (plist-put o :qa-link "none") (plist-put o :qa-backstage-url (plist-get o :pad-url)))) (plist-put o :recorded-intro - (let ((filename - (expand-file-name (concat (plist-get o :slug) ".webm") - (expand-file-name "intros" emacsconf-stream-asset-dir)))) - (and (file-exists-p filename) filename))) + (emacsconf-talk-file o "--intro.webm")) o)) (defun emacsconf-search-talk-info (search &optional info) @@ -1045,6 +1049,12 @@ The subheading should match `emacsconf-abstract-heading-regexp'." "Return the plist for TALK." (if (stringp talk) (emacsconf-find-talk-info talk info) talk)) +(defun emacsconf-resolve-shift (shift) + "Return the plist for SHIFT." + (if (stringp shift) (seq-find (lambda (o) (string= (plist-get o :id) shift)) + emacsconf-shifts) + shift)) + (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))) @@ -1446,7 +1456,7 @@ If TIMEZONES is a string, split it by commas." :vnc-display ":5" :vnc-port "5905" :autopilot crontab - :status "online") + :status "offline") (:name "Development" :color "skyblue" :id "dev" :channel "emacsconf-dev" :watch ,(format "https://live.emacsconf.org/%s/watch/dev/" emacsconf-year) :webchat-url "https://chat.emacsconf.org/?join=emacsconf,emacsconf-org,emacsconf-accessible,emacsconf-gen,emacsconf-dev" @@ -1461,7 +1471,7 @@ If TIMEZONES is a string, split it by commas." :vnc-display ":6" :vnc-port "5906" :autopilot crontab - :status "online"))) + :status "offline"))) (defun emacsconf-get-track (name) "Get the track for NAME. @@ -1526,7 +1536,7 @@ NAME could be a track name, a talk name, or a list." info)) (defvar emacsconf-shifts - (list (list :id "sat-am-gen" :track "General" :start "2023-12-02T08:00:00-0500" :end "2023-12-02T12:00:00-0500" :host "zaeph" :streamer "sachac" :checkin "FlowyCoder" :coord "sachac") (list :id "sat-pm-gen" :track "General" :start "2023-12-02T13:00:00-0500" :end "2023-12-02T18:00:00-0500" :host "zaph" :streamer "sachac" :checkin "FlowyCoder" :coord "sachac") (list :id "sat-am-dev" :track "Development" :start "2023-12-02T08:00:00-0500" :end "2023-12-02T12:00:00-0500" :host "bandali" :streamer "sachac" :checkin "FlowyCoder" :coord "sachac") (list :id "sat-pm-dev" :track "Development" :start "2023-12-02T13:00:00-0500" :end "2023-12-02T18:00:00-0500" :host "bandali" :streamer "sachac" :checkin "FlowyCoder" :coord "sachac") (list :id "sun-am-gen" :track "General" :start "2023-12-03T08:00:00-0500" :end "2023-12-03T12:00:00-0500" :host "zaeph" :streamer "sachac" :checkin "FlowyCoder" :coord "sachac") (list :id "sun-pm-gen" :track "General" :start "2023-12-03T13:00:00-0500" :end "2023-12-03T18:00:00-0500" :host "zaeph" :streamer "sachac" :checkin "FlowyCoder" :coord "sachac") (list :id "sun-am-dev" :track "Development" :start "2023-12-03T08:00:00-0500" :end "2023-12-03T12:00:00-0500" :host "bandali" :streamer "sachac" :checkin "FlowyCoder" :coord "sachac") (list :id "sun-pm-dev" :track "Development" :start "2023-12-03T13:00:00-0500" :end "2023-12-03T18:00:00-0500" :host "bandali" :streamer "sachac" :checkin "FlowyCoder" :coord "sachac"))) + (list (list :id "sat-am-gen" :track "General" :start "2024-12-07T09:00:00-0500" :end "2024-12-07T12:00:00-0500" :host "zaeph" :streamer "sachac" :checkin "sachac" :coord "sachac") (list :id "sat-pm-gen" :track "General" :start "2024-12-07T13:00:00-0500" :end "2024-12-07T17:00:00-0500" :host "zaeph" :streamer "sachac" :checkin "sachac" :coord "sachac") (list :id "sat-am-dev" :track "Development" :start "2024-12-07T10:00:00-0500" :end "2024-12-07T12:00:00-0500" :host "corwin" :streamer "sachac" :checkin "sachac" :coord "sachac") (list :id "sat-pm-dev" :track "Development" :start "2024-12-07T13:00:00-0500" :end "2024-12-07T17:00:00-0500" :host "corwin" :streamer "sachac" :checkin "sachac" :coord "sachac") (list :id "sun-am-gen" :track "General" :start "2024-12-08T09:00:00-0500" :end "2024-12-08T12:00:00-0500" :host "zaeph" :streamer "sachac" :checkin "corwin" :coord "sachac") (list :id "sun-pm-gen" :track "General" :start "2024-12-08T13:00:00-0500" :end "2024-12-08T17:00:00-0500" :host "zaeph" :streamer "sachac" :checkin "corwin" :coord "sachac"))) (defun emacsconf-filter-talks-by-time (start-time end-time info) "Return talks that are between START-TIME and END-TIME (inclusive) in INFO." @@ -1817,9 +1827,10 @@ tracks with the ID in the cdr of that list." (defun emacsconf-add-to-talk-logbook (talk note) "Add NOTE as a logbook entry for TALK." (interactive (list (emacsconf-complete-talk) (read-string "Note: "))) - (save-excursion - (emacsconf-with-talk-heading talk - (emacsconf-add-to-logbook note)))) + (save-window-excursion + (save-excursion + (emacsconf-with-talk-heading talk + (emacsconf-add-to-logbook note))))) (defun emacsconf-reload () "Reload the emacsconf-el modules." |