bongo-devel
[Top][All Lists]
Advanced

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

[bongo-devel] Support for streaming media metadata


From: Daniel Jensen
Subject: [bongo-devel] Support for streaming media metadata
Date: Sun, 11 Feb 2007 20:43:50 +0100
User-agent: Gnus/5.110006 (No Gnus v0.6) Emacs/22.0.92 (gnu/linux)

Here's my stream metadata thing, and boring text to go with it.

Currently it supports stream title/name, genre and information on
what's playing. This is pretty much modelled only after how VLC
appears to report metadata, so forgive me if I'm on the wrong track
here and there.

The idea is that backends (only VLC so far) store metadata from
streams. They run a hook when new data has arrived. Well, that's all
for the backends.

I wrote hook functions for Daniel's suggestions; those are functions
I've been using before so I guess most will want them. I also added
another couple of functions while I was at it.

If you want something to be added, fixed, removed; go ahead and
suggest it. Here are the hook functions:

* `bongo-use-stream-song-title' -- It simply sets the title for the
  currently playing song. This will automatically appear in the header
  line. And now you can use `bongo-show' as a hook function, to
  display info in the echo area.

  This does not parse the title into different infoset fields, but
  that can be arranged. I tried `bongo-default-infoset-from-file-name'
  without the base-name bit, and it worked just fine. Though, some
  streams send you data Bongo will have problems parsing, but that
  may not matter to you.

* `bongo-use-stream-title-as-track-title' -- like above, but uses the
  stream title instead. Some streams don't have the "now playing"
  data, use this to get something useful to display. This function is
  not used by default.

* `bongo-replace-uri-title-with-stream-title' -- this is an
  interesting function. It updates the playlist buffer with the stream
  title. The condition is that you accepted the default URI title when
  you inserted the URI. It will not replace a real title, because you
  may not like that.

* `bongo-use-stream-title/genre-tooltip -- this is not really
  necessary, but I couldn't think of a better place to put the genre
  data. It's also cool! It will put a tooltip on the currently playing
  line, telling you about the title and genre of the stream.

  This tooltip disappears when Bongo redisplays the line, but we can
  fix that. Maybe use `bongo-line-semantic-properties' or mess with
  the redisplay code. You decide. At least the tooltip is there while
  the track is playing.

Yup, that's it.

Oh, and here's a hack you may want for listening to HTTP streams that
redirect you (like those at live365.com), because VLC ignores it. You
need curl.

(defadvice bongo-start-vlc-player (before redirect-vlc-uris
                                    (file-name &optional extra-arguments)
                                    activate)
  "Use curl to pick up redirect locations in HTTP streams."
  (when (string-match "\\`http://www\\.live365\\.com"; file-name)
    (let ((http-header (shell-command-to-string
                        (format "curl -I -s '%s'" file-name))))
      (when (string-match "^Location: \\(.*\\)$" http-header)
        (setq file-name (match-string 1 http-header))))))

I tried this as a file name translator also. It might be an idea to
put something like it in Bongo, if there are many redirecting streams
around. What do you say?

And one final thing before I need to rest my hands. It's possible to
use this for integrating with Last.fm, but they don't like that. It's
probably best to let it be. However, if you are interested anyway, ask
me about it. I wrote something that worked some time ago, a modified
last.fm mode, but I haven't updated it.


diff -Naur bongo-old/bongo.el bongo-new/bongo.el
--- bongo-old/bongo.el  2007-02-08 22:38:07.000000000 +0100
+++ bongo-new/bongo.el  2007-02-11 20:13:28.000000000 +0100
@@ -4253,6 +4253,93 @@
            (bongo-player-paused/resumed player)))))
 
 
+;;;; Streaming media
+
+;;; A player backend can parse metadata from streaming media.
+;;; Stream metadata is stored as player properties:
+;;;
+;;;   stream-title      The name or title of the stream.
+;;;   stream-genre      The stream genre.
+;;;   stream-playing    What the stream is currently playing.
+;;;                     This may include artist, album and track info.
+;;;
+;;; When new metadata has arrived, Bongo runs the hook
+;;; `bongo-stream-metadata-changed-hook'.
+
+(defcustom bongo-stream-metadata-changed-hook
+  '(bongo-use-stream-song-title
+    bongo-use-stream-title/genre-tooltip
+    bongo-replace-uri-title-with-stream-title
+    bongo-show)
+  "Hook run when Bongo receives metadata from a playing stream."
+  :type 'hook
+  :options '(bongo-use-stream-song-title
+             bongo-use-stream-title-as-track-title
+             bongo-use-stream-title/genre-tooltip
+             bongo-replace-uri-title-with-stream-title
+             bongo-show)
+  :group 'bongo)
+
+(defun bongo-player-set-stream-metadata (player metadata)
+  "Store stream metadata for PLAYER.
+Argument METADATA is an alist of properties and values.
+Runs the hook `bongo-stream-metadata-changed-hook'."
+  ;; I haven't decided yet how to best store new metadata.
+  ;; For now, just use whatever we get and put the properties.
+  (dolist (property metadata)
+    (bongo-player-put player (car property) (cdr property)))
+  (run-hooks 'bongo-stream-metadata-changed-hook))
+
+(defun bongo-use-stream-song-title ()
+  "Update active player infoset with song title from stream."
+  (with-bongo-playlist-buffer
+    (let ((playing (bongo-player-get bongo-player 'stream-playing)))
+      (when playing
+        (bongo-player-put bongo-player
+            'infoset `((track (title . ,playing))))))))
+
+(defun bongo-use-stream-title-as-track-title ()
+  "Update active player infoset with stream title."
+  (with-bongo-playlist-buffer
+    (let ((title (bongo-player-get bongo-player 'stream-title)))
+      (when title
+        (bongo-player-put bongo-player
+            'infoset `((track (title . ,title))))))))
+
+(defun bongo-replace-uri-title-with-stream-title ()
+  "Set a new title from stream metadata for a playing URI track.
+The title is set only when the track has the default URI title."
+  (with-bongo-playlist-buffer
+    (save-excursion
+      (goto-char (bongo-point-at-current-track-line))
+      (let ((stream-title (bongo-player-get bongo-player 'stream-title))
+            (track-title (bongo-infoset-track-title (bongo-line-infoset))))
+        (when (equal track-title (bongo-line-file-name))
+          (bongo-line-set-property 'bongo-infoset
+                                   `((artist . unknown)
+                                     (album . unknown)
+                                     (track (title . ,stream-title))))
+          (bongo-redisplay-line))))))
+
+(defun bongo-use-stream-title/genre-tooltip ()
+  "Set a tooltip describing the stream for the current line."
+  (with-bongo-playlist-buffer
+    (save-excursion
+      (goto-char (bongo-point-at-current-track-line))
+      (let* ((title (bongo-player-get bongo-player 'stream-title))
+             (genre (bongo-player-get bongo-player 'stream-genre))
+             (tooltip (cond ((and title genre)
+                             (format "Title: %s\nGenre: %s" title genre))
+                            (title (concat "Title: " title))
+                            (genre (concat "Genre: " genre))))
+             (inhibit-read-only t))
+        (when tooltip
+          (put-text-property
+            (bongo-point-before-line)
+            (bongo-point-after-line)
+            'help-echo tooltip))))))
+
+
 ;;;; Backends
 
 (defun bongo-evaluate-program-argument (argument)
@@ -4721,49 +4808,91 @@
         (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 (title genre playing)
+            (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 title (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 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 playing (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 (or playing title genre)
+              (bongo-player-set-stream-metadata player
+                  `((stream-title . ,title)
+                    (stream-genre . ,genre)
+                    (stream-playing . ,playing)))))))
     ;; Getting errors in process filters is not fun, so stop.
     (error (bongo-stop)
            (signal (car condition) (cdr condition)))))
@@ -4773,6 +4902,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

reply via email to

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