Add new custom org link "music:ARTIST:TRACK::TIME"

This commit is contained in:
TEC 2020-06-07 00:18:47 +08:00
parent 0cc6f8a238
commit 6c24ceffb6

View file

@ -3638,6 +3638,155 @@ Saving seconds adds up after all! (but only so much)
"Complete xkcd using `+xkcd-stored-info'"
(format "xkcd:%d" (+xkcd-select))))
#+END_SRC
***** Music
First, we set up all the necessarily 'utility' functions.
#+BEGIN_SRC emacs-lisp
(after! org
(defvar org-music-player 'mpris
"Music player type. Curretly only supports mpris.")
(defvar org-music-mpris-player "Lollypop"
"Name of the mpris player, used in the form org.gnome.MPRIS.")
(defvar org-music-track-search-method 'beets
"Method to find the track file from the link.")
(defvar org-music-beets-db "~/Music/library.db"
"Location of the beets DB, when using beets as the `org-music-track-search-method'")
(defun org-music-get-link (full &optional include-time)
"Generate link string for currently playing track, optionally including a time-stamp"
(let* ((track-metadata
(dbus-get-property :session "org.gnome.Lollypop" "/org/mpris/MediaPlayer2"
"org.mpris.MediaPlayer2.Player" "Metadata"))
(artist (caar (cadr (assoc "xesam:artist" track-metadata))))
(album (car (cadr (assoc "xesam:album" track-metadata))))
(track (car (cadr (assoc "xesam:title" track-metadata))))
(seconds (when include-time
(/ (dbus-get-property :session "org.gnome.Lollypop" "/org/mpris/MediaPlayer2"
"org.mpris.MediaPlayer2.Player" "Position") 1000000))))
(if full
(format "[[music:%s][%s by %s]]" (org-music-format-link artist album track seconds) track artist)
(org-music-format-link artist album track seconds))))
(defun org-music-format-link (artist album track &optional seconds)
(let ((artist (replace-regexp-in-string ":" "\\:" artist))
(album (replace-regexp-in-string ":" "\\:" album))
(track (replace-regexp-in-string ":" "\\:" track)))
(if seconds
(format "%s:%s::%s" artist track (org-music-seconds-to-time seconds))
(format "%s:%s" artist track))))
(defun org-music-parse-link (link)
(let* ((link-dc (->> link
(replace-regexp-in-string "\\([^\\\\]\\)\\\\:" "\\1#COLON#")
(replace-regexp-in-string "\\`music:" "")
(replace-regexp-in-string "\\(::[a-z0-9]*[0-9]\\)\\'" "\\1s")))
(link-components (mapcar (lambda (lc) (replace-regexp-in-string "#COLON#" ":" lc))
(s-split ":" link-dc)))
(artist (nth 0 link-components))
(track (nth 1 link-components))
(seconds (when (and (> (length link-components) 3)
(equal (nth 2 link-components) ""))
(org-music-time-to-seconds (nth 3 link-components)))))
(list artist track seconds)))
(defun org-music-seconds-to-time (seconds)
"Convert a number of seconds to a nice human duration, e.g. 5m21s.
This action is reversed by `org-music-time-to-seconds'."
(if (< seconds 60)
(format "%ss" seconds)
(if (< seconds 3600)
(format "%sm%ss" (/ seconds 60) (% seconds 60))
(format "%sh%sm%ss" (/ seconds 3600) (/ (% seconds 3600) 60) (% seconds 60)))))
(defun org-music-time-to-seconds (time-str)
"Get the number of seconds in a string produced by `org-music-seconds-to-time'."
(let* ((time-components (reverse (s-split "[a-z]" time-str)))
(seconds (string-to-number (nth 1 time-components)))
(minutes (when (> (length time-components) 2)
(string-to-number (nth 2 time-components))))
(hours (when (> (length time-components) 3)
(string-to-number (nth 3 time-components)))))
(+ (* 3600 (or hours 0)) (* 60 (or minutes 0)) seconds)))
(defun org-music-play-track (artist title &optional seconds)
"Play the track specified by ARTIST and TITLE, optionally skipping to SECONDS in."
(case org-music-player
('mpris (org-music-mpris-play
(org-music-find-track-file artist title)
seconds))
(t (user-error! "The specified music player: %s is not supported" org-music-player))))
(defun org-music-mpris-play (file &optional seconds)
(let ((player (concat "org.gnome." org-music-mpris-player)))
(dbus-call-method :session player
"/org/mpris/MediaPlayer2" "org.mpris.MediaPlayer2.Player"
"OpenUri" (replace-regexp-in-string " " "%20" (rng-file-name-uri file)))
(when seconds
(let ((track-id (caadr (assoc "mpris:trackid"
(dbus-get-property :session
player
"/org/mpris/MediaPlayer2"
"org.mpris.MediaPlayer2.Player"
"Metadata")))))
(dbus-call-method :session player
"/org/mpris/MediaPlayer2" "org.mpris.MediaPlayer2.Player"
"SetPosition"
:object-path track-id
:int64 (round (* seconds 1000000)))))))
(defun org-music-find-track-file (artist title)
(case org-music-track-search-method
('beets (org-music-beets-find-file artist title))
(t (user-error! "The specified music search method: %s is not supported" org-music-track-search-method))))
(defun org-music-beets-find-file (artist title)
"Find the file correspanding to a given artist and title."
(let* ((artist-escaped (replace-regexp-in-string "\"" "\\\"" artist))
(title-escaped (replace-regexp-in-string "\"" "\\\"" title))
(file
(shell-command-to-string
(format
"sqlite3 '%s' \"SELECT path FROM items WHERE artist IS '%s' AND title IS '%s' COLLATE NOCASE\""
(expand-file-name org-music-beets-db) artist-escaped title-escaped))))
(if (> (length file) 0)
(substring file 0 -1)
(user-error! "Could not find '%s' by '%s' in your beets database" title artist)))))
#+END_SRC
Then we integrate this nicely with org-mode
#+BEGIN_SRC emacs-lisp
(after! org
(org-link-set-parameters "music"
:follow #'org-music-open-fn
:export #'org-music-export)
(defun org-music-open-fn (link)
(apply #'org-music-play-track (org-music-parse-link link)))
(defun org-music-insert-current-track (&optional include-time)
"Insert link to currest track, including a timestamp when the universal argument is supplied."
(interactive "P")
(pp include-time)
(insert (org-music-get-link t include-time)))
(defun org-music-export (path desc backend _com)
"Convert xkcd to html/LaTeX form"
(let* ((track-info (org-music-parse-link path))
(artist (nth 0 track-info))
(track (nth 1 track-info))
(seconds (nth 2 track-info))
(description ))
(cond ((org-export-derived-backend-p backend 'html)
(if seconds
(format "%s seconds into <span style=\"font-style: italic\">%s</span> by %s" seconds track artist)
(format "%s by %s" track artist)))
((org-export-derived-backend-p backend 'latex)
(if seconds
(format "%s seconds into \\emph{%s} by %s" seconds track artist)
(format "\\emph{%s} by %s" track artist)))
(t (if seconds
(format "%s seconds into %s by %s" seconds track artist)
(format "%s by %s" track artist)))))))
#+END_SRC
***** YouTube
The ~[[yt:...]]~ links preview nicely, but don't export nicely. Thankfully, we can
fix that.