[rygel] server: Add a M3U resource to containers



commit 63bc34de234634345e2e301cdda4c4d35ef916fd
Author: Jens Georg <jensg openismus com>
Date:   Sun Jun 16 11:22:31 2013 +0200

    server: Add a M3U resource to containers

 src/librygel-server/filelist.am                    |    1 +
 src/librygel-server/rygel-http-get.vala            |    6 +-
 .../rygel-http-playlist-handler.vala               |   40 +++++++++--
 src/librygel-server/rygel-m3u-playlist.vala        |   79 ++++++++++++++++++++
 src/librygel-server/rygel-media-container.vala     |   67 +++++++++++------
 src/librygel-server/rygel-serializer.vala          |   56 +++++++++++---
 6 files changed, 206 insertions(+), 43 deletions(-)
---
diff --git a/src/librygel-server/filelist.am b/src/librygel-server/filelist.am
index dec2e1e..fcc0bbc 100644
--- a/src/librygel-server/filelist.am
+++ b/src/librygel-server/filelist.am
@@ -62,6 +62,7 @@ LIBRYGEL_SERVER_NONVAPI_SOURCE_FILES = \
        rygel-last-change-obj-mod.vala \
        rygel-last-change-st-done.vala \
        rygel-last-change.vala \
+       rygel-m3u-playlist.vala \
        rygel-media-query-action.vala \
        rygel-media-receiver-registrar.vala \
        rygel-panasonic-hacks.vala \
diff --git a/src/librygel-server/rygel-http-get.vala b/src/librygel-server/rygel-http-get.vala
index e977cea..d8882fc 100644
--- a/src/librygel-server/rygel-http-get.vala
+++ b/src/librygel-server/rygel-http-get.vala
@@ -66,8 +66,10 @@ internal class Rygel.HTTPGet : HTTPRequest {
                                                      this.cancellable);
         }
 
-        if (uri.playlist_format != null) {
-            this.handler = new HTTPPlaylistHandler (this.cancellable);
+        if (uri.playlist_format != null &&
+            HTTPPlaylistHandler.is_supported (uri.playlist_format)) {
+            this.handler = new HTTPPlaylistHandler (uri.playlist_format,
+                                                    this.cancellable);
         }
 
         if (this.handler == null) {
diff --git a/src/librygel-server/rygel-http-playlist-handler.vala 
b/src/librygel-server/rygel-http-playlist-handler.vala
index 7adf0b7..e2a4e39 100644
--- a/src/librygel-server/rygel-http-playlist-handler.vala
+++ b/src/librygel-server/rygel-http-playlist-handler.vala
@@ -29,10 +29,13 @@ internal class Rygel.PlaylistDatasource : Rygel.DataSource, Object {
     private uint8[] data;
     private HTTPServer server;
     private ClientHacks hacks;
+    private SerializerType playlist_type;
 
-    public PlaylistDatasource (MediaContainer container,
+    public PlaylistDatasource (SerializerType playlist_type,
+                               MediaContainer container,
                                HTTPServer     server,
                                ClientHacks?   hacks) {
+        this.playlist_type = playlist_type;
         this.container = container;
         this.server = server;
         this.hacks = hacks;
@@ -82,7 +85,7 @@ internal class Rygel.PlaylistDatasource : Rygel.DataSource, Object {
                                                               null);
 
             if (children != null) {
-                var serializer = new Serializer (SerializerType.DIDL_S);
+                var serializer = new Serializer (this.playlist_type);
                 children.serialize (serializer, this.server, this.hacks);
 
                 var xml = serializer.get_string ();
@@ -105,13 +108,38 @@ internal class Rygel.PlaylistDatasource : Rygel.DataSource, Object {
  * playlists (DIDL_S format as defined by DLNA) on-the-fly.
  */
 internal class Rygel.HTTPPlaylistHandler : Rygel.HTTPGetHandler {
-    public HTTPPlaylistHandler (Cancellable? cancellable) {
+    private SerializerType playlist_type;
+
+    public static bool is_supported (string playlist_format) {
+        return playlist_format == "DIDL_S" || playlist_format == "M3U";
+    }
+
+    public HTTPPlaylistHandler (string playlist_format,
+                                Cancellable? cancellable) {
+        if (playlist_format == "DIDL_S") {
+            this.playlist_type = SerializerType.DIDL_S;
+        } else if (playlist_format == "M3U") {
+            this.playlist_type = SerializerType.M3UEXT;
+        }
+
         this.cancellable = cancellable;
     }
 
     public override void add_response_headers (HTTPGet request)
                                                throws HTTPRequestError {
-        request.msg.response_headers.append ("Content-Type", "text/xml");
+        // TODO: Why do we use response_headers.append instead of set_content_type
+        switch (this.playlist_type) {
+            case SerializerType.DIDL_S:
+                request.msg.response_headers.append ("Content-Type",
+                                                     "text/xml");
+                break;
+            case SerializerType.M3UEXT:
+                request.msg.response_headers.append ("ContentType",
+                                                     "audio/x-mpegurl");
+                break;
+            default:
+                assert_not_reached ();
+        }
 
         base.add_response_headers (request);
     }
@@ -120,7 +148,8 @@ internal class Rygel.HTTPPlaylistHandler : Rygel.HTTPGetHandler {
                                               throws HTTPRequestError {
         try {
             var source = new PlaylistDatasource
-                                        (request.object as MediaContainer,
+                                        (this.playlist_type,
+                                         request.object as MediaContainer,
                                          request.http_server,
                                          request.hack);
 
@@ -134,7 +163,6 @@ internal class Rygel.HTTPPlaylistHandler : Rygel.HTTPGetHandler {
                                         (DIDLLiteObject didl_object,
                                          HTTPGet        request) {
         var protocol = request.http_server.get_protocol ();
-        debug ("=> Protocol of this http server is: %s", protocol);
 
         try {
             return request.object.add_resource (didl_object, null, protocol);
diff --git a/src/librygel-server/rygel-m3u-playlist.vala b/src/librygel-server/rygel-m3u-playlist.vala
new file mode 100644
index 0000000..ae333dd
--- /dev/null
+++ b/src/librygel-server/rygel-m3u-playlist.vala
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2013 Jens Georg.
+ *
+ * Authors: Jens Georg <mail jensge org>
+ *
+ * This file is part of Rygel.
+ *
+ * Rygel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Rygel is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+using Gee;
+using GUPnP;
+
+/**
+ * Serializer class that serializes to an EXTM3U playlist for use with normal
+ * media players or UPnP Renderers that don't support DIDL_S.
+ *
+ * For the description of the EXTM3U format, see
+ * http://en.wikipedia.org/wiki/M3U#Extended_M3U_directives
+ */
+internal class Rygel.M3UPlayList : Object {
+    private LinkedList<DIDLLiteItem> items;
+
+    // We need this writer for the namespaces, the document etc.
+    private DIDLLiteWriter writer;
+
+    public M3UPlayList () {
+        Object ();
+    }
+
+    public override void constructed () {
+        this.items = new LinkedList<DIDLLiteItem> ();
+        this.writer = new DIDLLiteWriter (null);
+    }
+
+
+    public DIDLLiteItem? add_item () {
+        this.items.add (this.writer.add_item ());
+
+        return this.items.last ();
+    }
+
+    public string get_string () {
+        var builder = new StringBuilder ("#EXTM3U\r\n");
+
+        foreach (var item in this.items) {
+            var resources = item.get_resources ();
+            if (resources != null) {
+                var authors = item.get_artists ();
+                builder.append_printf ("#EXTINF:%ld,",
+                                       resources.data.duration);
+                if (authors != null) {
+                    builder.append_printf ("%s - ",
+                                           authors.data.get_name () ??
+                                               _("Unknown"));
+                }
+
+                builder.append (item.title ?? _("Unknown"));
+                builder.append ("\r\n");
+                builder.append (resources.data.uri);
+                builder.append ("\r\n");
+            }
+        }
+
+        return builder.str;
+    }
+}
diff --git a/src/librygel-server/rygel-media-container.vala b/src/librygel-server/rygel-media-container.vala
index 97ecfb1..4ae62e1 100644
--- a/src/librygel-server/rygel-media-container.vala
+++ b/src/librygel-server/rygel-media-container.vala
@@ -310,6 +310,15 @@ public abstract class Rygel.MediaContainer : MediaObject {
             didl_container.restricted = true;
         }
 
+        this.add_resources (http_server, didl_container);
+
+        return didl_container;
+    }
+
+    internal void add_resources (Rygel.HTTPServer http_server,
+                                 DIDLLiteContainer didl_container)
+                                 throws Error {
+        // Add resource with container contents serialized to DIDL_S playlist
         var uri = new HTTPItemURI (this,
                                    http_server,
                                    -1,
@@ -318,11 +327,24 @@ public abstract class Rygel.MediaContainer : MediaObject {
                                    "DIDL_S");
         uri.extension = "xml";
 
-        this.add_resource (didl_container,
-                           uri.to_string (),
-                           http_server.get_protocol ());
+        var res = this.add_resource (didl_container,
+                                     uri.to_string (),
+                                     http_server.get_protocol ());
+        if (res != null) {
+            res.protocol_info.mime_type = "text/xml";
+            res.protocol_info.dlna_profile = "DIDL_S";
+        }
 
-        return didl_container;
+        // Add resource with container contents serialized to M3U playlist
+        uri = new HTTPItemURI (this, http_server, -1, -1, null, "M3U");
+        uri.extension = "m3u";
+
+        res = this.add_resource (didl_container,
+                                 uri.to_string (),
+                                 http_server.get_protocol ());
+        if (res != null) {
+            res.protocol_info.mime_type = "audio/x-mpegurl";
+        }
     }
 
     internal override DIDLLiteResource add_resource
@@ -331,29 +353,28 @@ public abstract class Rygel.MediaContainer : MediaObject {
                                          string         protocol,
                                          string?        import_uri = null)
                                          throws Error {
-        if (this.child_count > 0) {
-            var res = base.add_resource (didl_object,
-                                         uri,
-                                         protocol,
-                                         import_uri);
-
-            if (uri != null) {
-                res.uri = uri;
-            }
+        if (this.child_count <= 0) {
+            return null as DIDLLiteResource;
+        }
 
-            var protocol_info = new ProtocolInfo ();
-            protocol_info.mime_type = "text/xml";
-            protocol_info.dlna_profile = "DIDL_S";
-            protocol_info.protocol = protocol;
-            protocol_info.dlna_flags = DLNAFlags.DLNA_V15 |
-                                       DLNAFlags.CONNECTION_STALL |
-                                       DLNAFlags.BACKGROUND_TRANSFER_MODE;
-            res.protocol_info = protocol_info;
+        var res = base.add_resource (didl_object,
+                                     uri,
+                                     protocol,
+                                     import_uri);
 
-            return res;
+        if (uri != null) {
+            res.uri = uri;
         }
 
-        return null as DIDLLiteResource;
+        var protocol_info = new ProtocolInfo ();
+        protocol_info.mime_type = "";
+        protocol_info.protocol = protocol;
+        protocol_info.dlna_flags = DLNAFlags.DLNA_V15 |
+                                   DLNAFlags.CONNECTION_STALL |
+                                   DLNAFlags.BACKGROUND_TRANSFER_MODE;
+        res.protocol_info = protocol_info;
+
+        return res;
     }
 
     /**
diff --git a/src/librygel-server/rygel-serializer.vala b/src/librygel-server/rygel-serializer.vala
index e7a411b..0e56022 100644
--- a/src/librygel-server/rygel-serializer.vala
+++ b/src/librygel-server/rygel-serializer.vala
@@ -23,40 +23,67 @@
 using GUPnP;
 
 internal enum SerializerType {
+    /// Normal serialization of container/item using DIDL-Lite
     GENERIC_DIDL,
-    DIDL_S
+
+    /// Special version of a DIDL-Lite document for playlists, defined by DLNA
+    DIDL_S,
+
+    /// M3UEXT format as used by various media players
+    M3UEXT
 }
 
+/**
+ * Proxy class hiding the different serializers (DIDL, DIDL_S, M3U) behind a
+ * single implementation.
+ */
 internal class Rygel.Serializer : Object {
     private DIDLLiteWriter writer;
     private MediaCollection collection;
+    private M3UPlayList playlist;
+
+    // private properties
+    public SerializerType serializer_type { construct; private get; }
 
     public Serializer (SerializerType type) {
-        switch (type) {
+        Object (serializer_type: type);
+    }
+
+    public override void constructed () {
+        switch (this.serializer_type) {
             case SerializerType.GENERIC_DIDL:
                 this.writer = new DIDLLiteWriter (null);
                 break;
             case SerializerType.DIDL_S:
                 this.collection = new MediaCollection ();
                 break;
+            case SerializerType.M3UEXT:
+                this.playlist = new M3UPlayList ();
+                break;
             default:
                 assert_not_reached ();
         }
+
+        base.constructed ();
     }
 
     public DIDLLiteItem? add_item () {
-        if (writer != null) {
-            return this.writer.add_item ();
-        } else {
-            return this.collection.add_item ();
+        switch (this.serializer_type) {
+            case SerializerType.GENERIC_DIDL:
+                return this.writer.add_item ();
+            case SerializerType.DIDL_S:
+                return this.collection.add_item ();
+            case SerializerType.M3UEXT:
+                return this.playlist.add_item ();
+            default:
+                return null;
         }
     }
 
     public DIDLLiteContainer? add_container () {
-        if (writer != null) {
+        if (this.writer != null) {
             return this.writer.add_container ();
         } else {
-            // MediaCollection does not support this.
             return null;
         }
     }
@@ -68,10 +95,15 @@ internal class Rygel.Serializer : Object {
     }
 
     public string get_string () {
-        if (writer != null) {
-            return this.writer.get_string ();
-        } else {
-            return this.collection.get_string ();
+        switch (this.serializer_type) {
+            case SerializerType.GENERIC_DIDL:
+                return this.writer.get_string ();
+            case SerializerType.DIDL_S:
+                return this.collection.get_string ();
+            case SerializerType.M3UEXT:
+                return this.playlist.get_string ();
+            default:
+                return "";
         }
     }
 }


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