From 0c9155ccee010dd2df3f6e6816c7325abfccee29 Mon Sep 17 00:00:00 2001 From: Sacha Chua Date: Sun, 8 Dec 2024 08:55:32 -0500 Subject: fix YOUTUBE_URL property name --- emacsconf-publish.el | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/emacsconf-publish.el b/emacsconf-publish.el index 4e906ab..31ff72b 100644 --- a/emacsconf-publish.el +++ b/emacsconf-publish.el @@ -2136,7 +2136,7 @@ This video is available under the terms of the Creative Commons Attribution-Shar (when (eq (read-char) ?q) (throw 'done t))) (emacsconf-set-property-from-slug (plist-get talk :slug) - "YOUTUBE" + "YOUTUBE_URL" (read-string (format "%s - YouTube URL: " (plist-get talk :scheduled)))))))) (defun emacsconf-publish-toobnix-step-through-publishing () @@ -2144,9 +2144,8 @@ This video is available under the terms of the Creative Commons Attribution-Shar (catch 'done (while t (let ((talk (seq-find (lambda (o) - (and (member (plist-get o :status) '("TO_STREAM" "TO_CHECK")) - (not (plist-get o :toobnix-url)) - (emacsconf-talk-file o "--main.webm"))) + (and (not (plist-get o :toobnix-url)) + (emacsconf-talk-file o "--main.webm"))) (emacsconf-publish-prepare-for-display (emacsconf-get-talk-info))))) (unless talk (message "All done so far.") @@ -2154,7 +2153,7 @@ This video is available under the terms of the Creative Commons Attribution-Shar (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) + (kill-new (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") -- cgit v1.2.3 From 574e83bd218859d10f1f421ed85d37449dfc18b7 Mon Sep 17 00:00:00 2001 From: Sacha Chua Date: Fri, 27 Dec 2024 10:16:51 -0500 Subject: add emacsconf-stream-populate-random-package-file --- emacsconf-stream.el | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/emacsconf-stream.el b/emacsconf-stream.el index 55c4648..6641392 100644 --- a/emacsconf-stream.el +++ b/emacsconf-stream.el @@ -1280,5 +1280,35 @@ International (CC BY-SA 4.0) license. Please observe the guidelines for conduct: (kill-new desc)) desc)) +(defun emacsconf-stream-populate-random-package-file () + (interactive) + (with-temp-file (expand-file-name "fortune.txt" emacsconf-cache-dir) + (dolist (entry + (emacsconf-stream-shuffle-list + (seq-mapcat (lambda (f) + (let ((base (file-name-base f))) + (mapcar + (lambda (entry) + (list base + (symbol-name (car entry)) + (elt (cdr entry) 2))) + (cdr + (with-temp-buffer + (insert-file-contents + (expand-file-name "archive-contents" f)) + (goto-char (point-min)) + (read (current-buffer))))))) + (directory-files + (expand-file-name "archives" package-user-dir) t + directory-files-no-dot-files-regexp)))) + (unless (bobp) (insert "\n%\n")) + (insert + (format "%s: %s (%s)" + (elt entry 1) + (elt entry 2) + (pcase (elt entry 0) + ("gnu" "GNU ELPA") + ("nongnu" "NonGNU ELPA") + ("melpa" "MELPA"))))))) (provide 'emacsconf-stream) ;;; emacsconf-stream.el ends here -- cgit v1.2.3 From 456fb8abbcc756422f106cb00af4066100a1bf22 Mon Sep 17 00:00:00 2001 From: Sacha Chua Date: Fri, 27 Dec 2024 10:31:57 -0500 Subject: add emacsconf-erc-copy --- emacsconf-erc.el | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/emacsconf-erc.el b/emacsconf-erc.el index b01e3f6..71b2478 100644 --- a/emacsconf-erc.el +++ b/emacsconf-erc.el @@ -547,5 +547,34 @@ Usage: /conflog keyword notes go here" (cons (cons (match-string-no-properties 1 string) (current-time)) (seq-take emacsconf-erc-recent-announcements (1- emacsconf-erc-recent-announcements-length)))))) +;; (keymap-set erc-mode-map "C-c w" #'emacsconf-erc-copy) +(defun emacsconf-erc-copy (&optional beg end) + "Unwrap and copy the current line to the clipboard. +This makes it easier to paste into the Etherpad." + (interactive + (list + (if (region-active-p) + (min (point) (mark))) + (if (region-active-p) + (max (point) (mark))))) + (setq beg (or beg (if (get-text-property (point) 'erc--ts) + (line-beginning-position) + (prop-match-beginning (text-property-search-backward 'erc--ts))))) + (setq end + (let ((end-field (save-excursion (text-property-search-forward 'field))) + (end-nick (save-excursion (text-property-search-forward 'erc--ts nil nil t)))) + (min (if end-field (prop-match-beginning end-field) most-positive-fixnum) + (if end-nick (prop-match-beginning end-nick) most-positive-fixnum)))) + (let* ((pulse-flag nil)) + (when (fboundp 'pulse-momentary-highlight-region) + (pulse-momentary-highlight-region beg end)) + (kill-new + (string-trim + (replace-regexp-in-string + "\n[ \t]+" " " + (buffer-substring-no-properties + beg + end)))))) + (provide 'emacsconf-erc) ;;; emacsconf-erc.el ends here -- cgit v1.2.3 From 9aecc040619a714c6f9af69680c46a2e8aad8e04 Mon Sep 17 00:00:00 2001 From: Sacha Chua Date: Tue, 7 Jan 2025 12:08:26 -0500 Subject: add youtube comment things --- emacsconf-extract.el | 325 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 244 insertions(+), 81 deletions(-) diff --git a/emacsconf-extract.el b/emacsconf-extract.el index efd2ade..5600494 100644 --- a/emacsconf-extract.el +++ b/emacsconf-extract.el @@ -247,6 +247,11 @@ (gethash "sentences" data))))) ;; (emacsconf-extract-qa-from-assemblyai-sentences "~/proj/emacsconf/rms/sentences") +(defun emacsconf-extract-unescape (s) + (replace-regexp-in-string + "\\\\\\(['\"]\\)" + "\\1" s)) + ;;;###autoload (defun emacsconf-extract-copy-pad-to-wiki () "Copy the notes and questions from the current file to the wiki page for this talk." @@ -268,12 +273,12 @@ nil (re-search-forward "-after)" nil t) (forward-line -1) - (insert "# Discussion\n\n")) - (save-excursion - (unless (string= (or questions "") "") - (insert "## Questions and answers\n\n" questions "\n\n")) - (unless (string= (or notes "") "") - (insert "## Notes\n\n" notes "\n\n"))))) + (insert "# Discussion\n\n") + (save-excursion + (unless (string= (or questions "") "") + (insert "## Questions and answers\n\n" (emacsconf-extract-unescape questions) "\n\n")) + (unless (string= (or notes "") "") + (insert "## Notes\n\n" (emacsconf-extract-unescape notes) "\n\n")))))) (defun emacsconf-extract-question-headings (slug) (with-temp-buffer @@ -308,7 +313,7 @@ "Question: " (emacsconf-extract-question-headings (emacsconf-get-slug-from-string (file-name-base (buffer-file-name))))))) - (insert "NOTE " question "\n\n")) + (subed-set-subtitle-comment (concat "Q: " question))) (defun emacsconf-extract-wget-bbb (o) (when (plist-get o :bbb-playback) @@ -355,7 +360,8 @@ (date-to-time (dom-text (dom-by-tag - (dom-elements dom 'eventname "StopRecordingEvent") + (or (dom-elements dom 'eventname "StopRecordingEvent") + (dom-elements dom 'eventname "EndAndKickAllEvent")) 'date)))) (setq start-ms (* 1000 (time-to-seconds start-recording)) stop-ms (* 1000 (time-to-seconds stop-recording))) @@ -428,11 +434,28 @@ (let ((results "")) (save-excursion (goto-char (point-min)) - (while (re-search-forward "^\\( *- \\([QA]: \\)?\\)\\[[0-9:]+\\] <.*?> \\(.*\n\\)" nil t) - (setq results (concat results (match-string 1) (match-string 3))) + (while (re-search-forward "^\\([qna] *\\| *- +\\([QA]: \\)?\\)\\[[0-9:]+\\] <.*?> \\(.*\n\\)" nil t) + (setq results (concat results + (save-match-data + (pcase (match-string 1) + ((rx "q") "- Q: ") + ((rx "a") "- A: ") + ((rx "n") "- ") + (_ "- "))) + (match-string 3))) (replace-match "" nil t nil 1)) (kill-new results)))) +(defvar-keymap emacsconf-extract-irc-log-map + "" #'forward-line + "" #'previous-line + "" (lambda () (interactive) (insert "-") (forward-line)) + "" #'forward-line) + +(defun emacsconf-extract-irc-log () + (interactive) + (set-transient-map emacsconf-extract-irc-log-map t)) + (defun emacsconf-extract-irc-backward-by-nick () (interactive) (goto-char (line-beginning-position)) @@ -503,29 +526,12 @@ (defun emacsconf-extract-irc-anonymize-log (beg end speakers) (interactive "r\nMNick(s): ") (when (stringp speakers) (setq speakers (split-string speakers))) - (let ((text (buffer-substring beg end)) - nicks) - (with-temp-buffer - (insert text) - (goto-char (point-min)) - ;; make a list of nicks - (while (re-search-forward "^\\[[0-9:]+\\] <\\(.*?\\)>" nil t) - (unless (member (match-string 1) speakers) - (add-to-list 'nicks (match-string 1)))) - (goto-char (point-min)) - (while (re-search-forward "^\\[[0-9:]+\\] <\\(.*?\\)> \\(.+\\)" nil t) - (replace-match - (if (member (match-string 1) speakers) - (concat " - A: " (match-string 2)) - (format "- {{%d}} %s" - (seq-position nicks (match-string 1)) - (propertize (match-string 2) - 'nick (match-string 1)))))) - (goto-char (point-min)) - (perform-replace (regexp-opt nicks) (lambda ())) - (setq text (buffer-string)) - (other-window 1) - (insert text)))) + (save-excursion + (goto-char beg) + (while (re-search-forward "^\\[[0-9:]+\\] <\\(.*?\\)> \\(.+\\)" end t) + (if (member (match-string 1) speakers) + (replace-match (concat "- " (match-string 1) ": " (match-string 2)) t t) + (replace-match (concat "- " (match-string 2)) t t))))) (defun emacsconf-private-qa (&optional info) (seq-remove (lambda (o) @@ -720,7 +726,7 @@ Would you like to help? See [[help_with_chapter_markers]] for more details. You (setq talk (emacsconf-resolve-talk talk)) (expand-file-name "events.xml" (expand-file-name (plist-get talk :bbb-meeting-id) emacsconf-extract-bbb-raw-dir))) -(defun emacsconf-extract-bbb-report () +(defun emacsconf-extract-bbb-report (&optional event-xml-files) (let* ((max 0) (participant-count 0) (meeting-count 0) @@ -730,39 +736,41 @@ Would you like to help? See [[help_with_chapter_markers]] for more details. You (meeting-events (sort (seq-mapcat - (lambda (talk) - (when (plist-get talk :bbb-meeting-id) - (let ((dom (xml-parse-file (emacsconf-extract-bbb-raw-events-file-name talk))) - participants talking meeting-events) - (mapc (lambda (o) - (pcase (dom-attr o 'eventname) - ("ParticipantJoinEvent" - (cl-pushnew (cons (dom-text (dom-by-tag o 'userId)) - (dom-text (dom-by-tag o 'name))) - participants) - (push (cons (string-to-number (dom-text (dom-by-tag o 'timestampUTC))) - (dom-attr o 'eventname)) - meeting-events)) - ("ParticipantLeftEvent" - (when (string= (dom-attr o 'module) "PARTICIPANT") - (push (cons (string-to-number (dom-text (dom-by-tag o 'timestampUTC))) - (dom-attr o 'eventname)) - meeting-events))) - ("ParticipantTalkingEvent" - (cl-pushnew (assoc-default (dom-text (dom-by-tag o 'participant)) participants) talking)) - ((or - "CreatePresentationPodEvent" - "EndAndKickAllEvent") + (lambda (file) + (let ((dom (xml-parse-file file)) + participants talking meeting-events) + (mapc (lambda (o) + (pcase (dom-attr o 'eventname) + ("ParticipantJoinEvent" + (cl-pushnew (cons (dom-text (dom-by-tag o 'userId)) + (dom-text (dom-by-tag o 'name))) + participants) + (push (cons (string-to-number (dom-text (dom-by-tag o 'timestampUTC))) + (dom-attr o 'eventname)) + meeting-events)) + ("ParticipantLeftEvent" + (when (string= (dom-attr o 'module) "PARTICIPANT") (push (cons (string-to-number (dom-text (dom-by-tag o 'timestampUTC))) (dom-attr o 'eventname)) - meeting-events)))) - (dom-search dom (lambda (o) (dom-attr o 'eventname)))) - (cl-pushnew (list :slug (plist-get talk :slug) - :participants participants - :talking talking) - meeting-participants) - meeting-events))) - (emacsconf-get-talk-info)) + meeting-events))) + ("ParticipantTalkingEvent" + (cl-pushnew (assoc-default (dom-text (dom-by-tag o 'participant)) participants) talking)) + ((or + "CreatePresentationPodEvent" + "EndAndKickAllEvent") + (push (cons (string-to-number (dom-text (dom-by-tag o 'timestampUTC))) + (dom-attr o 'eventname)) + meeting-events)))) + (dom-search dom (lambda (o) (dom-attr o 'eventname)))) + (cl-pushnew (list ;; :slug (plist-get talk :slug) + :participants participants + :talking talking) + meeting-participants) + meeting-events)) + (or event-xml-files + (mapcar #'emacsconf-extract-bbb-raw-events-file-name + (seq-filter (lambda (talk) (plist-get talk :bbb-meeting-id)) + (emacsconf-get-talk-info))))) (lambda (a b) (< (car a) (car b)))))) (dolist (event meeting-events) (pcase (cdr event) @@ -794,13 +802,8 @@ Would you like to help? See [[help_with_chapter_markers]] for more details. You (expand-file-name (plist-get talk :bbb-meeting-id) emacsconf-extract-bbb-published-dir)))) -(defun emacsconf-extract-bbb-parse-events (talk) - "Parse events TALK from raw recordings. -This works with the events.xml from /var/bigbluebutton/raw. -Files should be downloaded to `emacsconf-extract-bbb-raw-dir'." - (setq talk (emacsconf-resolve-talk talk)) - (let* ((xml-file (emacsconf-extract-bbb-raw-events-file-name talk)) - (dom (xml-parse-file xml-file)) +(defun emacsconf-extract-bbb-parse-events (xml-file) + (let* ((dom (xml-parse-file xml-file)) (meeting-name (dom-attr (dom-by-tag dom 'metadata) 'meetingName)) (meeting-id (dom-attr dom 'meeting_id)) (conf-joined (dom-search dom (lambda (o) (and (string= (dom-tag o) "name") (string= (dom-text o) emacsconf-extract-conference-username))))) @@ -886,6 +889,37 @@ Files should be downloaded to `emacsconf-extract-bbb-raw-dir'." (talking . ,(nreverse talking)) (chat . ,(nreverse chat))))) +(defun emacsconf-extract-bbb-talking-report (meeting-xml) + (let ((data (emacsconf-extract-bbb-parse-events meeting-xml))) + (unless (string= "" (alist-get 'meeting-date data)) + (format "- %s %s: %s\n" + (alist-get 'name data) + (format-time-string "%a %I:%M %p" + (date-to-time + (alist-get 'meeting-date data))) + (mapconcat + (lambda (person) + (format "%s (%s)" + (car person) + (/ (cdr person) 60000))) + (sort + (mapcar + (lambda (group) + (cons + (car group) + (apply '+ (mapcar (lambda (o) (- (elt o 2) (elt o 1))) (cdr group))))) + (seq-group-by 'car (alist-get 'talking data))) + :key 'cdr + :reverse t) + ", "))))) + +(defun emacsconf-extract-bbb-parse-events-for-talk (talk) + "Parse events TALK from raw recordings. +This works with the events.xml from /var/bigbluebutton/raw. +Files should be downloaded to `emacsconf-extract-bbb-raw-dir'." + (setq talk (emacsconf-resolve-talk talk)) + (emacsconf-extract-bbb-parse-events (emacsconf-extract-bbb-raw-events-file-name talk))) + (defun emacsconf-extract-bbb-format-chat () (mapconcat (lambda (events) @@ -1127,6 +1161,10 @@ Strategies: ;; To avoid being prompted for the client secret, it's helpful to have a line in ~/.authinfo or ~/.authinfo.gpg with ;; machine https://oauth2.googleapis.com/token username CLIENT_ID password CLIENT_SECRET +;; reset: +;; (setq url-http-oauth--interposed nil url-http-oauth--interposed-regexp nil) +;; and remove the token from ~/.authinfo + (defvar emacsconf-extract-google-client-identifier nil) (defvar emacsconf-extract-youtube-api-channels nil) (defvar emacsconf-extract-youtube-api-categories nil) @@ -1154,7 +1192,7 @@ Strategies: ("access_type" . "offline") ("prompt" . "consent"))) ("access-token-endpoint" . "https://oauth2.googleapis.com/token") - ("scope" . "https://www.googleapis.com/auth/youtube") + ("scope" . "https://www.googleapis.com/auth/youtube https://www.googleapis.com/auth/youtube.force-ssl https://www.googleapis.com/auth/youtube.upload") ("client-secret-method" . prompt)))) (setq emacsconf-extract-youtube-api-channels (plz 'get "https://youtube.googleapis.com/youtube/v3/channels?part=contentDetails&mine=true" @@ -1180,6 +1218,106 @@ Strategies: (string-match (regexp-quote emacsconf-year) (let-alist item .snippet.title)))))) +(defun emacsconf-extract-youtube-comment-list () + (seq-mapcat + (lambda (item) + (append + (if (alist-get 'topLevelComment (alist-get 'snippet item)) + (list (alist-get 'topLevelComment (alist-get 'snippet item)))) + (alist-get 'comments (alist-get 'replies item)))) + (alist-get + 'items + (or emacsconf-extract-youtube-comments (emacsconf-extract-youtube-get-channel-comments))))) + +(defun emacsconf-extract-youtube-get-talk-for-video-id (video-id) + (seq-find (lambda (o) + (or (string-match (regexp-quote video-id) (or (plist-get o :youtube-url) "")) + (string-match (regexp-quote video-id) (or (plist-get o :qa-youtube-url) "")))) + (emacsconf-get-talk-info))) + +(defun emacsconf-extract-youtube-comments-after (date) + (interactive (list (org-read-date nil t nil "On or after date: "))) + (when (stringp date) + (setq date (org-read-date nil t date))) + (seq-filter + (lambda (entry) + (time-less-p + date + (date-to-time + (alist-get + 'publishedAt + (alist-get 'snippet entry))))) + (emacsconf-extract-youtube-comment-list))) + +(defun emacsconf-extract-youtube-format-talk-comments (videos) + (mapconcat + (lambda (video) + (format + "- https://youtu.be/%s\n%s\n" + (car video) + (mapconcat + (lambda (comment) + (let-alist comment + (format + " - %s: %s\n" + .snippet.authorDisplayName + (replace-regexp-in-string "\n" "\n " .snippet.textOriginal)))) + (cdr video) + ""))) + videos + "")) + +(defun emacsconf-extract-youtube-comments-by-talk (&optional comments) + (interactive (list + (if current-prefix-arg (emacsconf-extract-youtube-comments-after (org-read-date nil nil nil "Date: "))))) + (setq comments (or comments (emacsconf-extract-youtube-comment-list))) + (let ((by-talk + (seq-group-by + (lambda (group) + (plist-get (emacsconf-extract-youtube-get-talk-for-video-id (car group)) :slug)) + (seq-group-by (lambda (o) + (alist-get 'videoId (alist-get 'snippet o))) + comments)))) + (when (called-interactively-p 'any) + (with-current-buffer (get-buffer-create "*comments*") + (erase-buffer) + (org-mode) + (dolist (group by-talk) + (when (car group) + (insert (format + "* %s\n\n%s\n\n" + (org-link-make-string + (concat "file:" + (expand-file-name + (concat + (car group) ".md") + (expand-file-name + "talks" + (expand-file-name + emacsconf-year + emacsconf-directory)))) + (car group)) + (emacsconf-extract-youtube-format-talk-comments (cdr group)))))) + (display-buffer (current-buffer)))) + by-talk)) + + +;; (emacsconf-extract-youtube-comment-list) + +;; (emacsconf-extract-youtube-comments-after "-2mon") + +(defvar emacsconf-extract-youtube-comments nil) +(defun emacsconf-extract-youtube-get-channel-comments (&optional no-cache) + (setq + emacsconf-extract-youtube-comments + (or (and emacsconf-extract-youtube-comments (not no-cache)) + (plz 'get + (format + "https://youtube.googleapis.com/youtube/v3/commentThreads?part=snippet,replies&allThreadsRelatedToChannelId=%s&maxResults=100" + (alist-get 'id (car (alist-get 'items emacsconf-extract-youtube-api-channels)))) + :headers `(("Authorization" . ,(url-oauth-auth "https://youtube.googleapis.com/youtube/v3/"))) + :as #'json-read)))) + (defvar emacsconf-extract-youtube-tags '("emacs" "emacsconf")) (defun emacsconf-extract-youtube-object (video-id talk &optional privacy-status qa) "Format the video object for VIDEO-ID using TALK details. @@ -1206,19 +1344,27 @@ If QA is non-nil, treat it as a Q&A video." (let-alist video-object (cond ;; not yet renamed - ((string-match (rx (literal emacsconf-id) " " (literal emacsconf-year) " " - (group (1+ (or (syntax word) "-"))) - " ") - .snippet.title) + ((and .snippet.title (string-match (rx (literal emacsconf-id) " " (literal emacsconf-year) " " + (group (1+ (or (syntax word) "-"))) + " ") + .snippet.title)) (match-string 1 .snippet.title)) ;; renamed, match the description instead - ((string-match (rx (literal emacsconf-base-url) (literal emacsconf-year) "/talks/" - (group (1+ (or (syntax word) "-")))) - .snippet.description) + ((and .snippet.description + (string-match (rx (literal emacsconf-base-url) (literal emacsconf-year) "/talks/" + (group (1+ (or (syntax word) "-")))) + .snippet.description)) (match-string 1 .snippet.description)) (t (plist-get - (seq-find (lambda (o) (string-match (regexp-quote .snippet.resourceId.videoId) (or (plist-get o :youtube-url) ""))) + (seq-find (lambda (o) + (or + (string-match (regexp-quote (or .snippet.videoId + .snippet.resourceId.videoId)) + (or (plist-get o :youtube-url) "")) + (string-match (regexp-quote (or .snippet.videoId + .snippet.resourceId.videoId)) + (or (plist-get o :qa-youtube-url) "")))) (emacsconf-get-talk-info)) :slug))))) @@ -1261,6 +1407,7 @@ If QA is non-nil, treat it as a Q&A video." (if (<= num-pages 0) (setq url null)))) result)) + (defun emacsconf-extract-youtube-api-update-video (video-object &optional qa) "Update VIDEO-OBJECT. If QA is non-nil, treat it as a Q&A video." @@ -1419,6 +1566,8 @@ If QA is non-nil, treat it as a Q&A video." :as #'json-read)) nil))))) + + (defun emacsconf-extract-youtube-duration-msecs (video) (let-alist video (when-let ((duration .contentDetails.duration)) @@ -1672,5 +1821,19 @@ Call with a prefix arg to store the URL as Q&A." url))))) +(defun emacsconf-extract-subed-copy-section-text () + (interactive) + (save-excursion + (subed-copy-region-text + (unless (looking-at "^NOTE") + (if (re-search-backward "^NOTE" nil t) + (point) + (point-min))) + (progn + (forward-line) + (if (re-search-forward "^NOTE" nil t) + (match-beginning 0) + (point-max)))))) + (provide 'emacsconf-extract) ;;; emacsconf-extract.el ends here -- cgit v1.2.3 From 4b4566625fa3d03cc71b1fb5fcb0e05a380d9cab Mon Sep 17 00:00:00 2001 From: Sacha Chua Date: Tue, 7 Jan 2025 12:08:38 -0500 Subject: tweak pad --- emacsconf-pad.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/emacsconf-pad.el b/emacsconf-pad.el index 6a79d45..2209b6d 100644 --- a/emacsconf-pad.el +++ b/emacsconf-pad.el @@ -366,7 +366,7 @@ ${next-talk-list} (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))))))) + :checkin-pad (concat emacsconf-pad-base "private-" emacsconf-private-pad-prefix "-checkin-" (downcase (format-time-string "%a" (date-to-time (plist-get shift :start))))))) (shift-talks (mapcar (lambda (o) (append prefixed o)) (seq-filter @@ -498,6 +498,7 @@ ${next-talk-list}
  • [ ] Window or screen can be shared
  • [ ] Text is readable
  • [ ] Webcam sharing (optional)
  • +
  • [ ] What kind of facilitation would the speaker like? (Host reads questions, chats a lot, etc.)
  • OK to do other things until going live at ${live}
  • People will add questions to the pad or IRC channel; host can read them to you, or you can read them
  • You can answer questions in any order, and you can skip questions if you want. Feel free to take your time to think about answers or to save some for following up later
  • -- cgit v1.2.3 From 6b40f1675ca9d9a71e79eedcfeed8d1057482af9 Mon Sep 17 00:00:00 2001 From: Sacha Chua Date: Tue, 7 Jan 2025 12:10:58 -0500 Subject: add pronouns and pronunciation to pad --- emacsconf-pad.el | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/emacsconf-pad.el b/emacsconf-pad.el index 2209b6d..0a958e1 100644 --- a/emacsconf-pad.el +++ b/emacsconf-pad.el @@ -177,6 +177,8 @@ You can find it in $ETHERPAD_PATH/APIKEY.txt" "\n") "") "") + :pronouns (emacsconf-surround " (" (plist-get o :pronouns) ")" "") + :pronunciation (emacsconf-surround " - Pronunciation: " (plist-get o :pronunciation) "" "") :track-id (plist-get (emacsconf-get-track (plist-get o :track)) :id) :watch @@ -198,7 +200,7 @@ You can find it in $ETHERPAD_PATH/APIKEY.txt" "
    All talks: ${talks}
    ${title}
    -
    ${base-url}${url} - ${speakers} - Track: ${track}
    +
    ${base-url}${url} - ${speakers}${pronouns}${pronunciation} - Track: ${track}
    Watch/participate: ${watch}
    ${bbb-info}
    IRC: ${irc-nick-details} https://chat.emacsconf.org/#/connect?join=emacsconf,emacsconf-${track-id} or #emacsconf-${track-id} on libera.chat network
    -- cgit v1.2.3 From ca01649acc6ea66ca75e6e45660107d57d0e306e Mon Sep 17 00:00:00 2001 From: Sacha Chua Date: Wed, 25 Jun 2025 09:07:22 -0400 Subject: update dates --- emacsconf.el | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/emacsconf.el b/emacsconf.el index fe2a30c..0aee6c5 100644 --- a/emacsconf.el +++ b/emacsconf.el @@ -34,20 +34,20 @@ "Name of conference" :group 'emacsconf :type 'string) -(defcustom emacsconf-year "2024" +(defcustom emacsconf-year "2025" "Conference year. String for easy inclusion." :group 'emacsconf :type 'string) -(defcustom emacsconf-cfp-deadline "2024-09-20" "Deadline for proposals." +(defcustom emacsconf-cfp-deadline "2025-09-19" "Target date for proposals." :group 'emacsconf :type 'string) -(defcustom emacsconf-date "2024-12-07" "Starting date of EmacsConf." +(defcustom emacsconf-date "2025-12-06" "Starting date of EmacsConf." :group 'emacsconf :type 'string) -(defcustom emacsconf-video-target-date "2024-11-08" "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 "2024-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 'conference +(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." @@ -1024,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)) @@ -1465,7 +1460,7 @@ If TIMEZONES is a string, split it by commas." :vnc-display ":5" :vnc-port "5905" :autopilot crontab - :status "offline") + :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" @@ -1544,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 "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"))) +(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." -- cgit v1.2.3 From 25cc4d1a55bf55ccbabe08bd79e8b766f6d1e080 Mon Sep 17 00:00:00 2001 From: Sacha Chua Date: Wed, 25 Jun 2025 09:07:49 -0400 Subject: switch to unsent mail buffer if there's been an error --- emacsconf-mail.el | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/emacsconf-mail.el b/emacsconf-mail.el index 23743e8..ee6fb88 100644 --- a/emacsconf-mail.el +++ b/emacsconf-mail.el @@ -253,7 +253,9 @@ insert into the current buffer instead of drafting e-mails." (add-hook 'message-sent-hook `(lambda () (save-window-excursion - (emacsconf-add-to-talk-logbook ,(plist-get o :slug) ,message))) + (emacsconf-add-to-talk-logbook ,(plist-get o :slug) ,message)) + (when (match-buffers "*unsent") + (switch-to-buffer (car (match-buffers "*unsent"))))) nil t)) (defun emacsconf-mail-group-by-email (&optional info) -- cgit v1.2.3 From 78ade030f63d34df0fe0b2c90de330d61085f5fb Mon Sep 17 00:00:00 2001 From: Sacha Chua Date: Wed, 25 Jun 2025 09:08:03 -0400 Subject: mail answers for review --- emacsconf-mail.el | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/emacsconf-mail.el b/emacsconf-mail.el index ee6fb88..1e70cbf 100644 --- a/emacsconf-mail.el +++ b/emacsconf-mail.el @@ -1076,6 +1076,60 @@ ${captions} :captions (mapconcat (lambda (sub) (concat (emacsconf-surround "\nNOTE " (elt sub 4) "\n\n" "") (elt sub 3))) (subed-parse-file captions) "\n"))) (mml-attach-file captions "text/vtt" "Subtitles" "attachment"))) +(defun emacsconf-mail-answers-for-review (talk) + "E-mail Q&A session for TALK so that the speakers can review them." + (interactive (list (emacsconf-complete-talk-info + (seq-filter + (lambda (talk) + (and (emacsconf-talk-file talk "--answers.vtt") + (file-exists-p (emacsconf-talk-file talk "--answers.vtt")) + (not (plist-get talk :qa-public)))) + (emacsconf-get-talk-info))))) + (let ((captions (expand-file-name (concat (plist-get talk :file-prefix) "--answers.vtt") + emacsconf-cache-dir)) + (captioner-info + (with-current-buffer (find-file-noselect emacsconf-org-file) + (org-entry-properties (org-find-property "CUSTOM_ID" (plist-get talk :captioner)))))) + (emacsconf-mail-prepare + (list + :subject "${conf-name} ${year}: Q&A for ${title}" + :to "${email}" + :log-note "sent q&a for review" + :body "${email-notes}Hi ${speakers-short}! + +Thank you for speaking at ${conf-name} ${year}! We're working on getting +the Q&A recordings out the door. We noticed you had a long Q&A session +that continued off-stream. Was there anything that would need to be +removed before we can publish the recording? You can review it at ${url} +(video is in --answers.webm, captions are in --answers.vtt). I've also +attached the automatic captions for easy skimming. In the interests of +getting stuff out the door quickly, we haven't edited the Q&A captions +much; it's mostly there so you can remember the conversation and let us +know if we need to trim anything.${wrap} + +${signature} + +${captions} +") + (plist-get talk :email) + (list + :email-notes (emacsconf-surround "ZZZ: " (plist-get talk :email-notes) "\n\n" "") + :conf-name emacsconf-name + :speakers-short (plist-get talk :speakers-short) + :year emacsconf-year + :email (plist-get talk :email) + :title (plist-get talk :title) + :signature user-full-name + :url + (format "https://%s:%s@media.emacsconf.org/%s/backstage/#%s" + emacsconf-backstage-user + emacsconf-backstage-password + emacsconf-year + (plist-get talk :slug)) + :password emacsconf-backstage-password + :captions (mapconcat (lambda (sub) (concat (emacsconf-surround "\nNOTE " (elt sub 4) "\n\n" "") (elt sub 3))) (subed-parse-file captions) "\n"))) + (mml-attach-file captions "text/vtt" "Subtitles" "attachment"))) + (defun emacsconf-mail-upload-and-backstage-info (group) "E-mail upload and backstage access information to GROUP." (interactive (list (emacsconf-mail-complete-email-group))) -- cgit v1.2.3 From 06665ca2de71934e0036061e3b0622270879a7cf Mon Sep 17 00:00:00 2001 From: Sacha Chua Date: Wed, 25 Jun 2025 09:08:16 -0400 Subject: rename ftp-upload to upload --- emacsconf-mail.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/emacsconf-mail.el b/emacsconf-mail.el index 1e70cbf..1220567 100644 --- a/emacsconf-mail.el +++ b/emacsconf-mail.el @@ -1180,7 +1180,7 @@ ${signature}") :backstage-user emacsconf-backstage-user :backstage-password emacsconf-backstage-password :upload-url - (concat "https://ftp-upload.emacsconf.org/?sid=" + (concat "https://upload.emacsconf.org/?sid=" emacsconf-upload-password "-" (mapconcat (lambda (o) (plist-get o :slug)) (cdr group) "-")) @@ -1492,7 +1492,7 @@ ${signature}")) (plist-get talk :slug))) (cdr group) " , ") - :upload-url (concat "https://ftp-upload.emacsconf.org/?sid=" + :upload-url (concat "https://upload.emacsconf.org/?sid=" emacsconf-upload-password "-" (mapconcat (lambda (o) (plist-get o :slug)) (cdr group) "-")) -- cgit v1.2.3 From 6cab8c760be9b4d8604f66c1ada16c4e42cc294e Mon Sep 17 00:00:00 2001 From: Sacha Chua Date: Wed, 25 Jun 2025 09:08:32 -0400 Subject: mail thanks --- emacsconf-mail.el | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/emacsconf-mail.el b/emacsconf-mail.el index 1220567..0d105fd 100644 --- a/emacsconf-mail.el +++ b/emacsconf-mail.el @@ -1831,7 +1831,7 @@ Sacha") (cdr group) " , ") :email-notes (emacsconf-surround "ZZZ: " (plist-get (cadr group) :email-notes) "\n\n" "")))) -(defun emacsconf-mail-template-speakers-thanks-after-conferences () +(defun emacsconf-mail-template-speakers-thanks-after-conference () (interactive) (let* ((log-note "sent thanks to speaker after conference") (groups @@ -1844,8 +1844,8 @@ Sacha") (emacsconf-mail-prepare (list :subject "Thanks for speaking at ${conf-name} ${year}!" - :reply-to "emacsconf-submit@gnu.org, ${email}, ${user-email}" - :mail-followup-to "emacsconf-submit@gnu.org, ${email}, ${user-email}" + :reply-to "${user-email}" + :mail-followup-to "${user-email}" :log-note log-note :body "${email-notes}Hi, ${speakers-short}! @@ -1854,18 +1854,22 @@ Thank you so much for being part of ${conf-name} ${year}! Hundreds of people enjoyed it, and I'm sure even more will come across the videos in the days to follow. -Your videos are available on the talk page at ${talk-urls} , and -we've added the questions and comments that we've collected from -IRC/BBB/Etherpad. For your convenience, I've also included them below. +We've added the questions and comments that we've collected from +IRC/BBB/Etherpad to ${talk-urls} . For your convenience, I've also +included them below. You can edit the wiki directly or e-mail me +anything you'd like me to add.${wrap} -Your videos are also available on YouTube and Toobnix at: +Videos are also available on YouTube and Toobnix at: ${video-urls} - -If you want to reupload the video to your own channel, feel free -to do so. If you let me know where you've uploaded it, I can -switch our playlist to include your version of the video -instead. That way, it might be easier for you to respond to +I'm waiting for people to check the audio of the Q&A videos and +renormalize them if needed before I upload those to YouTube and Toobnix, +but the Q&A videos are already available on the talk pages at ${wiki} +along with chapter indices and rough transcripts. + +If you want to reupload the video to your own channel, feel free to do +so. If you like, I can switch our playlist to include your version of +the video instead. That way, it might be easier for you to respond to comments on videos. If you would like to share more resources or add more answers to @@ -1886,6 +1890,7 @@ ${feedback} :speakers-short (plist-get (cadr group) :speakers-short) :conf-name emacsconf-name :year emacsconf-year + :wiki (concat emacsconf-base-url emacsconf-year "/talks/") :talk-urls (mapconcat (lambda (talk) -- cgit v1.2.3 From 6d95a82f170ab80724eb6eeac5275db06b25aaf7 Mon Sep 17 00:00:00 2001 From: Sacha Chua Date: Wed, 25 Jun 2025 09:08:39 -0400 Subject: mailing address --- emacsconf-mail.el | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) diff --git a/emacsconf-mail.el b/emacsconf-mail.el index 0d105fd..7c0966b 100644 --- a/emacsconf-mail.el +++ b/emacsconf-mail.el @@ -2075,6 +2075,147 @@ ${signature} (emacsconf-mail-template-mailing-address group)) (message "Drafted %d messages" (length groups)))) +(defun emacsconf-mail-template-ask-volunteer-for-mailing-address (volunteer) + (interactive (list (emacsconf-complete-volunteer))) + (emacsconf-mail-prepare + (list + :subject "${conf-name} ${year}: Thank you! Can we send you a sticker or pin of appreciation?" + :reply-to "${user-email}, ${sticker-mailer}, ${email}" + :mail-followup-to "${user-email}, ${sticker-mailer}, ${email}" + :body + "Hi, ${name-short}! + +${email-notes} + +We have swag this year, thanks to Corwin +Brust! Would you like a sticker or a pin as a +small token of our appreciation? This is what they +look like: + +https://bru.st/i/ecswag.jpg + +(It's also part of our Evil Plan: maybe people +will see the sticker or the pin and talk to you +about Emacs! =) ) + +If you want one, please e-mail your mailing +address and your preference* (sticker or pin) to +corwin@bru.st . We promise to use your address +only for sending it. + +(* While supplies last; Corwin thinks there should +be plenty, but just in case, feel free to send us +your second choice too.) + +Thank you so much for contributing to ${conf-name} ${year}! + +${signature} +") + (assoc-default "EMAIL" volunteer) + (list + :email-notes + (emacsconf-surround + "ZZZ: " + (replace-regexp-in-string + ":volunteer" "" + (assoc-default "ALLTAGS" volunteer)) + "\n\n" "") + :name-short (assoc-default "NAME_SHORT" volunteer) + :conf-name emacsconf-name + :year emacsconf-year + :email (assoc-default "EMAIL" volunteer) + :base-url emacsconf-base-url + :signature user-full-name + :user-email user-mail-address + :sticker-mailer emacsconf-sticker-mailer))) + +(defun emacsconf-mail-template-mail-youtube-comments (group) + "Send more YouTube comments." + (interactive (list (emacsconf-mail-complete-email-group + (seq-filter + (lambda (o) + (and + (or + (emacsconf-talk-file o "--answers--original.vtt") + (emacsconf-talk-file o "--original.vtt")) + (not (string-match "Asked for permission regarding the rest of the Q&A" + (plist-get o :logbook))))) + (emacsconf-get-talk-info))))) + (emacsconf-mail-prepare + (list + :subject "${conf-name} ${year}: May we post the rest of the Q&A?" + :reply-to "emacsconf-submit@gnu.org, ${email}, ${user-email}" + :mail-followup-to "emacsconf-submit@gnu.org, ${email}, ${user-email}" + :log-note "Asked for permission regarding the rest of the Q&A" + :body + "${email-notes}Hi, ${speakers-short}! + +We're experimenting with a new harvesting workflow for live and +Q&A videos this year to make things more predictable for speakers +and participants. Sometimes people have so much fun chatting +after the talk that they might forget that the recording for the +session Q&A will be posted for other people to learn from. + +I've trimmed your online videos to roughly when the host left the +BigBlueButton room. There was lots of great discussion +afterwards, though, so I'd love to include the rest of it if +that's okay with you. To make it easier for you to review that +part or reuse what you shared in the Q&A session, I've included +an automatically-generated transcript for the whole Q&A +session. I've indicated the section that got trimmed out of the +published recording with \"NOTE Start of section to review\" in +the transcript. You can watch the session at ${bbb-recording-url} . + +- Option A: We could post the rest of the Q&A as is, which lets + people listen to the conversation and learn from it + +- Option B: We can keep the published Q&A video to just the part + that the host was in, and either you or I can go over the + transcript to pull out interesting notes for the summary or for + other posts + +What do you think? + +${signature} +---- +${transcript} +") + (car group) + (list + :email-notes (emacsconf-surround "ZZZ: " (string-join (seq-uniq (seq-map (lambda (talk) (plist-get talk :email-notes)) (cdr group))) + ", ") "\n\n" "") + :speakers-short (plist-get (cadr group) :speakers-short) + :conf-name emacsconf-name + :year emacsconf-year + :bbb-recording-url + (mapconcat + (lambda (talk) + (plist-get talk :bbb-rec)) + (cdr group) + " , ") + :signature user-full-name + :email (car group) + :transcript + (mapconcat + (lambda (talk) + (concat + (plist-get talk :title) "\n\n" + (mapconcat + (lambda (sub) + (concat (emacsconf-surround "\nNOTE " (elt sub 4) "\n\n" "") + (elt sub 3))) + (subed-parse-file (or (emacsconf-talk-file talk "--answers--original.vtt") + (emacsconf-talk-file talk "--original.vtt"))) "\n"))) + (cdr group) + "----") + :user-email user-mail-address)) + (dolist (talk (cdr group)) + (mml-attach-file (or (emacsconf-talk-file talk "--answers--original.vtt") + (emacsconf-talk-file talk "--original.vtt")) + "text/vtt" + (concat "Automatic captions for " (plist-get talk :title)) + "attachment"))) + ;;; Other mail functions (defun emacsconf-mail-verify-delivery (subject &optional groups) -- cgit v1.2.3 From 207928b737929a0d221ba48c704974cce8ac6cd9 Mon Sep 17 00:00:00 2001 From: Sacha Chua Date: Wed, 25 Jun 2025 09:09:34 -0400 Subject: tweak video ID --- emacsconf-publish.el | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/emacsconf-publish.el b/emacsconf-publish.el index 31ff72b..24c364f 100644 --- a/emacsconf-publish.el +++ b/emacsconf-publish.el @@ -30,7 +30,7 @@ :type 'string :group 'emacsconf) -(defcustom emacsconf-main-extensions '("--main.webm" "--main.opus" "--main.org" ".org" ".odp" ".pdf" ".pptx" ".el" "--compressed56.webm" "--main.vtt" "--main_fr.vtt" "--main_ja.vtt" "--main_es.vtt" "--main--chapters.vtt" "--script.fountain" "--main.pdf" "--slides.pdf") +(defcustom emacsconf-main-extensions '("--main.webm" "--main.opus" "--main.org" ".org" ".odp" ".pdf" ".pptx" ".el" "--compressed56.webm" "--main.vtt" "--main_fr.vtt" "--main_ja.vtt" "--main_es.vtt" "--main--chapters.vtt" "--script.fountain" "--main.pdf" "--slides.pdf" "--answers.vtt" "--answers.webm") "Extensions to list on public pages." :type '(repeat string) :group 'emacsconf) @@ -145,13 +145,14 @@ (video (and file-prefix (emacsconf-publish-index-card-video (or (plist-get talk :video-id) - (concat (plist-get talk :slug) "-mainVideo")) + (concat "mainVideo-" (plist-get talk :slug))) video-file talk)))) ;; Add extra information to the talk (setq talk (append talk (list + :video-type (or (plist-get talk :video-type) "mainVideo") :time-info (emacsconf-surround "Duration: " (plist-get talk :video-duration) " minutes" "") :video-html (or (plist-get video :video) "") :audio-html (or (plist-get video :audio) "") @@ -161,7 +162,7 @@ :speaker-info (or (plist-get talk :speakers-with-pronouns) "")))) (emacsconf-replace-plist-in-string talk - "
    ${video-html}${audio-html}
    ${extra}
    ${time-info}${resources}${chapter-list}
    "))) + "
    ${video-html}${audio-html}
    ${extra}
    ${time-info}${resources}${chapter-list}
    "))) ;; (emacsconf-publish-format-track-as-org (car emacsconf-tracks) "US/Eastern") ;; (emacsconf-get-talk-info) @@ -364,10 +365,10 @@ (list :source-src (when (stringp video-file) - (if (plist-get talk :public) - (format "%s%s/%s" emacsconf-media-base-url (plist-get talk :conf-year) - (file-name-nondirectory video-file)) - (file-name-nondirectory video-file))) + (if (plist-get talk (if (string-match "--answers" video-file) :qa-public :public)) + (format "%s%s/%s" emacsconf-media-base-url (plist-get talk :conf-year) + (file-name-nondirectory video-file)) + (file-name-nondirectory video-file))) :captions (or (and (stringp video-file) @@ -428,6 +429,11 @@ (concat "
  • " s "
  • ")) (emacsconf-publish-link-file-formats-as-list talk) "") + :youtube-info (if (plist-get talk :youtube-url) + (format + "
  • View on Youtube
  • " + (plist-get talk :youtube-url)) + "") :toobnix-info (if (plist-get talk :toobnix-url) (format "
  • View on Toobnix
  • " @@ -457,7 +463,7 @@ :resources (emacsconf-replace-plist-in-string info - "
      ${links}${other-files}${toobnix-info}
    ")))) + "
      ${links}${other-files}${toobnix-info}${youtube-info}
    ")))) (defun emacsconf-publish-format-public-email (o &optional email) (format "[%s](mailto:%s?subject=%s)" @@ -563,8 +569,10 @@ ${categories} (emacsconf-publish-index-card (append (list :public 1 - :video-id (concat (plist-get o :slug) "-qanda") - :toobnix-url nil + :video-type "qanda" + :video-id (concat "qanda-" (plist-get o :slug)) + :youtube-url (plist-get o :qa-youtube) + :toobnix-url (plist-get o :qa-toobnix) :captions-edited (plist-get o :qa-captions-edited) :caption-file (emacsconf-talk-file o "--answers.vtt") :video-file (emacsconf-talk-file o "--answers.webm") @@ -763,6 +771,7 @@ This includes the intro note, the schedule, and talk resources." (defun emacsconf-publish-format-transcript (talk &optional video-id lang title) "Format the transcript for TALK, adding paragraph markers when possible." (require 'subed) + (setq video-id (or video-id "mainVideo")) (let* ((subtitles (subed-parse-file (if lang (format "%s_%s.vtt" @@ -771,14 +780,14 @@ This includes the intro note, the schedule, and talk resources." lang) (plist-get talk :caption-file))))) (if subtitles - (format " -# %s%s + (format "

    %s%s

    %s -" +
    " + (if video-id (concat " transcript-" video-id) "") (plist-get talk :slug) - (or video-id "mainVideo") + video-id (emacsconf-surround "-" lang "" "") (if lang (assoc-default lang emacsconf-publish-subtitle-languages) (or title "Transcript")) (if (emacsconf-captions-edited-p (plist-get talk :caption-file)) -- cgit v1.2.3 From a611b8ed3f7b2cb35588d5a053039eb63f51e25d Mon Sep 17 00:00:00 2001 From: Sacha Chua Date: Wed, 27 Aug 2025 21:12:39 -0400 Subject: add year to upload sid --- emacsconf-mail.el | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/emacsconf-mail.el b/emacsconf-mail.el index 7c0966b..b48cead 100644 --- a/emacsconf-mail.el +++ b/emacsconf-mail.el @@ -583,6 +583,22 @@ Include some other things, too, such as emacsconf-year, title, name, email, url, "Volunteers: " (emacsconf-volunteer-emails-for-completion)))) (compose-mail (string-join volunteers ", "))) +(defun emacsconf-mail-core () + (interactive) + (let ((people + (seq-remove + (lambda (o) + (string= user-mail-address(assoc-default "EMAIL" o 'string=))) + (emacsconf-get-volunteer-info "core")))) + (compose-mail + (mapconcat (lambda (o) (assoc-default "EMAIL" o 'string=)) people ", ")) + (message-goto-body) + (insert + "Hello, " + (string-join (sort (mapcar (lambda (o) (assoc-default "NAME_SHORT" o 'string=)) people)) ", ") + "!\n\n") + (message-goto-subject))) + (defun emacsconf-mail-notmuch-search-for-volunteer (volunteer) (interactive (list @@ -624,7 +640,7 @@ This includes NAME_SHORT and EMAIL_NOTES." :body " Hi, ${speakers-short}! -Thanks for submitting your proposal! (ZZZ TODO: feedback) +Thanks for submitting your proposal! We'll wait a week (~ ${notification-date}) in case the other volunteers want to chime in regarding your talk. =) @@ -1181,6 +1197,8 @@ ${signature}") :backstage-password emacsconf-backstage-password :upload-url (concat "https://upload.emacsconf.org/?sid=" + emacsconf-year + "-" emacsconf-upload-password "-" (mapconcat (lambda (o) (plist-get o :slug)) (cdr group) "-")) @@ -2377,10 +2395,17 @@ This minimizes the risk of mail delivery issues and radio silence." part (expand-file-name new-filename emacsconf-backstage-dir))))) (mm-dissect-buffer)))) +;;;###autoload (defun emacsconf-notmuch-submissions () "Search for recent submissions." (interactive) - (notmuch-search emacsconf-submit-email)) + (notmuch-search (format "to:%s and not subject:\"requires approval\" and not subject:\"moderator request(s) waiting\" and not from:no-reply@netdata.cloud" emacsconf-submit-email))) + +;;;###autoload +(defun emacsconf-notmuch-new-submissions () + "Search for recent submissions." + (interactive) + (notmuch-search (format "to:%s and not subject:\"requires approval\" and not subject:\"moderator request(s) waiting\" and not from:no-reply@netdata.cloud and not tag:replied and not tag:sent" emacsconf-submit-email))) (defun emacsconf-notmuch-check-sent (query &optional groups) (interactive "MSubject: ") -- cgit v1.2.3 From 3b0e69d610f353902a84e9cd3fe90c9138a9fbf6 Mon Sep 17 00:00:00 2001 From: Sacha Chua Date: Wed, 27 Aug 2025 21:13:21 -0400 Subject: update year --- emacsconf-erc.el | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/emacsconf-erc.el b/emacsconf-erc.el index 71b2478..6139645 100644 --- a/emacsconf-erc.el +++ b/emacsconf-erc.el @@ -68,12 +68,12 @@ (defcustom emacsconf-erc-org "#emacsconf-org" "Channel for organizers") (defcustom emacsconf-topic-templates - '(("#emacsconf" "Welcome to EmacsConf 2024 | please join our track-specific channels #emacsconf-gen and #emacsconf-dev as well | Subscribe to https://lists.gnu.org/mailman/listinfo/emacsconf-discuss for updates") - ("#emacsconf-gen" "General track | https://emacsconf.org/2024/watch/gen/ | Subscribe to https://lists.gnu.org/mailman/listinfo/emacsconf-discuss for updates") - ("#emacsconf-dev" "Development track | https://emacsconf.org/2024/watch/dev/ | Subscribe to https://lists.gnu.org/mailman/listinfo/emacsconf-discuss for updates") - ("#emacsconf-accessible" "EmacsConf 2024 accessibility - help by describing what's happening | Subscribe to https://lists.gnu.org/mailman/listinfo/emacsconf-discuss for updates") - ("#emacsconf-org" "EmacsConf 2024 | Dedicated channel for EmacsConf organizers and speakers | this is intended as an internal, low-traffic channel; for main discussion around EmacsConf, please join #emacsconf | Subscribe to https://lists.gnu.org/mailman/listinfo/emacsconf-discuss for updates") - ("#emacsconf-questions" "EmacsConf 2024 | Low-traffic channel for questions if speakers prefer IRC and need help focusing; for main discussion around EmacsConf, please join #emacsconf | Subscribe to https://lists.gnu.org/mailman/listinfo/emacsconf-discuss for updates")) + '(("#emacsconf" "Welcome to EmacsConf 2025 | please join our track-specific channels #emacsconf-gen and #emacsconf-dev as well | Subscribe to https://lists.gnu.org/mailman/listinfo/emacsconf-discuss for updates") + ("#emacsconf-gen" "General track | https://emacsconf.org/2025/watch/gen/ | Subscribe to https://lists.gnu.org/mailman/listinfo/emacsconf-discuss for updates") + ("#emacsconf-dev" "Development track | https://emacsconf.org/2025/watch/dev/ | Subscribe to https://lists.gnu.org/mailman/listinfo/emacsconf-discuss for updates") + ("#emacsconf-accessible" "EmacsConf 2025 accessibility - help by describing what's happening | Subscribe to https://lists.gnu.org/mailman/listinfo/emacsconf-discuss for updates") + ("#emacsconf-org" "EmacsConf 2025 | Dedicated channel for EmacsConf organizers and speakers | this is intended as an internal, low-traffic channel; for main discussion around EmacsConf, please join #emacsconf | Subscribe to https://lists.gnu.org/mailman/listinfo/emacsconf-discuss for updates") + ("#emacsconf-questions" "EmacsConf 2025 | Low-traffic channel for questions if speakers prefer IRC and need help focusing; for main discussion around EmacsConf, please join #emacsconf | Subscribe to https://lists.gnu.org/mailman/listinfo/emacsconf-discuss for updates")) "List of (channel topic-template) entries for mass-setting channel topics." :group 'emacsconf :type '(repeat (list (string :tag "Channel") -- cgit v1.2.3 From ae77e3dd815011c6bfd7d4fa2ccc31a752a59ee0 Mon Sep 17 00:00:00 2001 From: Sacha Chua Date: Wed, 27 Aug 2025 21:13:31 -0400 Subject: insert question heading --- emacsconf-subed.el | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/emacsconf-subed.el b/emacsconf-subed.el index fafdf38..422aec3 100644 --- a/emacsconf-subed.el +++ b/emacsconf-subed.el @@ -147,13 +147,11 @@ TYPE can be 'end if you want the match end instead of the beginning." (let ((new-filename (concat (file-name-sans-extension (buffer-file-name)) "--chapters.vtt")) (subtitles (subed-subtitle-list)) (subed-auto-play-media nil)) - (when (or (not (file-exists-p new-filename)) - (yes-or-no-p (format "%s exists. Overwrite? " new-filename))) - (subed-create-file - new-filename - (emacsconf-subed-list-chapter-markers-based-on-comments - subtitles) - t)))) + (subed-create-file + new-filename + (emacsconf-subed-list-chapter-markers-based-on-comments + subtitles) + t))) (defun emacsconf-subed-list-chapter-markers-based-on-comments (subtitles) "Make a list of subtitles based on which SUBTITLES have comments." @@ -431,5 +429,14 @@ Create it if necessary." map) t)) +(defun emacsconf-subed-insert-question-heading-from-other-window () + (interactive) + (insert + (with-selected-window + (other-window) + (replace-regexp-in-string + "^- +" "NOTE " + (buffer-substring (line-beginning-position) (line-end-position)))) + "\n\n")) (provide 'emacsconf-subed) ;;; emacsconf-subed.el ends here -- cgit v1.2.3 From a57eea70cc0ac443d5ac2e7ef89355a1ec3114c2 Mon Sep 17 00:00:00 2001 From: Sacha Chua Date: Wed, 27 Aug 2025 21:13:38 -0400 Subject: insert talk link --- emacsconf.el | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/emacsconf.el b/emacsconf.el index 0aee6c5..957ad2a 100644 --- a/emacsconf.el +++ b/emacsconf.el @@ -1166,6 +1166,11 @@ The subheading should match `emacsconf-abstract-heading-regexp'." (interactive (list (emacsconf-complete-talk))) (insert (plist-get (emacsconf-search-talk-info search) :email))) +(defun emacsconf-insert-talk-link (search) + "Insert the talk link matching SEARCH." + (interactive (list (emacsconf-complete-talk))) + (insert (concat emacsconf-base-url "/" (plist-get (emacsconf-search-talk-info search) :url)))) + (defun emacsconf-backstage-url (&optional base-url) "Return or insert backstage URL with credentials." (interactive) @@ -1193,6 +1198,7 @@ The subheading should match `emacsconf-abstract-heading-regexp'." :doc "Keymap for emacsconf-related things" "a" #'emacsconf-announce "i e" #'emacsconf-insert-talk-email + "i l" #'emacsconf-insert-talk-link "i t" #'emacsconf-insert-talk-title "i s" #'emacsconf-insert-talk-schedule "I" #'emacsconf-message-talk-info -- cgit v1.2.3 From b74a01462d72ae12f626ad6fd6700638e060b324 Mon Sep 17 00:00:00 2001 From: Sacha Chua Date: Wed, 27 Aug 2025 21:14:01 -0400 Subject: publishing updates --- emacsconf-publish.el | 157 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 144 insertions(+), 13 deletions(-) diff --git a/emacsconf-publish.el b/emacsconf-publish.el index 24c364f..3204da9 100644 --- a/emacsconf-publish.el +++ b/emacsconf-publish.el @@ -475,9 +475,15 @@ (let ((extra-info (mapconcat #'identity (delq nil (list (unless (string= (plist-get o :pronunciation) "nil") - (emacsconf-surround "Pronunciation: " (plist-get o :pronunciation) "")) + (emacsconf-surround "Pronunciation: " + (if (string-match "\\[" (or (plist-get o :pronunciation) "")) + (org-export-string-as (plist-get o :pronunciation) 'md t) + (plist-get o :pronunciation)) + "")) (when (plist-get o :irc) (format "IRC: %s" (plist-get o :irc))) - (plist-get o :public-contact) + (if (string-match "\\[" (or (plist-get o :public-contact) "")) + (org-export-string-as (plist-get o :public-contact) 'md t) + (plist-get o :public-contact)) (when (plist-get o :public-email) (format "" (plist-get o :public-email))))) ", "))) (concat (plist-get o :speakers-with-pronouns) @@ -613,7 +619,10 @@ ${categories} (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")) + (plist-get o :time)) + (if (string= (plist-get o :status) "CANCELLED") + "-min talk cancelled" + "-min talk"))) :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")))) @@ -728,7 +737,7 @@ This includes the intro note, the schedule, and talk resources." (setq info (or info (emacsconf-publish-prepare-for-display (emacsconf-get-talk-info)))) (with-temp-file (expand-file-name (format "%s-before.md" (plist-get talk :slug)) (expand-file-name "info" (expand-file-name emacsconf-year emacsconf-directory))) - + (hack-dir-local-variables-non-file-buffer) (insert "\n") (insert (emacsconf-surround "" (plist-get talk :intro-note) "\n\n" "")) (let ((is-live (emacsconf-talk-live-p talk))) @@ -751,7 +760,7 @@ This includes the intro note, the schedule, and talk resources." (let ((msecs (elt sub 1))) (concat (if (and (elt sub 4) (not (string= (elt sub 4) ""))) - (format "\n[[!template new=\"1\" text=\"\"\"%s\"\"\" start=\"%s\" video=\"%s\" id=\"subtitle\"%s]]\n\n" + (format "\n
    [[!template new=\"1\" text=\"\"\"%s\"\"\" start=\"%s\" video=\"%s\" id=\"subtitle\"%s]]
    " (string-trim (replace-regexp-in-string "^NOTE[ \n]" "" (elt sub 4))) (concat (format-seconds "%02h:%02m:%02s" (/ (floor msecs) 1000)) "." (format "%03d" (mod (floor msecs) 1000))) @@ -836,12 +845,13 @@ This includes captions, contact, and an invitation to participate." ;; Contact information (with-temp-file (expand-file-name (format "%s-after.md" (plist-get talk :slug)) (expand-file-name "info" (expand-file-name emacsconf-year emacsconf-directory))) + (hack-dir-local-variables-non-file-buffer) (insert "\n" "\n\n" ;; main transcript (if (plist-get talk :public) (emacsconf-publish-format-captions talk) "") - (if (emacsconf-talk-file talk "--answers.vtt") + (if (and (plist-get talk :qa-public) (emacsconf-talk-file talk "--answers.vtt")) (emacsconf-publish-format-transcript (append (list :chapter-file (emacsconf-talk-file talk "--answers--chapters.vtt") @@ -1189,7 +1199,7 @@ You can also get this schedule as iCalendar files: ${icals}. Importing that into (with-temp-file (expand-file-name "schedule-details.md" (expand-file-name emacsconf-year emacsconf-directory)) (emacsconf-publish-schedule-with-times info))) - ((or 'cfp 'program) + ((or 'cfp 'program 'harvest 'resources) (with-temp-file (expand-file-name "schedule-details.md" (expand-file-name emacsconf-year emacsconf-directory)) (emacsconf-publish-program-without-times info)) @@ -1287,6 +1297,8 @@ You can also get this schedule as iCalendar files: ${icals}. Importing that into (or (plist-get o :toobnix-url) (plist-get o :video-file))) "video posted") + (when (plist-get o :qa-public) + "Q&A posted") (emacsconf-surround "video: " (plist-get o :video-duration) "" nil) (emacsconf-surround "answers: " (and (plist-get o :qa-public) (plist-get o :qa-video-duration)) @@ -1583,7 +1595,7 @@ answers without needing to listen to everything again. You can see ${base-url}${url}." ))))))) -(defun emacsconf-publish-media-files-on-change (talk) +(defun emacsconf-publish-media-files-on-change (talk &optional always-update) "Publish the files and update the index." - (interactive (list (emacsconf-complete-talk-info))) + (interactive (list (emacsconf-complete-talk-info) current-prefix-arg)) (let ((org-state (if (boundp 'org-state) org-state (plist-get talk :status)))) (if (plist-get talk :public) ;; Copy main files from backstage to public (let ((public-files (emacsconf-publish-filter-public-files talk))) (mapc (lambda (file) - (when (not (file-exists-p (expand-file-name file emacsconf-public-media-directory))) - (copy-file (expand-file-name file emacsconf-backstage-dir) - (expand-file-name file emacsconf-public-media-directory) t))) + (when (or always-update (not (file-exists-p (expand-file-name file emacsconf-public-media-directory)))) + (copy-file (if (file-exists-p (expand-file-name file emacsconf-backstage-dir)) + (expand-file-name file emacsconf-backstage-dir) + (expand-file-name file emacsconf-cache-dir)) + (expand-file-name file emacsconf-public-media-directory) t))) public-files)) ;; Remove files from public (let ((files (directory-files emacsconf-public-media-directory nil @@ -2823,6 +2837,9 @@ Tends to be quota-limited, though." arguments) " ")) (with-current-buffer (get-buffer-create "*YouTube*") (erase-buffer) + (kill-new (concat (car emacsconf-publish-youtube-upload-command) + " " + (mapconcat #'shell-quote-argument arguments " "))) (apply #'call-process (car emacsconf-publish-youtube-upload-command) nil t t @@ -3029,5 +3046,119 @@ Tends to be quota-limited, though." (expand-file-name (concat (plist-get talk :file-prefix) "--intro.webm") emacsconf-backstage-dir) t)))) + +(defun emacsconf-publish-update-transcript () + (interactive) + (emacsconf-subed-make-chapter-file-based-on-comments) + (let ((talk (emacsconf-resolve-talk (emacsconf-get-slug-from-string (buffer-file-name))))) + (emacsconf-publish-media-files-on-change talk) + (emacsconf-publish-with-wiki-change + (emacsconf-publish-captions-in-wiki talk) + (emacsconf-publish-info-pages-for-talk talk)))) + +;; for emacs.tv +(defun emacsconf-publish-insert-video-entries (&optional info tags) + (interactive) + (setq tags (or tags (format ":emacsconf:emacsconf%s:" emacsconf-year))) + (dolist (talk (emacsconf-publish-prepare-for-display (or info (emacsconf-get-talk-info)))) + (when (emacsconf-talk-file talk "--main.webm") + (let ((new-entry (emacsconf-replace-plist-in-string + (append + (list + :conf-name emacsconf-name + :conf-year emacsconf-year + :media-url (format "https://media.emacsconf.org/%s/%s--main.webm" + emacsconf-year + (plist-get talk :file-prefix)) + :transcript-url + (if (emacsconf-talk-file "--main.vtt" + (format "https://media.emacsconf.org/%s/%s--main.vtt" + emacsconf-year + (plist-get talk :file-prefix))) + "") + :duration (or (plist-get talk :qa-video-duration) + (emacsconf-format-seconds + (/ (compile-media-get-file-duration-ms (emacsconf-talk-file talk "--main.webm")) + 1000))) + :url (concat emacsconf-base-url (plist-get talk :url)) + :tags (if (plist-get talk :tags) (concat tags (substring (plist-get talk :tags) 1)) tags) + :date (format-time-string "%FT%T%z" (plist-get talk :start-time) t)) + talk + (list :youtube-url "" :toobnix-url "" :speakers "")) + "* ${title} ${tags} +:PROPERTIES: +:DATE: ${date} +:URL: ${url} +:DURATION: ${duration} +:MEDIA_URL: ${media-url} +:YOUTUBE_URL: ${youtube-url} +:TOOBNIX_URL: ${toobnix-url} +:TRANSCRIPT_URL: ${transcript-url} +:SPEAKERS: ${speakers} +:SERIES: ${conf-name} ${conf-year} +:END: +" + ))) + (with-current-buffer (find-file-noselect emacstv-index-org) + (if (and (plist-get talk :youtube-url) (emacstv-find-by-youtube-url (plist-get talk :youtube-url))) + (org-entry-put (point) "DATE" (format-time-string "%FT%T%z" (plist-get talk :start-time) t)) + (goto-char (point-max)) + (insert new-entry))))))) + +(defun emacsconf-publish-insert-video-entries-for-answers (&optional info tags) + (interactive) + (setq tags (or tags (format ":answers:emacsconf:emacsconf%s:" emacsconf-year))) + (dolist (talk (emacsconf-publish-prepare-for-display (or info (emacsconf-get-talk-info)))) + (when (emacsconf-talk-file talk "--answers.webm") + (let ((new-entry (emacsconf-replace-plist-in-string + (append + (list + :conf-name emacsconf-name + :conf-year emacsconf-year + :youtube-url (plist-get talk :qa-youtube) + :toobnix-url (plist-get talk :qa-toobnix) + :media-url (format "https://media.emacsconf.org/%s/%s--answers.webm" + emacsconf-year + (plist-get talk :file-prefix)) + :transcript-url (if (emacsconf-talk-file talk "--answers.vtt") + (format "https://media.emacsconf.org/%s/%s--answers.vtt" + emacsconf-year + (plist-get talk :file-prefix)) + "") + :url (concat emacsconf-base-url (plist-get talk :url)) + :duration (or (plist-get talk :qa-video-duration) + (emacsconf-format-seconds + (/ (compile-media-get-file-duration-ms (emacsconf-talk-file talk "--answers.webm")) + 1000))) + :tags (if (plist-get talk :tags) (concat tags (substring (plist-get talk :tags) 1)) tags) + :date (format-time-string "%FT%T%z" (plist-get talk :start-time) t)) + talk + (list :youtube-url "" :toobnix-url "" :speakers "")) + "* Q&A: ${title} ${tags} +:PROPERTIES: +:DATE: ${date} +:URL: ${url} +:DURATION: ${duration} +:MEDIA_URL: ${media-url} +:YOUTUBE_URL: ${youtube-url} +:TOOBNIX_URL: ${toobnix-url} +:TRANSCRIPT_URL: ${transcript-url} +:SPEAKERS: ${speakers} +:SERIES: ${conf-name} ${conf-year} +:END: +" + ))) + (with-current-buffer (find-file-noselect emacstv-index-org) + (if (and (plist-get talk :qa-youtube) (emacstv-find-by-youtube-url (plist-get talk :qa-youtube))) + (progn + (org-entry-put (point) "DATE" (format-time-string "%FT%T%z" (plist-get talk :start-time) t)) + (org-entry-put (point) + "TRANSCRIPT_URL" + (if (emacsconf-talk-file talk "--answers.vtt") + (format "https://media.emacsconf.org/%s/%s--answers.vtt" + emacsconf-year + (plist-get talk :file-prefix)) + ""))) + (insert new-entry))))))) ;; (provide 'emacsconf-publish) -- cgit v1.2.3 From f9bf73edaca57d1cf8e6730fb425e0b8db31672f Mon Sep 17 00:00:00 2001 From: Sacha Chua Date: Fri, 19 Sep 2025 10:17:55 -0400 Subject: fix schedule matching --- emacsconf-schedule.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/emacsconf-schedule.el b/emacsconf-schedule.el index 260ef9c..b73d61b 100644 --- a/emacsconf-schedule.el +++ b/emacsconf-schedule.el @@ -719,14 +719,14 @@ Both start and end time are tested." (goto-char (point-min)) (while (not (eobp)) (cond - ((looking-at "\\([<>]\\)=? *\\([0-9]+:[0-9]+\\) *EST \\([0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]\\|Sat\\|Sun\\)?") + ((looking-at "\\([<>]\\)=? *\\([0-9]+:[0-9]+\\) *EST\\( [0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]\\|Sat\\|Sun\\)?") (push (and (string= (match-string 1) ">") ; start time (match-string 2)) result) (push (and (string= (match-string 1) "<") ; end time (match-string 2)) result) - (push (match-string 3) result) + (push (and (match-string 3) (string-trim (match-string 3))) result) (goto-char (match-end 0))) ((looking-at " or ") (push 'or result) -- cgit v1.2.3 From 165bfc5fab12a60328bedc4095462390e22685e2 Mon Sep 17 00:00:00 2001 From: Sacha Chua Date: Fri, 19 Sep 2025 10:18:11 -0400 Subject: add stuff for current org notebook, availability --- emacsconf.el | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/emacsconf.el b/emacsconf.el index 957ad2a..b2889f2 100644 --- a/emacsconf.el +++ b/emacsconf.el @@ -126,7 +126,7 @@ (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/shared/%s" emacsconf-year)) -(defvar emacsconf-media-extensions '("webm" "mkv" "mp4" "webm" "mov" "avi" "ts" "ogv" "wav" "ogg" "mp3" )) +(defvar emacsconf-media-extensions '("webm" "mkv" "mp4" "webm" "mov" "avi" "mpv" "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") (defvar emacsconf-backstage-password nil "Password for backstage area.") @@ -172,6 +172,11 @@ (defun emacsconf-backstage-dired () (interactive) (dired emacsconf-backstage-dir "-tl")) + +(defun emacsconf-backstage-web () + (interactive) + (browse-url (emacsconf-backstage-url))) + (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")) @@ -2001,5 +2006,30 @@ tracks with the ID in the cdr of that list." (message "Deleting %s from %s" (file-name-nondirectory file) dir))))) +(defun emacsconf-current-org-notebook-filename () + "Return the filename for the current year's public organizers notebook." + (expand-file-name "organizers-notebook/index.org" (expand-file-name emacsconf-year emacsconf-directory))) + +(defun emacsconf-current-org-notebook-open () + "Open the current year's public organizers notebook." + (interactive) + (find-file (emacsconf-current-org-notebook-filename))) + +(defun emacsconf-current-org-notebook-refresh-schedule () + "Refresh info from draft schedule." + (interactive) + (save-window-excursion + (with-current-buffer (find-file-noselect (emacsconf-current-org-notebook-filename)) + (save-restriction + (widen) + (goto-char (org-find-property "CUSTOM_ID" "draft-schedule")) + (org-babel-execute-subtree))))) + +(defun emacsconf-insert-availability-comment (talk) + (interactive (list (or (emacsconf-search-talk-info (thing-at-point 'symbol)) + (emacsconf-complete-talk)))) + (save-excursion + (goto-char (line-end-position)) + (insert " ; " (plist-get talk :availability)))) (provide 'emacsconf) ;;; emacsconf.el ends here -- cgit v1.2.3 From efac99d8bd3f88f26b19dfd27b57743a17ff6361 Mon Sep 17 00:00:00 2001 From: Sacha Chua Date: Fri, 19 Sep 2025 10:18:23 -0400 Subject: refresh notebook --- emacsconf-publish.el | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/emacsconf-publish.el b/emacsconf-publish.el index 3204da9..4512aaf 100644 --- a/emacsconf-publish.el +++ b/emacsconf-publish.el @@ -65,6 +65,7 @@ (defun emacsconf-publish-add-talk () "Add the current talk to the wiki." (interactive) + (emacsconf-current-org-notebook-refresh-schedule) (emacsconf-publish-talk-page (emacsconf-get-talk-info-for-subtree)) (emacsconf-publish-info-pages) (emacsconf-publish-schedule) @@ -1411,6 +1412,7 @@ If MODIFY-FUNC is specified, use it to modify the talk." (defun emacsconf-publish-backstage-index (&optional filename) "Render the backstage index to FILENAME." (interactive) + (emacsconf-current-org-notebook-refresh-schedule) (setq filename (or filename (expand-file-name "index.html" emacsconf-backstage-dir))) (let ((info (or emacsconf-schedule-draft (emacsconf-publish-prepare-for-display (emacsconf-get-talk-info))))) (with-temp-file filename @@ -1419,7 +1421,7 @@ If MODIFY-FUNC is specified, use it to modify the talk." (lambda (o) (append (list :captions-edited t :backstage t) o)) - (emacsconf-filter-talks info))) + (emacsconf-filter-talks info))) (by-status (seq-group-by (lambda (o) (plist-get o :status)) talks)) (files (directory-files emacsconf-backstage-dir))) (insert -- cgit v1.2.3 From a76f18ba6a03e684aa0901b4c7646dac2ff17677 Mon Sep 17 00:00:00 2001 From: Sacha Chua Date: Fri, 19 Sep 2025 11:10:48 -0400 Subject: allow vertical view --- emacsconf-schedule.el | 116 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 76 insertions(+), 40 deletions(-) diff --git a/emacsconf-schedule.el b/emacsconf-schedule.el index b73d61b..f2da024 100644 --- a/emacsconf-schedule.el +++ b/emacsconf-schedule.el @@ -287,23 +287,23 @@ Pairs with `emacsconf-schedule-dump-sexp'." (defvar emacsconf-schedule-svg-modify-functions '(emacsconf-schedule-svg-color-by-track) "Functions to run to modify the display of each item.") (defvar emacsconf-use-absolute-url nil "Non-nil means try to use absolute URLs.") -(defun emacsconf-schedule-svg-track (svg base-x base-y width height start-time end-time info) +(defun emacsconf-schedule-svg-track (svg base-x base-y width height start-time end-time info &optional direction) "Draw the actual rectangles and text for the talks." - (let ((scale (/ width (float-time (time-subtract end-time start-time))))) + (let ((scale (/ (if (eq direction 'vertical) height width) (float-time (time-subtract end-time start-time))))) (mapc (lambda (o) (let* ((offset (floor (* scale (float-time (time-subtract (plist-get o :start-time) start-time))))) (size (floor (* scale (float-time (time-subtract (plist-get o :end-time) (plist-get o :start-time)))))) - (x (+ base-x offset)) - (y base-y) + (x (if (eq direction 'vertical) base-x (+ base-x offset))) + (y (if (eq direction 'vertical) (+ base-y offset) base-y)) (node (dom-node 'rect (list (cons 'x x) (cons 'y y) (cons 'opacity "0.8") - (cons 'width size) - (cons 'height (1- height)) + (cons 'width (if (eq direction 'vertical) (1- width) size)) + (cons 'height (if (eq direction 'vertical) size (1- height))) (cons 'stroke "black") (cons 'stroke-dasharray (if (string-match "live" (or (plist-get o :q-and-a) "live")) @@ -336,7 +336,8 @@ Pairs with `emacsconf-schedule-dump-sexp'." (dom-node 'g `((transform . ,(format "translate(%d,%d)" - (+ x size -2) (+ y height -2)))) + (if (eq direction 'vertical) x (+ x size -2)) + (if (eq direction 'vertical) (+ y size -2) (+ y height -2))))) (dom-node 'text (list @@ -344,7 +345,7 @@ Pairs with `emacsconf-schedule-dump-sexp'." (cons 'x 0) (cons 'y 0) (cons 'font-size 10) - (cons 'transform "rotate(-90)")) + (cons 'transform (if (eq direction 'vertical) nil "rotate(-90)"))) (svg--encode-text (or (plist-get o :slug) (plist-get o :title)))))))) (run-hook-with-args 'emacsconf-schedule-svg-modify-functions @@ -354,44 +355,73 @@ Pairs with `emacsconf-schedule-dump-sexp'." parent))) info))) -(defun emacsconf-schedule-svg-day (elem label width height start end tracks) +(defun emacsconf-schedule-svg-day (elem label width height start end tracks &optional direction) "Add the time scale and the talks on a given day." - (let* ((label-margin 15) - (track-height (/ (- height (* 2 label-margin)) (length tracks))) - (x 0) (y label-margin) - (scale (/ width (float-time (time-subtract end start)))) + (let* ((label-margin (if (eq direction 'vertical) 40 15)) + (x (if (eq direction 'vertical) label-margin 0)) + (y (if (eq direction 'vertical) label-margin label-margin)) + (track-size (if (eq direction 'vertical) + (/ (- width (* 2 label-margin)) (length tracks)) + (/ (- height (* 2 label-margin)) (length tracks)))) + (scale (/ (if (eq direction 'vertical) height width) (float-time (time-subtract end start)))) (time start)) (dom-append-child elem (dom-node 'title nil (concat "Schedule for " label))) (svg-rectangle elem 0 0 width height :fill "white") - (svg-text elem label :x 3 :y (- label-margin 3) :fill "black" :font-size "10") + (if (eq direction 'vertical) + (svg-text elem label :x 3 :y (- label-margin 10) :fill "black" :font-size "10") + (svg-text elem label :x 3 :y (- label-margin 3) :fill "black" :font-size "10")) (mapc (lambda (track) (emacsconf-schedule-svg-track - elem x y width track-height - start end track) - (setq y (+ y track-height))) + elem x y + (if (eq direction 'vertical) track-size width) + (if (eq direction 'vertical) height track-size) + start end track + direction) + (if (eq direction 'vertical) + (setq x (+ x track-size)) + (setq y (+ y track-size)))) tracks) ;; draw grid (while (time-less-p time end) - (let ((x (* (float-time (time-subtract time start)) scale))) + (let ((x (if (eq direction 'vertical) + 3 + (* (float-time (time-subtract time start)) scale))) + (y (if (eq direction 'vertical) + (+ (* (float-time (time-subtract time start)) scale) label-margin) + 3))) (dom-append-child elem (dom-node 'g - `((transform . ,(format "translate(%d,%d)" x label-margin))) - (dom-node - 'line - `((stroke . "darkgray") - (x1 . 0) - (y1 . 0) - (x2 . 0) - (y2 . ,(- height label-margin label-margin)))) + `((transform . ,(format "translate(%d,%d)" x y))) + (if (eq direction 'vertical) + (dom-node + 'line + `((stroke . "darkgray") + (x1 . ,label-margin) + (y1 . 0) + (x2 . ,(- width label-margin)) + (y2 . 0))) + (dom-node + 'line + `((stroke . "darkgray") + (x1 . 0) + (y1 . 0) + (x2 . 0) + (y2 . ,(- height label-margin label-margin))))) (dom-node 'text - `((fill . "black") - (x . 0) - (y . ,(- height 2 label-margin)) - (font-size . 10) - (text-anchor . "left")) + (if (eq direction 'vertical) + `((fill . "black") + (x . 0) + (y . 0) + (font-size . 10) + (dy . ".4em")) + `((fill . "black") + (x . 0) + (y . ,(- height label-margin -5)) + (font-size . 10) + (text-anchor . "left"))) (svg--encode-text (format-time-string "%-l %p" time emacsconf-timezone))))) (setq time (time-add time (seconds-to-time 3600))))) elem)) @@ -411,7 +441,7 @@ Pairs with `emacsconf-schedule-dump-sexp'." "peachpuff") (t "gray")))) -(defun emacsconf-schedule-svg (width height &optional info) +(defun emacsconf-schedule-svg (width height &optional info direction) "Make the schedule SVG for INFO." (setq info (or info (emacsconf-publish-prepare-for-display (emacsconf-get-talk-info)))) (let ((days (seq-group-by (lambda (o) @@ -430,7 +460,8 @@ Pairs with `emacsconf-schedule-dump-sexp'." :start start :end end :tracks (emacsconf-by-track (cdr o))))) - days)))) + days) + direction))) (defun emacsconf-schedule-svg-color-by-status (o node &optional _) "Set talk color based on status. @@ -459,23 +490,28 @@ Other status: gray" "lightgray") (_ "gray"))))) -(defun emacsconf-schedule-svg-days (width height days) +(defun emacsconf-schedule-svg-days (width height days &optional direction) "Display multiple days." (let ((svg (svg-create width height)) - (day-height (/ height (length days))) - (y 0)) + (day-height (if (eq direction 'vertical) height (/ height (length days)))) + (day-width (if (eq direction 'vertical) (/ width (length days)) width)) + (y 0) + (x 0)) (dom-append-child svg (dom-node 'title nil "Graphical view of the schedule")) (mapc (lambda (day) - (let ((group (dom-node 'g `((transform . ,(format "translate(0,%d)" y)))))) + (let ((group (dom-node 'g `((transform . ,(format "translate(%d,%d)" x y)))))) (dom-append-child svg group) (emacsconf-schedule-svg-day group (plist-get day :label) - width day-height + day-width day-height (date-to-time (plist-get day :start)) (date-to-time (plist-get day :end)) - (plist-get day :tracks))) - (setq y (+ y day-height))) + (plist-get day :tracks) + direction)) + (if (eq direction 'vertical) + (setq x (+ x day-width)) + (setq y (+ y day-height)))) days) svg)) -- cgit v1.2.3 From f4ff6e2e46e5933d4405074cd1b8dc044e44239c Mon Sep 17 00:00:00 2001 From: Sacha Chua Date: Sun, 21 Sep 2025 13:45:36 -0400 Subject: emacsconf-org-insert-description --- emacsconf.el | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/emacsconf.el b/emacsconf.el index b2889f2..35233f6 100644 --- a/emacsconf.el +++ b/emacsconf.el @@ -479,10 +479,16 @@ FILENAME specifies an extra string to add to the file prefix if needed." (format "[%s](%s \"%s\")" desc path (plist-get talk :title))) (_ path)))) +(defun emacsconf-org-insert-description (link desc) + (unless desc + (when (string-match "emacsconf:\\(.+\\)" link) + (plist-get (emacsconf-search-talk-info (match-string 1 link)) :title)))) + (with-eval-after-load 'org (org-link-set-parameters "emacsconf" :follow #'emacsconf-go-to-talk + :insert-description #'emacsconf-org-insert-description :complete (lambda () (concat "emacsconf:" (emacsconf-complete-slug))) :export #'emacsconf-export-slug)) -- cgit v1.2.3 From 7b55f6a420ef4e5d6ecd6c79b49053734d978b71 Mon Sep 17 00:00:00 2001 From: Sacha Chua Date: Thu, 25 Sep 2025 18:52:54 -0400 Subject: keep unwrapping message bodies --- emacsconf-mail.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/emacsconf-mail.el b/emacsconf-mail.el index b48cead..aec6506 100644 --- a/emacsconf-mail.el +++ b/emacsconf-mail.el @@ -363,8 +363,8 @@ insert into the current buffer instead of drafting e-mails." (defun emacsconf-mail-parse-submission (body) "Extract data from EmacsConf submissions in BODY." - (when (listp body) (setq body (plist-get (car body) :content))) - (when (listp body) (setq body (plist-get (car body) :content))) + (while (and (listp body) (plist-get (car body) :content)) + (setq body (plist-get (car body) :content))) (let* ((data (list :body body)) (fields '((:title "^[* ]*Talk title") (:description "^[* ]*Talk description") -- cgit v1.2.3 From ac24ad231fabe040777e2c390b7c231366d19682 Mon Sep 17 00:00:00 2001 From: Sacha Chua Date: Sat, 22 Nov 2025 12:21:35 -0500 Subject: fix duplicated code --- emacsconf.el | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/emacsconf.el b/emacsconf.el index 35233f6..0f3b0d0 100644 --- a/emacsconf.el +++ b/emacsconf.el @@ -391,6 +391,27 @@ FILENAME specifies an extra string to add to the file prefix if needed." (kill-new (string-join command " "))) command)) +(defun emacsconf-upload-copy-scp-from-json (talk key filename) + "Parse PsiTransfer JSON files and make an scp command. +This command will copy the uploaded file to the current 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* ((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)))) + (cmd (format "scp media:%s %s" + (file-name-sans-extension (tramp-file-local-name (buffer-file-name))) + new-filename))) + (message "%s" cmd) + (kill-new cmd))) (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. @@ -642,6 +663,7 @@ If INFO is specified, limit it to that list." (:caption-note "CAPTION_NOTE") (:captions-edited "CAPTIONS_EDITED") ;; Conference + (:live "LIVE") (:check-in "CHECK_IN") (:public "PUBLIC") (:intro-note "INTRO_NOTE") @@ -1628,12 +1650,32 @@ 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-bbb-create-rooms () + "Copy the commands needed to create the rooms. +docker exec -it greenlight-v3 /bin/bash -c \"bundle exec rails console\" +user_id = User.find_by_email(\"emacsconf@sachachua.com\").id" + (interactive) + (kill-new + (mapconcat (lambda (group) + (format + "Room.create(user_id: user_id, name: \"%s - %s\")\n" + (plist-get (cadr group) :speakers) + (string-join (mapcar (lambda (talk) (plist-get talk :slug)) + (cdr group)) + ", "))) + (emacsconf-mail-groups (emacsconf-active-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 + +Print out room IDs with: +Room.all.each { |x| puts x.friendly_id + " " + x.name }; nil " + (interactive "MInput: ") (let ((rooms (mapcar (lambda (row) (when (string-match "^\\(.+?\\) \\(.+\\)" row) (list (match-string 1 row) (match-string 2 row)))) @@ -1661,6 +1703,47 @@ friendly-id speaker - slugs "/join")))) (emacsconf-active-talks (emacsconf-get-talk-info))))) +(defun emacsconf-bbb-spookfox-set-moderator-codes () + (interactive) + (dolist (talk (seq-filter (lambda (o) + (and (plist-get o :bbb-room) + (not (plist-get o :bbb-mod-code)))) + (emacsconf-publish-prepare-for-display (emacsconf-get-talk-info)))) + (spookfox-js-injection-eval-in-active-tab + (format "window.location.href = \"%s\"" + (replace-regexp-in-string "/join" "" (plist-get talk :bbb-room))) + t) + (sleep-for 3) + (spookfox-js-injection-eval-in-active-tab + "document.querySelector('button[data-rr-ui-event-key=\"settings\"]').click()" t) + (spookfox-js-injection-eval-in-active-tab + "document.querySelector('input#glAnyoneCanStart').checked = true") + (spookfox-js-injection-eval-in-active-tab + "document.querySelector('input#muteOnStart').checked = true") + (spookfox-js-injection-eval-in-active-tab + "document.querySelectorAll('.border-end button')[2].click()" t) + (let ((code (spookfox-js-injection-eval-in-active-tab + "document.querySelector('.access-code-input input').value" t))) + (message "Setting %s to %s" (plist-get talk :slug) code) + (emacsconf-set-property-from-slug + talk "BBB_MOD_CODE" + code) + (sit-for 2)))) + +(defun emacsconf-bbb-spookfox-confirm-settings () + (interactive) + (dolist (talk (seq-filter (lambda (o) + (plist-get o :bbb-room)) + (emacsconf-publish-prepare-for-display (emacsconf-get-talk-info)))) + (spookfox-js-injection-eval-in-active-tab + (format "window.location.href = \"%s\"" + (replace-regexp-in-string "/join" "" (plist-get talk :bbb-room))) + t) + (sleep-for 3) + (spookfox-js-injection-eval-in-active-tab + "document.querySelector('button[data-rr-ui-event-key=\"settings\"]').click()" t) + (sleep-for 3))) + (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 ""))) -- cgit v1.2.3