--- /dev/null
+#+date: <2025-01-25 Sat 23:50>
+#+title: Using Lisp to submit listens.
+#+filetags: :elisp:music:open_web:emacs:
+#+OPTIONS: \n:t
+
+#+BEGIN_PREVIEW
+~cum mortuis in lingua mortua~
+
+Tracking music listening habits doesn't need to be hard. My local radio station [[https://rtrfm.com.au/][RTRFM]] publishes playlists for most of its shows (subject to the presenter's interest in doing so of course - most of them are volunteers after all). It's trivial to copy-paste these into [[https://www.gnu.org/software/emacs/][an Emacs Lisp interpreter]], making everything possible.
+
+A quick fork of [[http://git.vanrenterghem.biz/musicbrainz.git][listenbrainz.el]] and a few updates to its code later, we've got a tool to submit a playlist of a show we've listened to. In a Lisp dialect even, rumours of the language's demise notwithstanding.
+#+END_PREVIEW
+
+First we adjust the time the tracks were originally aired to the time we're listening to the show - re-streaming is possible for some weeks after the original air date.
+
+#+BEGIN_SRC emacs-lisp
+ (setq starttime (date-to-time "Sat 25 Jan 2025 15:10"))
+#+END_SRC
+
+We're grabbing the playlist from an org mode headline and put each line in a list (of course).
+
+#+BEGIN_SRC emacs-lisp
+ (setq playlist (let ((playlist
+ (save-excursion
+ (goto-char (search-forward "Playlist data" nil t))
+ (let ((el (org-element-at-point-no-context)))
+ (split-string
+ (buffer-substring-no-properties
+ (org-element-property :contents-begin el)
+ (org-element-property :contents-end el))
+ "\n")))))
+ playlist))
+#+END_SRC
+
+And next we can parse and submit our listens. The data is provided in groups of 3 lines, with the first line the relative time in HH:MM after the show started, the second the track title and the third line the performer. I've included a basic rate limit of 30 calls per minute to not overload the ListenBrainz servers.
+
+#+BEGIN_SRC emacs-lisp
+ (while playlist
+ (let* ((songrelstart (split-string (pop playlist) ":"))
+ (starthour (string-to-number (pop songrelstart)))
+ (startminute (string-to-number (pop songrelstart)))
+ (songrelstartf (format-time-string "%F %R" (time-add starttime (* (+ (* starthour 60) startminute) 60))))
+ (title (pop playlist))
+ (performer (pop playlist)))
+ (progn
+ (listenbrainz-submit-historic-listen performer title (listenbrainz-timestamp (date-to-time songrelstartf)))
+ (sleep-for 2))))
+#+END_SRC