summaryrefslogtreecommitdiffstats
path: root/emacsconf.el
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--emacsconf.el232
1 files changed, 183 insertions, 49 deletions
diff --git a/emacsconf.el b/emacsconf.el
index 1a76a84..0aee6c5 100644
--- a/emacsconf.el
+++ b/emacsconf.el
@@ -34,20 +34,20 @@
"Name of conference"
:group 'emacsconf
:type 'string)
-(defcustom emacsconf-year "2023"
+(defcustom emacsconf-year "2025"
"Conference year. String for easy inclusion."
:group 'emacsconf
:type 'string)
-(defcustom emacsconf-cfp-deadline "2023-09-14" "Deadline for proposals."
+(defcustom emacsconf-cfp-deadline "2025-09-19" "Target date for proposals."
:group 'emacsconf
:type 'string)
-(defcustom emacsconf-date "2023-12-02" "Starting date of EmacsConf."
+(defcustom emacsconf-date "2025-12-06" "Starting date of EmacsConf."
:group 'emacsconf
:type 'string)
-(defcustom emacsconf-video-target-date "2023-11-04" "Target date for receiving talk videos from the speakers."
+(defcustom emacsconf-video-target-date "2025-10-31" "Target date for receiving talk videos from the speakers."
:group 'emacsconf
:type 'string)
-(defcustom emacsconf-schedule-announcement-date "2023-10-25" "Date for publishing the schedule."
+(defcustom emacsconf-schedule-announcement-date "2025-10-24" "Date for publishing the schedule."
:group 'emacsconf
:type 'string)
(defcustom emacsconf-directory "~/vendor/emacsconf-wiki"
@@ -70,7 +70,7 @@
(defcustom emacsconf-base-url "https://emacsconf.org/" "Includes trailing slash"
:group 'emacsconf
:type 'string)
-(defcustom emacsconf-publishing-phase 'program
+(defcustom emacsconf-publishing-phase 'resources
"Controls what information to include.
'program - don't include times
'schedule - include times; use this leading up to the conference
@@ -86,7 +86,7 @@
(const :tag "Harvest: Extracting info" conference)
(const :tag "Resources: Don't include status, publish all Q&A" resources)))
-(defcustom emacsconf-backstage-phase 'prerec
+(defcustom emacsconf-backstage-phase 'harvest
"Contros what information to include backstage.
'prerec - focus on captioning
'harvest - focus on Q&A."
@@ -125,7 +125,7 @@
(defvar emacsconf-chat-base "https://chat.emacsconf.org/")
(defvar emacsconf-backstage-dir "/ssh:orga@media.emacsconf.org:/var/www/media.emacsconf.org/2022/backstage")
(defvar emacsconf-upload-dir "/ssh:orga@media.emacsconf.org:/srv/upload")
-(defvar emacsconf-res-dir (format "/ssh:orga@res.emacsconf.org:/data/emacsconf/%s" emacsconf-year))
+(defvar emacsconf-res-dir (format "/ssh:orga@res.emacsconf.org:/data/emacsconf/shared/%s" emacsconf-year))
(defvar emacsconf-media-extensions '("webm" "mkv" "mp4" "webm" "mov" "avi" "ts" "ogv" "wav" "ogg" "mp3" ))
(defvar emacsconf-ftp-upload-dir "/ssh:orga@media.emacsconf.org:/srv/ftp/anon/upload-here")
(defvar emacsconf-backstage-user "emacsconf")
@@ -173,6 +173,7 @@
(interactive)
(dired emacsconf-backstage-dir "-tl"))
(defun emacsconf-res-dired () (interactive) (dired emacsconf-res-dir "-tl"))
+(defun emacsconf-res-cache-dired () (interactive) (dired (expand-file-name "cache" emacsconf-res-dir) "-tl"))
(defun emacsconf-media-dired () (interactive) (dired emacsconf-public-media-directory "-tl"))
(defun emacsconf-cache-dired ()
(interactive)
@@ -330,6 +331,61 @@ Return the list of new filenames."
dir)
t))))
+(defun emacsconf-upload-scp-from-json (talk key filename)
+ "Parse PsiTransfer JSON files and copy the uploaded file to the res directory.
+The file is associated with TALK. KEY identifies the file in a multi-file upload.
+FILENAME specifies an extra string to add to the file prefix if needed."
+ (interactive (let-alist (json-parse-string (buffer-string) :object-type 'alist)
+ (list (emacsconf-complete-talk-info)
+ .metadata.key
+ (read-string (format "Filename: ")))))
+ (let* ((source-dir (file-name-directory (buffer-file-name)))
+ (new-filename (concat (plist-get talk :file-prefix)
+ (if (string= filename "")
+ filename
+ (concat "--" filename))
+ "."
+ (let-alist (json-parse-string (buffer-string) :object-type 'alist)
+ (file-name-extension .metadata.name))))
+ (default-directory (expand-file-name "cache" emacsconf-res-dir))
+ (command (emacsconf-upload-scp-command-from-json talk key filename))
+ process)
+ (with-current-buffer (get-buffer-create (format "*scp %s*" (plist-get talk :slug)))
+ (insert (string-join command " ") "\n")
+ (set-process-sentinel
+ (apply #'start-process (concat "scp-" (plist-get talk :slug))
+ (current-buffer) nil
+ command)
+ (lambda (process event)
+ (when (string= event "finished")
+ (message "Finished copying %s"
+ new-filename)))))))
+
+(defun emacsconf-upload-scp-command-from-json (talk key filename)
+ "Parse PsiTransfer JSON files and get the SCP command for copying the uploaded file to the res directory.
+The file is associated with TALK. KEY identifies the file in a multi-file upload.
+FILENAME specifies an extra string to add to the file prefix if needed."
+ (interactive (let-alist (json-parse-string (buffer-string) :object-type 'alist)
+ (list (emacsconf-complete-talk-info)
+ .metadata.key
+ (read-string (format "Filename: ")))))
+ (let* ((source-dir (file-name-directory (buffer-file-name)))
+ (new-filename (concat (plist-get talk :file-prefix)
+ (if (string= filename "")
+ filename
+ (concat "--" filename))
+ "."
+ (let-alist (json-parse-string (buffer-string) :object-type 'alist)
+ (file-name-extension .metadata.name))))
+ (default-directory (expand-file-name "cache" emacsconf-res-dir))
+ (command (list
+ "scp"
+ (replace-regexp-in-string "^/ssh:" "" (expand-file-name key source-dir))
+ new-filename)))
+ (when (called-interactively-p 'any)
+ (kill-new (string-join command " ")))
+ command))
+
(defun emacsconf-upload-copy-from-json (talk key filename)
"Parse PsiTransfer JSON files and copy the uploaded file to the res directory.
The file is associated with TALK. KEY identifies the file in a multi-file upload.
@@ -394,6 +450,14 @@ FILENAME specifies an extra string to add to the file prefix if needed."
(setq value (or value (org-read-property-value prop)))
(org-entry-put (point) prop value))))
+(defun emacsconf-copy-property (search prop)
+ (interactive (list (emacsconf-complete-talk) nil))
+ (save-window-excursion
+ (emacsconf-with-talk-heading search
+ (setq prop (or prop (org-read-property-name)))
+ (when (called-interactively-p 'any)
+ (kill-new (org-entry-get (point) prop)))
+ (org-entry-get (point) prop))))
(defun emacsconf-complete-slug ()
(emacsconf-get-slug-from-string (emacsconf-complete-talk)))
@@ -489,7 +553,9 @@ If INFO is specified, limit it to that list."
,@body))
(defvar emacsconf-status-types
- '(("WAITING_FOR_PREREC" . "Waiting for video from speaker")
+ '(("TO_ACCEPT" . "Waiting for video from speaker")
+ ("TO_CONFIRM" . "Waiting for video from speaker")
+ ("WAITING_FOR_PREREC" . "Waiting for video from speaker")
("TO_PROCESS" . "Processing uploaded video")
("PROCESSING" . "Processing uploaded video")
("TO_AUTOCAP" . "Processing uploaded video")
@@ -513,6 +579,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 :live))
+ (plist-get talk :video-file)))
+
(defun emacsconf-get-talk-info-from-properties (o)
(let ((heading (org-heading-components))
(field-props '(
@@ -544,6 +615,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")
@@ -574,7 +646,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
@@ -685,7 +757,10 @@ The subheading should match `emacsconf-abstract-heading-regexp'."
")")))
(defun emacsconf-get-abstract-from-wiki (o)
- (plist-put o :markdown (emacsconf-talk-markdown-from-wiki (plist-get o :slug))))
+ (plist-put o :markdown (emacsconf-talk-markdown-from-wiki (plist-get o :slug)))
+ (when (plist-get o :markdown)
+ (plist-put o :org-description
+ (pandoc-convert-stdio (plist-get o :markdown) "markdown" "org"))))
(defun emacsconf-add-talk-status (o)
@@ -879,10 +954,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)
@@ -952,13 +1024,8 @@ The subheading should match `emacsconf-abstract-heading-regexp'."
(defun emacsconf-get-talk-info-from-file (&optional filename)
- (with-temp-buffer
- (insert-file-contents (or filename "conf.org"))
- (org-mode)
- (org-show-all)
- (goto-char (point-min))
- (goto-char (org-find-property "ID" "talks"))
- (emacsconf-get-talk-info 'wiki)))
+ (let ((emacsconf-org-file filename))
+ (emacsconf-get-talk-info)))
(defun emacsconf-include-next-talks (info number)
(let* ((info (emacsconf-publish-prepare-for-display info))
@@ -984,6 +1051,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)))
@@ -1083,6 +1156,11 @@ The subheading should match `emacsconf-abstract-heading-regexp'."
(interactive (list (emacsconf-complete-talk)))
(insert (plist-get (emacsconf-search-talk-info search) :title)))
+(defun emacsconf-insert-talk-schedule (search)
+ "Insert the schedule for SEARCH."
+ (interactive (list (emacsconf-complete-talk)))
+ (insert (emacsconf-mail-format-talk-schedule (emacsconf-search-talk-info search))))
+
(defun emacsconf-insert-talk-email (search)
"Insert the talk email matching SEARCH."
(interactive (list (emacsconf-complete-talk)))
@@ -1090,8 +1168,8 @@ The subheading should match `emacsconf-abstract-heading-regexp'."
(defun emacsconf-backstage-url (&optional base-url)
"Return or insert backstage URL with credentials."
- (setq base-url (or base-url (concat emacsconf-media-base-url emacsconf-year "/backstage/")))
(interactive)
+ (setq base-url (or base-url (concat emacsconf-media-base-url emacsconf-year "/backstage/")))
(let ((url (format
"https://%s:%s@%s"
emacsconf-backstage-user
@@ -1116,10 +1194,12 @@ The subheading should match `emacsconf-abstract-heading-regexp'."
"a" #'emacsconf-announce
"i e" #'emacsconf-insert-talk-email
"i t" #'emacsconf-insert-talk-title
+ "i s" #'emacsconf-insert-talk-schedule
"I" #'emacsconf-message-talk-info
"c" #'emacsconf-find-captions-from-slug
"d" #'emacsconf-find-caption-directives-from-slug
"p" #'emacsconf-set-property-from-slug
+ "P" #'emacsconf-copy-property
"w" #'emacsconf-edit-wiki-page
"s" #'emacsconf-set-start-time-for-slug
"W" #'emacsconf-browse-wiki-page
@@ -1362,6 +1442,7 @@ If TIMEZONES is a string, split it by commas."
'((emacsconf-pad-api-key . etherpad_api_key)
(emacsconf-pad-base . etherpad_url)
(emacsconf-backstage-password . emacsconf_backstage_password))))))
+(defvar emacsconf-live-base-url "https://live.emacsconf.org/")
;; (emacsconf-ansible-load-vars (expand-file-name "prod-vars.yml" emacsconf-ansible-directory))
;;; Tracks
(defvar emacsconf-tracks
@@ -1380,21 +1461,21 @@ If TIMEZONES is a string, split it by commas."
:vnc-port "5905"
:autopilot crontab
:status "online")
- (: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"
- :tramp "/ssh:emacsconf-dev@res.emacsconf.org#46668:"
- ;; :toobnix-url "https://toobnix.org/w/w6K77y3bNMo8xsNuqQeCcD"
- ;; :youtube-url "https://www.youtube.com/watch?v=PMaoF-xa1b4"
- ;; :youtube-studio-url "https://studio.youtube.com/video/PMaoF-xa1b4/livestreaming"
- :stream ,(concat emacsconf-stream-base "dev.webm")
- :480p ,(concat emacsconf-stream-base "dev-480p.webm")
- :uid 2003
- :start "10:00" :end "17:00"
- :vnc-display ":6"
- :vnc-port "5906"
- :autopilot crontab
- :status "online")))
+ (: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"
+ :tramp "/ssh:emacsconf-dev@res.emacsconf.org#46668:"
+ ;; :toobnix-url "https://toobnix.org/w/w6K77y3bNMo8xsNuqQeCcD"
+ ;; :youtube-url "https://www.youtube.com/watch?v=PMaoF-xa1b4"
+ ;; :youtube-studio-url "https://studio.youtube.com/video/PMaoF-xa1b4/livestreaming"
+ :stream ,(concat emacsconf-stream-base "dev.webm")
+ :480p ,(concat emacsconf-stream-base "dev-480p.webm")
+ :uid 2003
+ :start "10:00" :end "17:00"
+ :vnc-display ":6"
+ :vnc-port "5906"
+ :autopilot crontab
+ :status "offline")))
(defun emacsconf-get-track (name)
"Get the track for NAME.
@@ -1458,8 +1539,7 @@ NAME could be a track name, a talk name, or a list."
info)
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")))
+(setq emacsconf-shifts (list (list :id "sat-am-gen" :track "General" :start "2025-12-07T09:00:00-0500" :end "2025-12-07T12:00:00-0500") (list :id "sat-pm-gen" :track "General" :start "2025-12-07T13:00:00-0500" :end "2025-12-07T17:00:00-0500") (list :id "sat-am-dev" :track "Development" :start "2025-12-07T10:00:00-0500" :end "2025-12-07T12:00:00-0500") (list :id "sat-pm-dev" :track "Development" :start "2025-12-07T13:00:00-0500" :end "2025-12-07T17:00:00-0500") (list :id "sun-am-gen" :track "General" :start "2025-12-08T09:00:00-0500" :end "2025-12-08T12:00:00-0500") (list :id "sun-pm-gen" :track "General" :start "2025-12-08T13:00:00-0500" :end "2025-12-08T17:00:00-0500")))
(defun emacsconf-filter-talks-by-time (start-time end-time info)
"Return talks that are between START-TIME and END-TIME (inclusive) in INFO."
@@ -1531,6 +1611,39 @@ Filter by TRACK if given. Use INFO as the list of talks."
(seq-group-by (lambda (o) (plist-get o :speakers))
(or info (emacsconf-active-talks (emacsconf-filter-talks (emacsconf-get-talk-info))))))))
+(defun emacsconf-load-rooms (string)
+ "Split STRING and load them as ROOM properties.
+STRING should be a list of rooms, one room per line, like this:
+friendly-id speaker - slugs
+friendly-id speaker - slugs
+"
+ (let ((rooms
+ (mapcar (lambda (row) (when (string-match "^\\(.+?\\) \\(.+\\)" row)
+ (list (match-string 1 row) (match-string 2 row))))
+ (split-string string "\n"))))
+ (mapc (lambda (talk)
+ (emacsconf-go-to-talk talk)
+ (when (plist-get talk :speakers)
+ (org-entry-put
+ (point)
+ "ROOM"
+ (concat
+ emacsconf-bbb-base-url
+ "rooms/"
+ (car
+ (seq-find
+ (lambda (o)
+ (string-match
+ (concat
+ "^"
+ (regexp-quote
+ (plist-get talk :speakers))
+ " - ")
+ (cadr o)))
+ rooms))
+ "/join"))))
+ (emacsconf-active-talks (emacsconf-get-talk-info)))))
+
(defun emacsconf-surround (before text after &optional alternative)
"Concat BEFORE, TEXT, and AFTER if TEXT is specified, or return ALTERNATIVE."
(if (and text (not (string= text "")))
@@ -1538,10 +1651,12 @@ Filter by TRACK if given. Use INFO as the list of talks."
alternative))
;;; Volunteer management
-(defun emacsconf-get-volunteer-info ()
+(defun emacsconf-get-volunteer-info (&optional tag)
(with-current-buffer (find-file-noselect emacsconf-org-file)
(org-map-entries (lambda () (org-entry-properties))
- "volunteer+EMAIL={.}")))
+ (format "volunteer+EMAIL={.}%s"
+ (if tag (concat "+" tag)
+ "")))))
(defun emacsconf-volunteer-emails-for-completion (&optional info)
(mapcar (lambda (o)
@@ -1627,19 +1742,21 @@ Filter by TRACK if given. Use INFO as the list of talks."
(defun emacsconf-add-org-after-todo-state-change-hook ()
"Add FUNC to `org-after-todo-stage-change-hook'."
(interactive)
- (with-current-buffer (find-buffer-visiting emacsconf-org-file)
+ (with-current-buffer (or (find-buffer-visiting emacsconf-org-file)
+ (find-file-noselect emacsconf-org-file))
(add-hook 'org-after-todo-state-change-hook #'emacsconf-org-after-todo-state-change nil t)))
(defun emacsconf-remove-org-after-todo-state-change-hook ()
"Remove FUNC from `org-after-todo-stage-change-hook'."
(interactive)
- (with-current-buffer (find-buffer-visiting emacsconf-org-file)
+ (with-current-buffer (or (find-buffer-visiting emacsconf-org-file)
+ (find-file-noselect emacsconf-org-file))
(remove-hook 'org-after-todo-state-change-hook
#'emacsconf-org-after-todo-state-change t)))
(defvar emacsconf-todo-hooks
- '(emacsconf-stream-play-talk-on-change
- emacsconf-stream-open-qa-windows-on-change
+ '(;; emacsconf-stream-play-talk-on-change
+ ;; emacsconf-stream-open-qa-windows-on-change
emacsconf-erc-announce-on-change ;; announce via ERC
emacsconf-publish-bbb-redirect
emacsconf-stream-update-talk-info-on-change
@@ -1704,6 +1821,7 @@ tracks with the ID in the cdr of that list."
"Add NOTE as a logbook entry for the current subtree."
(move-marker org-log-note-return-to (point))
(move-marker org-log-note-marker (point))
+ (setq org-log-note-window-configuration (current-window-configuration))
(with-temp-buffer
(insert note)
(let ((org-log-note-purpose 'note))
@@ -1712,9 +1830,11 @@ 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)
+ (bury-buffer (current-buffer))))))
(defun emacsconf-reload ()
"Reload the emacsconf-el modules."
@@ -1861,5 +1981,19 @@ tracks with the ID in the cdr of that list."
(concat "ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "
(shell-quote-argument (expand-file-name filename)))))))
+(defun emacsconf-delete-from-all (files)
+ "Delete FILES from all the directories."
+ (interactive (list (dired-get-marked-files)))
+ (dolist (dir (list emacsconf-cache-dir emacsconf-backstage-dir
+ (expand-file-name "cache" emacsconf-res-dir)
+ emacsconf-public-media-directory))
+ (dolist (file files)
+ (when (and dir (file-exists-p (expand-file-name (file-name-nondirectory file)
+ dir)))
+ (delete-file (expand-file-name (file-name-nondirectory file)
+ dir))
+ (message "Deleting %s from %s"
+ (file-name-nondirectory file) dir)))))
+
(provide 'emacsconf)
;;; emacsconf.el ends here