bongo-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [bongo-devel] Re: Stream metadata


From: Daniel Brockman
Subject: Re: [bongo-devel] Re: Stream metadata
Date: Sun, 08 Apr 2007 18:26:03 +0200
User-agent: Gnus/5.11 (Gnus v5.11) Emacs/22.0.92 (gnu/linux)

address@hidden (Daniel Jensen) writes:

> Daniel Brockman <address@hidden> writes:
>
>> Here are my new suggestions:
>>
>>   - `stream-genre'
>>   - `stream-title'
>>   - `stream-part-title'
>
> Okay. For the record, I still prefer stream "name"!
> It sounds more natural to me.

Okay, let's go with that.  I changed that one back to
`stream-name', and kept the other two as above.

I have been massaging this code for a while now, trying to
make it fit nicely into Bongo.  I believe I've come up with
something reasonably clean (but the process has certainly
reminded me of why I started to consider deeper changes).

>> Thanks.  I should read it.  I want to understand Common Lisp.
>
> I'll warn you that the risk of becoming frustrated with Emacs
> Lisp in the future is high.

Bah.  I was in love with Scheme before I dug into Emacs Lisp. :-)

Anyway, please consider the following patch.  I believe it
is fit for installation, but you may want to make changes.

It is a move away from treating infosets as data stores,
and towards treating them as a means of data presentation.
That's not the way I originally thought of them, --- quite
the opposite, in fact, --- but it appears to be more natural
given the skew and evolution that the code has gone through.

It is also a move towards recognizing a special kind of
track for streams.  This also appears to be a more natural
way of meeting the goals we have for this feature.

Here's a brief description of how the code works now:

 * Players set their `stream-name', `stream-part-title'
   and `stream-genre' properties and notify Bongo by
   calling `bongo-player-metadata-changed'.

 * The `stream-name' and `stream-genre' properties are
   propagated (i.e., duplicated) to the track line.
   (This is because they should accompany the track wherever
   it goes --- it could be copied, enqueued, saved, etc.)
   They are stored in properties called `bongo-stream-name'
   and `bongo-stream-genre'.

 * The infoset for a URI track line that has at least one
   stream property is built (on demand) like so:

      `((artist . unknown)
        (album . unknown)
        (stream (uri . ,(bongo-line-file-name))
                (uri-title
                 . ,(bongo-line-get-property 'bongo-uri-title))
                (name
                 . ,(bongo-line-get-property 'bongo-stream-name))
                (genre
                 . ,(bongo-line-get-property 'bongo-stream-genre))
                (part-title . ,stream-part-title)))

   (As the stream part title is not stored in a track
   line property, it is taken from the player property.)

   The URI title is the user-specified title for the URI,
   which used to be stored in the infoset.  In case there's
   no URI title or stream name, the URI itself is included.

 * I don't know where I'm going with this and why I made
   this into a bulleted list so I'll just stop here.

diff -rN -u old-bongo/bongo.el new-bongo/bongo.el
--- old-bongo/bongo.el  2007-04-08 14:33:05.000000000 +0200
+++ new-bongo/bongo.el  2007-04-08 14:33:05.000000000 +0200
@@ -658,7 +658,27 @@
 When the expressions are evaluated,
  - `bongo-action-description' is bound to the action description;
  - `bongo-action-expression' is bound to the action expression;
- - `bongo-target' is short for `bongo-infoset-formattnig-target';
+ - `bongo-target' is short for `bongo-infoset-formatting-target';
+ - `bongo-line' is short for `bongo-infoset-formatting-target-line'."
+  :type '(repeat sexp)
+  :group 'bongo-display)
+
+(defcustom bongo-stream-format
+  '((or bongo-uri-title bongo-stream-name bongo-uri)
+    (when bongo-stream-genre
+      (concat " (" bongo-stream-genre ")"))
+    (when bongo-stream-part-title
+      (concat ": " bongo-stream-part-title)))
+  "Template for displaying stream tracks in Bongo.
+Value is a list of expressions, each evaluating to a string or nil.
+The values of the expressions are concatenated.
+When the expressions are evaluated,
+ - `bongo-uri' is bound to the URI of the stream;
+ - `bongo-uri-title' is bound to the URI title or nil;
+ - `bongo-stream-name' is bound to the stream name or nil;
+ - `bongo-stream-genre' is bound to the stream genre or nil;
+ - `bongo-stream-part-title' is bound to the stream part title;
+ - `bongo-target' is short for `bongo-infoset-formatting-target';
  - `bongo-line' is short for `bongo-infoset-formatting-target-line'."
   :type '(repeat sexp)
   :group 'bongo-display)
@@ -1896,7 +1921,31 @@
             (require 'format-spec)
             (format-spec bongo-track-format `((?t . ,bongo-title)
                                               (?i . ,bongo-index)))))))
+     ((stream)
+      ;; These variables are used in `bongo-stream-format',
+      ;; so their names are significant.
+      (let ((bongo-uri
+             (propertize (bongo-alist-get data 'uri)
+                         'face 'bongo-album-title))
+            (bongo-uri-title
+             (when (bongo-alist-get data 'uri-title)
+               (propertize (bongo-alist-get data 'uri-title)
+                           'face 'bongo-album-title)))
+            (bongo-stream-name
+             (when (bongo-alist-get data 'name)
+               (propertize (bongo-alist-get data 'name)
+                           'face 'bongo-album-title)))
+            (bongo-stream-genre
+             (when (bongo-alist-get data 'genre)
+               (bongo-alist-get data 'genre)))
+            (bongo-stream-part-title
+             (when (bongo-alist-get data 'part-title)
+               (propertize (bongo-alist-get data 'part-title)
+                           'face 'bongo-track-title))))
+        (bongo-format-string bongo-stream-format)))
      ((action)
+      ;; These variables are used in `bongo-action-format',
+      ;; so their names are significant.
       (let ((bongo-action-expression data)
             (bongo-action-description
              (let ((description-specifier
@@ -2596,8 +2645,40 @@
         (bongo-point-at-bol)
       (bongo-point-at-previous-track-line))))
 
-(defun bongo-infoset-from-action (action)
-  `((action . ,action)))
+(defun bongo-uri-track-infoset (&optional point)
+  "Return the infoset for the URI track at POINT.
+You should use `bongo-line-infoset' most of the time."
+  (let ((file-name
+         (bongo-line-file-name point))
+        (uri-title
+         (bongo-line-get-property 'bongo-uri-title point))
+        (stream-name
+         (bongo-line-get-property 'bongo-stream-name point))
+        (stream-genre
+         (bongo-line-get-property 'bongo-stream-genre point))
+        (stream-part-title
+         (let ((player (bongo-line-get-property 'bongo-player point)))
+           (and player (bongo-player-running-p player)
+                (bongo-player-get player 'stream-part-title)))))
+    (cond ((or stream-name stream-genre stream-part-title)
+           `((artist . unknown)
+             (album . unknown)
+             (stream (uri . ,file-name)
+                     (uri-title . ,uri-title)
+                     (name . ,stream-name)
+                     (genre . ,stream-genre)
+                     (part-title . ,stream-part-title))))
+          (uri-title
+           `((artist . unknown)
+             (album . unknown)
+             (track (title . ,(or uri-title stream-name)))))
+          (t
+           (bongo-infoset-from-file-name file-name)))))
+
+(defun bongo-action-track-infoset (&optional point)
+  "Return the infoset for the action track at POINT.
+You should use `bongo-line-infoset' most of the time."
+  `((action . ,(bongo-line-action point))))
 
 (defun bongo-track-infoset (&optional point)
   "Return the infoset for the track at POINT.
@@ -2605,10 +2686,13 @@
   (unless (bongo-track-line-p point)
     (error "Point is not on a track line"))
   (or (bongo-line-get-property 'bongo-infoset point)
-      (cond ((bongo-line-file-name point)
-             (bongo-infoset-from-file-name (bongo-line-file-name point)))
-            ((bongo-line-action point)
-             (bongo-infoset-from-action (bongo-line-action point))))))
+      (let (file-name)
+        (cond ((setq file-name (bongo-line-file-name point))
+               (if (bongo-uri-p file-name)
+                   (bongo-uri-track-infoset point)
+                 (bongo-infoset-from-file-name file-name)))
+              ((bongo-line-action point)
+               (bongo-action-track-infoset))))))
 
 (defun bongo-header-infoset (&optional point)
   "Return the infoset for the header at POINT.
@@ -3075,8 +3159,9 @@
 (defvar bongo-line-semantic-properties
   ;; When changing this, consider also changing
   ;; `bongo-line-serializable-properties'.
-  (list 'bongo-file-name 'bongo-action
-        'bongo-infoset 'bongo-backend
+  (list 'bongo-file-name 'bongo-action 'bongo-backend
+        'bongo-infoset 'bongo-uri-title
+        'bongo-stream-name 'bongo-stream-genre
         'bongo-fields 'bongo-external-fields
         'bongo-header 'bongo-collapsed
         'bongo-marked 'bongo-reference-counted-marker
@@ -3101,6 +3186,7 @@
         (position (bongo-point-at-eol point)))
     (bongo-ensure-final-newline)
     (put-text-property position (1+ position) name value)))
+(put 'bongo-line-set-property 'lisp-indent-function 1)
 
 (defun bongo-line-set-properties (properties &optional point)
   "Set the text properties PROPERTIES on the line at POINT.
@@ -4524,6 +4610,44 @@
     (when (bufferp bongo-seek-buffer)
       (bongo-seek-redisplay))))
 
+(defcustom bongo-player-metadata-changed-hook '(bongo-show)
+  "Normal hook run when a Bongo player receives new metadata."
+  :options '(bongo-show)
+  :type 'hook
+  :group 'bongo)
+
+(defvar bongo-player-metadata-changed-functions nil
+  "Abnormal hook run when a Bongo player receives new metadata.")
+
+(defun bongo-player-metadata-changed (player)
+  "Take actions appropriate for when PLAYER's metadata changed.
+Changing metadata is provided by some Internet radio streams.
+This function runs the hooks `bongo-player-metadata-changed-hook'
+  and `bongo-player-metadata-changed-functions'.
+The following metadata properties are currently used:
+  `stream-name'        - The name of the stream.
+  `stream-genre'       - The genre of the stream.
+  `stream-part-title'  - The title of the part that is
+                         currently being streamed."
+  (save-current-buffer
+    (when (buffer-live-p (bongo-player-buffer player))
+      (set-buffer (bongo-player-buffer player)))
+    (run-hook-with-args 'bongo-stream-metadata-changed-functions player)
+    (when (bongo-buffer-p)
+      (save-excursion
+        (goto-char (bongo-point-at-current-track-line))
+        (bongo-line-set-property 'bongo-stream-name
+          (bongo-player-get player 'stream-name))
+        (bongo-line-set-property 'bongo-stream-genre
+          (bongo-player-get player 'stream-genre))
+        (bongo-player-put player 'infoset (bongo-line-infoset))
+        (when bongo-header-line-mode
+          (bongo-update-header-line-string))
+        (when bongo-mode-line-indicator-mode
+          (bongo-update-mode-line-indicator-string))
+        (bongo-redisplay-line))
+      (run-hooks 'bongo-stream-metadata-changed-hook))))
+
 (defun bongo-player-backend-name (player)
   "Return the name of PLAYER's backend."
   (car player))
@@ -5244,49 +5368,99 @@
         (with-temp-buffer
           (insert string)
           (goto-char (point-min))
-          (while (not (eobp))
-            (cond
-             ((looking-at (eval-when-compile
-                            (rx (and line-start
-                                     "status change:"
-                                     (zero-or-more (or space "("))
-                                     "play state:"
-                                     (zero-or-more space)
-                                     (submatch (one-or-more digit))
-                                     (zero-or-more (or space ")"))
-                                     line-end))))
-              (case (string-to-number (match-string 1))
-                ((1 3)
-                 (bongo-player-put player 'paused nil)
-                 (bongo-player-paused/resumed player)
-                 (when (null (bongo-player-get player 'timer))
-                   (bongo-vlc-player-start-timer player)))
-                ((2 4)
-                 (bongo-player-put player 'paused t)
-                 (bongo-player-paused/resumed player))))
-             ((looking-at (eval-when-compile
-                            (rx (and line-start
-                                     (optional
-                                      (and "[" (zero-or-more digit) "]"))
-                                     (zero-or-more space)
-                                     "main playlist: nothing to play"
-                                     line-end))))
-              (process-send-string process "quit\n"))
-             ((looking-at (eval-when-compile
-                            (rx (and line-start
-                                     (submatch (one-or-more digit))
-                                     (zero-or-more space)
-                                     line-end))))
-              (when (bongo-player-get player 'pending-queries)
-                (let ((value (string-to-number (match-string 1))))
-                  (ecase (bongo-player-shift player 'pending-queries)
-                    (time
-                     (bongo-player-update-elapsed-time player value)
-                     (bongo-player-times-changed player))
-                    (length
-                     (bongo-player-update-total-time player value)
-                     (bongo-player-times-changed player)))))))
-            (forward-line))))
+          (let (stream-name stream-genre stream-part-title)
+            (while (not (eobp))
+              (cond ((looking-at
+                      (eval-when-compile
+                        (rx (and line-start
+                                 "status change:"
+                                 (zero-or-more (or space "("))
+                                 "play state:"
+                                 (zero-or-more space)
+                                 (submatch (one-or-more digit))
+                                 (zero-or-more (or space ")"))
+                                 line-end))))
+                     (case (string-to-number (match-string 1))
+                       ((1 3)
+                        (bongo-player-put player 'paused nil)
+                        (bongo-player-paused/resumed player)
+                        (when (null (bongo-player-get player 'timer))
+                          (bongo-vlc-player-start-timer player)))
+                       ((2 4)
+                        (bongo-player-put player 'paused t)
+                        (bongo-player-paused/resumed player))))
+                    ((looking-at
+                      (eval-when-compile
+                        (rx (and line-start
+                                 (optional
+                                  (and "[" (zero-or-more digit) "]"))
+                                 (zero-or-more space)
+                                 "main input debug:"
+                                 (zero-or-more space)
+                                 "- 'Title' = '"
+                                 (submatch (zero-or-more not-newline))
+                                 "'"
+                                 line-end))))
+                     (setq stream-name (match-string 1)))
+                    ((looking-at
+                      (eval-when-compile
+                        (rx (and line-start
+                                 (optional
+                                  (and "[" (zero-or-more digit) "]"))
+                                 (zero-or-more space)
+                                 "main input debug:"
+                                 (zero-or-more space)
+                                 "- 'Genre' = '"
+                                 (submatch (zero-or-more not-newline))
+                                 "'"
+                                 line-end))))
+                     (setq stream-genre (match-string 1)))
+                    ((looking-at
+                      (eval-when-compile
+                        (rx (and line-start
+                                 (optional
+                                  (and "[" (zero-or-more digit) "]"))
+                                 (zero-or-more space)
+                                 "main input debug:"
+                                 (zero-or-more space)
+                                 "- 'Now Playing' = '"
+                                 (submatch (zero-or-more not-newline))
+                                 "'"
+                                 line-end))))
+                     (setq stream-part-title (match-string 1)))
+                    ((looking-at
+                      (eval-when-compile
+                        (rx (and line-start
+                                 (optional
+                                  (and "[" (zero-or-more digit) "]"))
+                                 (zero-or-more space)
+                                 "main playlist: nothing to play"
+                                 line-end))))
+                     (process-send-string process "quit\n"))
+                    ((looking-at
+                      (eval-when-compile
+                        (rx (and line-start
+                                 (submatch (one-or-more digit))
+                                 (zero-or-more space)
+                                 line-end))))
+                     (when (bongo-player-get player 'pending-queries)
+                       (let ((value (string-to-number (match-string 1))))
+                         (ecase (bongo-player-shift player 'pending-queries)
+                           (time
+                            (bongo-player-update-elapsed-time player value)
+                            (bongo-player-times-changed player))
+                           (length
+                            (bongo-player-update-total-time player value)
+                            (bongo-player-times-changed player)))))))
+              (forward-line))
+            (when stream-name
+              (bongo-player-put player 'stream-name stream-name))
+            (when stream-genre
+              (bongo-player-put player 'stream-genre stream-genre))
+            (when stream-part-title
+              (bongo-player-put player 'stream-part-title stream-part-title))
+            (when (or stream-name stream-genre stream-part-title)
+              (bongo-stream-metadata-changed player)))))
     ;; Getting errors in process filters is not fun, so stop.
     (error (bongo-stop)
            (signal (car condition) (cdr condition)))))
@@ -5296,6 +5470,8 @@
          (arguments (append
                      (when bongo-vlc-interactive
                        (append (list "-I" "rc" "--rc-fake-tty")
+                               (when (bongo-uri-p file-name)
+                                 (list "-vv"))
                                (when (eq window-system 'w32)
                                  (list "--rc-quiet"))))
                      (bongo-evaluate-program-arguments
@@ -7039,11 +7215,7 @@
                               nil nil uri)))
      (list uri title)))
   (with-bongo-buffer
-    (apply 'bongo-insert-line 'bongo-file-name uri
-           (when title
-             (list 'bongo-infoset `((artist . unknown)
-                                    (album . unknown)
-                                    (track (title . ,title)))))))
+    (bongo-insert-line 'bongo-file-name uri))
   (when (and (interactive-p) (not (bongo-buffer-p)))
     (message "Inserted URI: %s"
              (bongo-format-infoset
@@ -8333,21 +8505,20 @@
 NEW-URI is the new URI; NEW-TITLE, if non-nil, is the new title."
   (interactive
    (when (bongo-uri-track-line-p)
-     (list (read-from-minibuffer "Change URI to: " (bongo-line-file-name))
-           (let ((old-title
-                  (cdr (assq 'title
-                             (cdr (assq 'track
-                                        (bongo-line-infoset)))))))
-             (read-from-minibuffer "Change URI title to: " old-title)))))
+     (list (read-from-minibuffer
+            "Change URI to: " (bongo-line-file-name))
+           (read-from-minibuffer
+            "Change URI title to: "
+            (or (bongo-line-get-property 'bongo-uri-title)
+                (bongo-line-get-property 'bongo-stream-name))))))
   (with-point-at-bongo-track point
     (when (not (bongo-uri-track-line-p))
       (error "No URI track at point"))
     (bongo-line-set-property 'bongo-file-name new-uri)
     (when new-title
-      (bongo-line-set-property 'bongo-infoset
-                               `((artist . unknown)
-                                 (album . unknown)
-                                 (track (title . ,new-title)))))
+      (bongo-line-set-property 'bongo-uri-title
+                               (and (not (equal new-title ""))
+                                    new-title)))
     (bongo-redisplay-line)))
 
 (defun bongo-rename-action-track (new-action &optional point)
@@ -8452,8 +8623,9 @@
 (defvar bongo-line-serializable-properties
   ;; When changing this, consider also changing
   ;; `bongo-line-semantic-properties'.
-  (list 'bongo-file-name 'bongo-action
-        'bongo-infoset 'bongo-backend
+  (list 'bongo-file-name 'bongo-action 'bongo-backend
+        'bongo-infoset 'bongo-uri-title
+        'bongo-stream-name 'bongo-stream-genre
         'bongo-fields 'bongo-external-fields
         'bongo-header 'bongo-collapsed)
   "List of serializable text properties used in Bongo buffers.
   
-- 
Daniel Brockman <address@hidden>

reply via email to

[Prev in Thread] Current Thread [Next in Thread]