[rygel] renderer: Turn PlayerController into an interface



commit ac922555b81299f86ec405f6d54fb843002f8f58
Author: Richard Röjfors <richard rojfors gmail com>
Date:   Sun Jun 22 19:36:19 2014 +0200

    renderer: Turn PlayerController into an interface
    
    There is a need to be able to reimplement the rygel-player-controller
    By changing the class to become an interface, other classes can
    implement it.
    The current implementation is moved to a default controller.
    This controller is instantiated by get_controller of the plugin as
    before, but now the get_controller function is defined virtual.
    Making it possible for plugins to reimplement and return another
    controller.
    
    Signed-off-by: Richard Röjfors <richard rojfors gmail com>
    
    https://bugzilla.gnome.org/show_bug.cgi?id=732057

 src/librygel-renderer/filelist.am                  |    5 +-
 .../rygel-default-player-controller.vala           |  417 ++++++++++++++++++++
 .../rygel-media-renderer-plugin.vala               |    4 +-
 src/librygel-renderer/rygel-player-controller.vala |  378 ++-----------------
 4 files changed, 445 insertions(+), 359 deletions(-)
---
diff --git a/src/librygel-renderer/filelist.am b/src/librygel-renderer/filelist.am
index 3f5cb08..d5e68a0 100644
--- a/src/librygel-renderer/filelist.am
+++ b/src/librygel-renderer/filelist.am
@@ -1,11 +1,12 @@
 LIBRYGEL_RENDERER_VAPI_SOURCE_FILES = \
        rygel-media-renderer-plugin.vala \
        rygel-media-player.vala \
-       rygel-media-renderer.vala
+       rygel-media-renderer.vala \
+       rygel-player-controller.vala
 
 LIBRYGEL_RENDERER_NONVAPI_SOURCE_FILES = \
        rygel-av-transport.vala \
-       rygel-player-controller.vala \
+       rygel-default-player-controller.vala \
        rygel-rendering-control.vala \
        rygel-sink-connection-manager.vala \
        rygel-time-utils.vala \
diff --git a/src/librygel-renderer/rygel-default-player-controller.vala 
b/src/librygel-renderer/rygel-default-player-controller.vala
new file mode 100644
index 0000000..93ff541
--- /dev/null
+++ b/src/librygel-renderer/rygel-default-player-controller.vala
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) 2012 Intel Corporation.
+ * Copyright (C) 2014 Atlantic PuffinPack AB.
+ *
+ * Author: Richard Röjfors <richard puffinpack se>
+ *
+ * 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 GUPnP;
+
+/**
+ * This class keeps track of global states that are not dependant on the
+ * RygelMediaPlayer.
+ *
+ * These states are:
+ * # URI
+ * # MetaData
+ * # Number of tracks
+ * # Current track
+ * # Playback state
+ *
+ * In case of playlists this class will also control the player. It needs to
+ * proxy the playback state to react on end of item to be able to switch to
+ * the next item.
+ */
+internal class Rygel.DefaultPlayerController : Rygel.PlayerController, Object {
+    private const int DEFAULT_IMAGE_TIMEOUT = 15;
+    private const string CONFIG_SECTION = "Renderer";
+    private const string TIMEOUT_KEY = "image-timeout";
+    private const string DIDL_FRAME_TEMPLATE = "<DIDL-Lite " +
+        "xmlns:dc=\"http://purl.org/dc/elements/1.1/\"; " +
+        "xmlns:upnp=\"urn:schemas-upnp-org:metadata-1-0/upnp/\" " +
+        "xmlns:dlna=\"urn:schemas-dlna-org:metadata-1-0/\" " +
+        "xmlns=\"urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/\">" +
+        "%s</DIDL-Lite>";
+
+    /* private (construction) properties */
+    public MediaPlayer player { construct; private get; }
+    public string protocol_info { construct; private get; }
+
+    /* public properties */
+
+    /* this._playback_state mirrors player.playback_state without including
+     * non-UPnP "EOS" value. It is updated from notify_state_cb */
+    [CCode (notify = false)]
+    public string playback_state {
+        get { return this._playback_state; }
+        set { this.player.playback_state = value; }
+    }
+
+    [CCode (notify = false)]
+    public uint n_tracks {
+        get { return this._n_tracks; }
+        private set {
+            if (value != this._n_tracks) {
+                this._n_tracks = value;
+                this.notify_property ("n-tracks");
+            }
+        }
+        default = 0;
+    }
+
+    [CCode (notify = false)]
+    public uint track {
+        get { return this._track; }
+        set {
+            if (value != this._track) {
+                this._track = value;
+                this.apply_track ();
+                this.notify_property ("track");
+            }
+        }
+        default = 0;
+    }
+
+    public string uri { owned get; private set; default = ""; }
+    public string metadata { owned get; private set; default = ""; }
+
+    [CCode (notify = false)]
+    public string track_uri {
+        owned get {
+            if (this.player.uri != null) {
+                return Markup.escape_text (this.player.uri);
+            } else {
+                return "";
+            }
+        }
+
+        private set {
+            this.player.uri = value;
+        }
+    }
+
+    [CCode (notify = false)]
+    public string track_metadata {
+        owned get { return this.player.metadata ?? ""; }
+
+        private set {
+            if (value.has_prefix ("&lt;")) {
+                this.player.metadata = this.unescape (value);
+            } else {
+                this.player.metadata = value;
+            }
+        }
+    }
+
+    public string next_uri { owned get; private set; default = ""; }
+    public string next_metadata { owned get; private set; default = ""; }
+
+    public string current_transport_actions {
+        owned get {
+            string actions = null;
+            switch (this.playback_state) {
+                case "PLAYING":
+                case "TRANSITIONING":
+                    actions = "Stop,Seek,Pause";
+                    break;
+                case "STOPPED":
+                    actions = "Play";
+                    break;
+                case "PAUSED_PLAYBACK":
+                    actions = "Stop,Play,Seek";
+                    break;
+                default:
+                    break;
+            }
+
+            if (actions == null) {
+                return "";
+            }
+
+            if (this.track < this.n_tracks) {
+                actions += ",Next";
+            }
+            if (this.track > 1) {
+                actions += ",Previous";
+            }
+
+            if (this.player.can_seek) {
+                actions += ",X_DLNA_SeekTime";
+            }
+            if (actions != null && this.player.can_seek_bytes) {
+                actions += ",X_DLNA_SeekByte";
+            }
+
+            if (actions != null &&
+                this.player.allowed_playback_speeds.length > 1) {
+                string play_speeds = "";
+                foreach (var speed in this.player.allowed_playback_speeds) {
+                    if (speed != "1") {
+                        if (play_speeds == "") {
+                            play_speeds = ",X_DLNA_PS=" + speed;
+                        } else {
+                            play_speeds += "\\," + speed;
+                        }
+                    }
+                }
+                actions += play_speeds;
+            }
+
+            return actions;
+        }
+    }
+
+    // Private members
+    private List<DIDLLiteItem> playlist;
+    private uint timeout_id;
+    private uint default_image_timeout;
+    private Configuration config;
+
+    private string next_features;
+    private string next_mime;
+    private MediaCollection next_collection;
+
+    // Private property variables
+    private uint _n_tracks;
+    private uint _track;
+    private string _playback_state = "NO_MEDIA_PRESENT";
+
+    public DefaultPlayerController (MediaPlayer player, string protocol_info) {
+        Object (player : player, protocol_info : protocol_info);
+    }
+
+    public override void constructed () {
+        base.constructed ();
+
+        this.player.notify["playback-state"].connect (this.notify_state_cb);
+        this.player.notify["uri"].connect (this.notify_uri_cb);
+        this.player.notify["metadata"].connect (this.notify_metadata_cb);
+
+        this.config = MetaConfig.get_default ();
+        this.config.setting_changed.connect (this.on_setting_changed);
+        this.default_image_timeout = DEFAULT_IMAGE_TIMEOUT;
+        this.on_setting_changed (CONFIG_SECTION, TIMEOUT_KEY);
+    }
+
+    public bool next () {
+        // Try advancing in playlist
+        if (this.track < this.n_tracks) {
+            this.track++;
+
+            return true;
+        }
+
+        // Try playing next_uri
+        if (this.next_uri != "") {
+            if (this.next_collection != null) {
+                this.set_playlist_uri (this.next_uri,
+                                       this.next_metadata,
+                                       this.next_collection);
+            } else {
+                this.set_single_play_uri (this.next_uri,
+                                          this.next_metadata,
+                                          this.next_mime,
+                                          this.next_features);
+            }
+
+            this.next_uri = "";
+            this.next_metadata = "";
+            this.next_mime = null;
+            this.next_features = null;
+            this.next_collection = null;
+
+            return true;
+        }
+
+        return false;
+    }
+
+    public bool previous () {
+        if (this.track <= 1) {
+            return false;
+        }
+
+        this.track--;
+
+        return true;
+    }
+
+    public void set_single_play_uri (string uri,
+                                     string metadata,
+                                     string? mime,
+                                     string? features)
+    {
+        if (this.timeout_id != 0) {
+            Source.remove (this.timeout_id);
+            this.timeout_id = 0;
+        }
+
+        this.metadata = this.unescape (metadata);
+        this.uri = uri;
+
+        this.player.mime_type = mime ?? "";
+        this.player.content_features = features ?? "*";
+
+        this.track_metadata = this.metadata;
+        this.track_uri = this.uri;
+
+        this.playlist = null;
+
+        if (this.uri == "") {
+            this.n_tracks = 0;
+            this.track = 0;
+        } else {
+            this.n_tracks = 1;
+            this.track = 1;
+        }
+    }
+
+    public void set_playlist_uri (string uri,
+                                  string metadata,
+                                  MediaCollection collection) {
+        if (this.timeout_id != 0) {
+            Source.remove (this.timeout_id);
+            this.timeout_id = 0;
+        }
+
+        this.metadata = this.unescape (metadata);
+        this.uri = uri;
+
+        this.playlist = collection.get_items ();
+        this.n_tracks = this.playlist.length ();
+
+        // bypass track setter: we want to run apply_track()
+        // even if track value does not change
+        var need_notify = (this.track != 1);
+        this._track = 1;
+        this.apply_track ();
+        if (need_notify) {
+            this.notify_property ("track");
+        }
+    }
+
+    public void set_next_single_play_uri (string uri,
+                                          string metadata,
+                                          string? mime,
+                                          string? features) {
+        this.next_uri = uri;
+        this.next_metadata = metadata;
+        this.next_mime = mime;
+        this.next_features = features;
+        this.next_collection = null;
+    }
+
+    public void set_next_playlist_uri (string uri,
+                                       string metadata,
+                                       MediaCollection collection) {
+        this.next_uri = uri;
+        this.next_metadata = metadata;
+        this.next_mime = null;
+        this.next_features = null;
+        this.next_collection = collection;
+    }
+
+    private void notify_state_cb (Object player, ParamSpec p) {
+        var state = this.player.playback_state;
+        if (state == "EOS") {
+            // Play next item in playlist, play next_uri, or move to STOPPED
+            Idle.add (() => {
+                if (!this.next ()) {
+                    this.reset ();
+                }
+
+                return false;
+            });
+        } else if (this._playback_state != state) {
+            // mirror player value in _playback_state and notify
+            this._playback_state = state;
+            this.notify_property ("playback-state");
+        }
+    }
+
+    private void notify_uri_cb (Object player, ParamSpec p) {
+        notify_property ("track-uri");
+    }
+
+    private void notify_metadata_cb (Object player, ParamSpec p) {
+        notify_property ("track-metadata");
+    }
+
+    private void apply_track () {
+        // We only have something to do here if we have playlist items
+        if (this.playlist != null) {
+            var item = this.playlist.nth (this.track - 1).data;
+
+            var res = item.get_compat_resource (this.protocol_info, true);
+            this.track_metadata = DIDL_FRAME_TEMPLATE.printf
+                                        (item.get_xml_string ());
+            this.track_uri = res.get_uri ();
+
+            if (item.upnp_class.has_prefix ("object.item.image") &&
+                this.playback_state != "STOPPED") {
+                this.setup_image_timeouts (item.lifetime);
+            }
+        }
+    }
+
+    private void reset () {
+        this.playback_state = "STOPPED";
+        this.track = 1;
+    }
+
+    private void setup_image_timeouts (long lifetime) {
+        // For images, we handle the timeout here. Either the item carries a
+        // dlna:lifetime tag, then we use that or we use a default timeout of
+        // 5 minutes.
+        var timeout = this.default_image_timeout;
+        if (lifetime > 0) {
+            timeout = (uint) lifetime;
+        }
+
+        debug ("Item is image, setup timer: %ld", timeout);
+
+        if (this.timeout_id != 0) {
+            Source.remove (this.timeout_id);
+        }
+
+        this.timeout_id = Timeout.add_seconds ((uint) timeout, () => {
+            this.timeout_id = 0;
+            if (!this.next ()) {
+                this.reset ();
+            }
+
+            return false;
+        });
+    }
+
+    private void on_setting_changed (string section, string key) {
+        if (section != CONFIG_SECTION && key != TIMEOUT_KEY) {
+            return;
+        }
+
+        try {
+            this.default_image_timeout = config.get_int (CONFIG_SECTION,
+                                                         TIMEOUT_KEY,
+                                                         0,
+                                                         int.MAX);
+        } catch (Error error) {
+            this.default_image_timeout = DEFAULT_IMAGE_TIMEOUT;
+        }
+
+        debug ("New image timeout: %lu", this.default_image_timeout);
+    }
+}
diff --git a/src/librygel-renderer/rygel-media-renderer-plugin.vala 
b/src/librygel-renderer/rygel-media-renderer-plugin.vala
index 4d27406..fd0c795 100644
--- a/src/librygel-renderer/rygel-media-renderer-plugin.vala
+++ b/src/librygel-renderer/rygel-media-renderer-plugin.vala
@@ -107,9 +107,9 @@ public class Rygel.MediaRendererPlugin : Rygel.Plugin {
         return null;
     }
 
-    internal PlayerController get_controller () {
+    internal virtual PlayerController get_controller () {
         if (this.controller == null) {
-            this.controller = new PlayerController (this.get_player (),
+            this.controller = new DefaultPlayerController (this.get_player (),
                                                     this.get_protocol_info ());
         }
 
diff --git a/src/librygel-renderer/rygel-player-controller.vala 
b/src/librygel-renderer/rygel-player-controller.vala
index 24f8469..93f5a36 100644
--- a/src/librygel-renderer/rygel-player-controller.vala
+++ b/src/librygel-renderer/rygel-player-controller.vala
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2012 Intel Corporation.
+ * Copyright (C) 2014 Atlantic PuffinPack AB.
  *
  * Author: Jens Georg <jensg openismus com>
  *
@@ -35,386 +36,53 @@ using GUPnP;
  * proxy the playback state to react on end of item to be able to switch to
  * the next item.
  */
-internal class Rygel.PlayerController : Object {
-    private const int DEFAULT_IMAGE_TIMEOUT = 15;
-    private const string CONFIG_SECTION = "Renderer";
-    private const string TIMEOUT_KEY = "image-timeout";
-    private const string DIDL_FRAME_TEMPLATE = "<DIDL-Lite " +
-        "xmlns:dc=\"http://purl.org/dc/elements/1.1/\"; " +
-        "xmlns:upnp=\"urn:schemas-upnp-org:metadata-1-0/upnp/\" " +
-        "xmlns:dlna=\"urn:schemas-dlna-org:metadata-1-0/\" " +
-        "xmlns=\"urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/\">" +
-        "%s</DIDL-Lite>";
-
-    /* private (construction) properties */
-    public MediaPlayer player { construct; private get; }
-    public string protocol_info { construct; private get; }
+public interface Rygel.PlayerController : GLib.Object {
 
     /* public properties */
 
     /* this._playback_state mirrors player.playback_state without including
      * non-UPnP "EOS" value. It is updated from notify_state_cb */
-    [CCode (notify = false)]
-    public string playback_state {
-        get { return this._playback_state; }
-        set { this.player.playback_state = value; }
-    }
-
-    [CCode (notify = false)]
-    public uint n_tracks {
-        get { return this._n_tracks; }
-        private set {
-            if (value != this._n_tracks) {
-                this._n_tracks = value;
-                this.notify_property ("n-tracks");
-            }
-        }
-        default = 0;
-    }
-
-    [CCode (notify = false)]
-    public uint track {
-        get { return this._track; }
-        set {
-            if (value != this._track) {
-                this._track = value;
-                this.apply_track ();
-                this.notify_property ("track");
-            }
-        }
-        default = 0;
-    }
-
-    public string uri { get; private set; default = ""; }
-    public string metadata { get; private set; default = ""; }
-
-    [CCode (notify = false)]
-    public string track_uri {
-        owned get {
-            if (this.player.uri != null) {
-                return Markup.escape_text (this.player.uri);
-            } else {
-                return "";
-            }
-        }
-
-        private set {
-            this.player.uri = value;
-        }
-    }
-
-    [CCode (notify = false)]
-    public string track_metadata {
-        owned get { return this.player.metadata ?? ""; }
-
-        private set {
-            if (value.has_prefix ("&lt;")) {
-                this.player.metadata = this.unescape (value);
-            } else {
-                this.player.metadata = value;
-            }
-        }
-    }
-
-    public string next_uri { get; private set; default = ""; }
-    public string next_metadata { get; private set; default = ""; }
-
-    public string current_transport_actions {
-        owned get {
-            string actions = null;
-            switch (this.playback_state) {
-                case "PLAYING":
-                case "TRANSITIONING":
-                    actions = "Stop,Seek,Pause";
-                    break;
-                case "STOPPED":
-                    actions = "Play";
-                    break;
-                case "PAUSED_PLAYBACK":
-                    actions = "Stop,Play,Seek";
-                    break;
-                default:
-                    break;
-            }
-
-            if (actions == null) {
-                return "";
-            }
-
-            if (this.track < this.n_tracks) {
-                actions += ",Next";
-            }
-            if (this.track > 1) {
-                actions += ",Previous";
-            }
+    public abstract string playback_state { get; set; }
 
-            if (this.player.can_seek) {
-                actions += ",X_DLNA_SeekTime";
-            }
-            if (actions != null && this.player.can_seek_bytes) {
-                actions += ",X_DLNA_SeekByte";
-            }
+    public abstract uint n_tracks { get; protected set; }
 
-            if (actions != null &&
-                this.player.allowed_playback_speeds.length > 1) {
-                string play_speeds = "";
-                foreach (var speed in this.player.allowed_playback_speeds) {
-                    if (speed != "1") {
-                        if (play_speeds == "") {
-                            play_speeds = ",X_DLNA_PS=" + speed;
-                        } else {
-                            play_speeds += "\\," + speed;
-                        }
-                    }
-                }
-                actions += play_speeds;
-            }
+    public abstract uint track { get; set; }
 
-            return actions;
-        }
-    }
-
-    // Private members
-    private List<DIDLLiteItem> playlist;
-    private uint timeout_id;
-    private uint default_image_timeout;
-    private Configuration config;
-
-    private string next_features;
-    private string next_mime;
-    private MediaCollection next_collection;
+    public abstract string uri { owned get; protected set; }
+    public abstract string metadata { owned get; protected set; }
 
-    // Private property variables
-    private uint _n_tracks;
-    private uint _track;
-    private string _playback_state = "NO_MEDIA_PRESENT";
-
-    public PlayerController (MediaPlayer player, string protocol_info) {
-        Object (player : player, protocol_info : protocol_info);
-    }
-
-    public override void constructed () {
-        base.constructed ();
-
-        this.player.notify["playback-state"].connect (this.notify_state_cb);
-        this.player.notify["uri"].connect (this.notify_uri_cb);
-        this.player.notify["metadata"].connect (this.notify_metadata_cb);
-
-        this.config = MetaConfig.get_default ();
-        this.config.setting_changed.connect (this.on_setting_changed);
-        this.default_image_timeout = DEFAULT_IMAGE_TIMEOUT;
-        this.on_setting_changed (CONFIG_SECTION, TIMEOUT_KEY);
-    }
+    public abstract string track_uri { owned get; protected set; }
 
-    public bool next () {
-        // Try advancing in playlist
-        if (this.track < this.n_tracks) {
-            this.track++;
+    public abstract string track_metadata { owned get; protected set; }
 
-            return true;
-        }
+    public abstract string next_uri { owned get; protected set; }
+    public abstract string next_metadata { owned get; protected set; }
 
-        // Try playing next_uri
-        if (this.next_uri != "") {
-            if (this.next_collection != null) {
-                this.set_playlist_uri (this.next_uri,
-                                       this.next_metadata,
-                                       this.next_collection);
-            } else {
-                this.set_single_play_uri (this.next_uri,
-                                          this.next_metadata,
-                                          this.next_mime,
-                                          this.next_features);
-            }
+    public abstract string current_transport_actions { owned get; }
 
-            this.next_uri = "";
-            this.next_metadata = "";
-            this.next_mime = null;
-            this.next_features = null;
-            this.next_collection = null;
+    public abstract bool next ();
 
-            return true;
-        }
+    public abstract bool previous ();
 
-        return false;
-    }
-
-    public bool previous () {
-        if (this.track <= 1) {
-            return false;
-        }
-
-        this.track--;
-
-        return true;
-    }
-
-    public void set_single_play_uri (string uri,
+    public abstract void set_single_play_uri (string uri,
                                      string metadata,
                                      string? mime,
-                                     string? features)
-    {
-        if (this.timeout_id != 0) {
-            Source.remove (this.timeout_id);
-            this.timeout_id = 0;
-        }
-
-        this.metadata = this.unescape (metadata);
-        this.uri = uri;
-
-        this.player.mime_type = mime ?? "";
-        this.player.content_features = features ?? "*";
+                                     string? features);
 
-        this.track_metadata = this.metadata;
-        this.track_uri = this.uri;
-
-        this.playlist = null;
-
-        if (this.uri == "") {
-            this.n_tracks = 0;
-            this.track = 0;
-        } else {
-            this.n_tracks = 1;
-            this.track = 1;
-        }
-    }
-
-    public void set_playlist_uri (string uri,
+    public abstract void set_playlist_uri (string uri,
                                   string metadata,
-                                  MediaCollection collection) {
-        if (this.timeout_id != 0) {
-            Source.remove (this.timeout_id);
-            this.timeout_id = 0;
-        }
-
-        this.metadata = this.unescape (metadata);
-        this.uri = uri;
+                                  MediaCollection collection);
 
-        this.playlist = collection.get_items ();
-        this.n_tracks = this.playlist.length ();
-
-        // bypass track setter: we want to run apply_track()
-        // even if track value does not change
-        var need_notify = (this.track != 1);
-        this._track = 1;
-        this.apply_track ();
-        if (need_notify) {
-            this.notify_property ("track");
-        }
-    }
-
-    public void set_next_single_play_uri (string uri,
+    public abstract void set_next_single_play_uri (string uri,
                                           string metadata,
                                           string? mime,
-                                          string? features) {
-        this.next_uri = uri;
-        this.next_metadata = metadata;
-        this.next_mime = mime;
-        this.next_features = features;
-        this.next_collection = null;
-    }
+                                          string? features);
 
-    public void set_next_playlist_uri (string uri,
+    public abstract void set_next_playlist_uri (string uri,
                                        string metadata,
-                                       MediaCollection collection) {
-        this.next_uri = uri;
-        this.next_metadata = metadata;
-        this.next_mime = null;
-        this.next_features = null;
-        this.next_collection = collection;
-    }
-
-    private void notify_state_cb (Object player, ParamSpec p) {
-        var state = this.player.playback_state;
-        if (state == "EOS") {
-            // Play next item in playlist, play next_uri, or move to STOPPED
-            Idle.add (() => {
-                if (!this.next ()) {
-                    this.reset ();
-                }
-
-                return false;
-            });
-        } else if (this._playback_state != state) {
-            // mirror player value in _playback_state and notify
-            this._playback_state = state;
-            this.notify_property ("playback-state");
-        }
-    }
-
-    private void notify_uri_cb (Object player, ParamSpec p) {
-        notify_property ("track-uri");
-    }
-
-    private void notify_metadata_cb (Object player, ParamSpec p) {
-        notify_property ("track-metadata");
-    }
-
-    private void apply_track () {
-        // We only have something to do here if we have playlist items
-        if (this.playlist != null) {
-            var item = this.playlist.nth (this.track - 1).data;
-
-            var res = item.get_compat_resource (this.protocol_info, true);
-            this.track_metadata = DIDL_FRAME_TEMPLATE.printf
-                                        (item.get_xml_string ());
-            this.track_uri = res.get_uri ();
-
-            if (item.upnp_class.has_prefix ("object.item.image") &&
-                this.playback_state != "STOPPED") {
-                this.setup_image_timeouts (item.lifetime);
-            }
-        }
-    }
-
-    private void reset () {
-        this.playback_state = "STOPPED";
-        this.track = 1;
-    }
-
-    private void setup_image_timeouts (long lifetime) {
-        // For images, we handle the timeout here. Either the item carries a
-        // dlna:lifetime tag, then we use that or we use a default timeout of
-        // 5 minutes.
-        var timeout = this.default_image_timeout;
-        if (lifetime > 0) {
-            timeout = (uint) lifetime;
-        }
-
-        debug ("Item is image, setup timer: %ld", timeout);
-
-        if (this.timeout_id != 0) {
-            Source.remove (this.timeout_id);
-        }
-
-        this.timeout_id = Timeout.add_seconds ((uint) timeout, () => {
-            this.timeout_id = 0;
-            if (!this.next ()) {
-                this.reset ();
-            }
-
-            return false;
-        });
-    }
-
-    private void on_setting_changed (string section, string key) {
-        if (section != CONFIG_SECTION && key != TIMEOUT_KEY) {
-            return;
-        }
-
-        try {
-            this.default_image_timeout = config.get_int (CONFIG_SECTION,
-                                                         TIMEOUT_KEY,
-                                                         0,
-                                                         int.MAX);
-        } catch (Error error) {
-            this.default_image_timeout = DEFAULT_IMAGE_TIMEOUT;
-        }
-
-        debug ("New image timeout: %lu", this.default_image_timeout);
-    }
+                                       MediaCollection collection);
 
-    private string unescape (string input) {
+    protected string unescape (string input) {
         var result = input.replace ("&quot;", "\"");
         result = result.replace ("&lt;", "<");
         result = result.replace ("&gt;", ">");


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