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 \
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) {
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;
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 ();
* 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);
}
throws HTTPRequestError {
try {
var source = new PlaylistDatasource
- (request.object as MediaContainer,
+ (this.playlist_type,
+ request.object as MediaContainer,
request.http_server,
request.hack);
(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);
--- /dev/null
+/*
+ * 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;
+ }
+}
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,
"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
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;
}
/**
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;
}
}
}
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 "";
}
}
}