[banshee] Mpris: Implement the Playlists interface



commit 345ddbd5a48396c922835feb02eac0538c67732e
Author: Bertrand Lorentz <bertrand lorentz gmail com>
Date:   Mon Dec 27 18:12:09 2010 +0100

    Mpris: Implement the Playlists interface
    
    This allows MPRIS clients to list our playlists and smart playlists and
    start playing any one of them.
    
    The org.mpris.MediaPlayer2.Playlists interface is part of the MPRIS v2.1
    specification available at http://www.mpris.org.

 src/Extensions/Banshee.Mpris/Banshee.Mpris.csproj  |    2 +
 .../Banshee.Mpris/Banshee.Mpris/IPlaylists.cs      |   56 +++++++
 .../Banshee.Mpris/Banshee.Mpris/MediaPlayer.cs     |  165 +++++++++++++++++++-
 .../Banshee.Mpris/Banshee.Mpris/MprisService.cs    |   22 +++
 src/Extensions/Banshee.Mpris/Makefile.am           |    1 +
 5 files changed, 239 insertions(+), 7 deletions(-)
---
diff --git a/src/Extensions/Banshee.Mpris/Banshee.Mpris.csproj b/src/Extensions/Banshee.Mpris/Banshee.Mpris.csproj
index 709870b..840d383 100644
--- a/src/Extensions/Banshee.Mpris/Banshee.Mpris.csproj
+++ b/src/Extensions/Banshee.Mpris/Banshee.Mpris.csproj
@@ -34,6 +34,7 @@
     <Compile Include="Banshee.Mpris\MprisService.cs" />
     <Compile Include="Banshee.Mpris\IPlayer.cs" />
     <Compile Include="Banshee.Mpris\IMediaPlayer.cs" />
+    <Compile Include="Banshee.Mpris\IPlaylists.cs" />
   </ItemGroup>
   <ItemGroup>
     <Reference Include="System" />
@@ -43,6 +44,7 @@
     <Reference Include="NDesk.DBus.GLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f6716e4f9b2ed099">
       <Package>ndesk-dbus-glib-1.0</Package>
     </Reference>
+    <Reference Include="System.Core" />
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\..\Core\Banshee.Core\Banshee.Core.csproj">
diff --git a/src/Extensions/Banshee.Mpris/Banshee.Mpris/IPlaylists.cs b/src/Extensions/Banshee.Mpris/Banshee.Mpris/IPlaylists.cs
new file mode 100644
index 0000000..f739851
--- /dev/null
+++ b/src/Extensions/Banshee.Mpris/Banshee.Mpris/IPlaylists.cs
@@ -0,0 +1,56 @@
+//
+// IPlaylists.cs
+//
+// Author:
+//   Bertrand Lorentz <bertrand lorentz gmail com>
+//
+// Copyright 2010 Bertrand Lorentz
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using NDesk.DBus;
+
+namespace Banshee.Mpris
+{
+    public struct Playlist
+    {
+        public ObjectPath Id;
+        public string Name;
+        public string Icon;
+    }
+
+    public struct MaybePlaylist
+    {
+        public bool Valid;
+        public Playlist Playlist;
+    }
+
+    [Interface ("org.mpris.MediaPlayer2.Playlists")]
+    public interface IPlaylists
+    {
+        uint PlaylistCount { get; }
+        string [] Orderings { get; }
+        MaybePlaylist ActivePlaylist { get; }
+
+        void ActivatePlaylist (ObjectPath playlist_id);
+        Playlist [] GetPlaylists (uint index, uint max_count, string order, bool reverse_order);
+    }
+}
+
diff --git a/src/Extensions/Banshee.Mpris/Banshee.Mpris/MediaPlayer.cs b/src/Extensions/Banshee.Mpris/Banshee.Mpris/MediaPlayer.cs
index 419f0ae..5b86685 100644
--- a/src/Extensions/Banshee.Mpris/Banshee.Mpris/MediaPlayer.cs
+++ b/src/Extensions/Banshee.Mpris/Banshee.Mpris/MediaPlayer.cs
@@ -30,6 +30,8 @@
 
 using System;
 using System.Collections.Generic;
+using System.Linq;
+using System.Text;
 
 using NDesk.DBus;
 using Hyena;
@@ -37,16 +39,20 @@ using Hyena;
 using Banshee.Gui;
 using Banshee.MediaEngine;
 using Banshee.PlaybackController;
+using Banshee.Playlist;
 using Banshee.ServiceStack;
+using Banshee.Sources;
 
 namespace Banshee.Mpris
 {
-    public class MediaPlayer : IMediaPlayer, IPlayer, IProperties
+    public class MediaPlayer : IMediaPlayer, IPlayer, IPlaylists, IProperties
     {
         private static string mediaplayer_interface_name = "org.mpris.MediaPlayer2";
         private static string player_interface_name = "org.mpris.MediaPlayer2.Player";
+        private static string playlists_interface_name = "org.mpris.MediaPlayer2.Playlists";
         private PlaybackControllerService playback_service;
         private PlayerEngineService engine_service;
+        private Dictionary<string, AbstractPlaylistSource> playlist_sources;
         private Dictionary<string, object> changed_properties;
         private List<string> invalidated_properties;
 
@@ -71,6 +77,7 @@ namespace Banshee.Mpris
         {
             playback_service = ServiceManager.PlaybackController;
             engine_service = ServiceManager.PlayerEngine;
+            playlist_sources = new Dictionary<string, AbstractPlaylistSource> ();
             changed_properties = new Dictionary<string, object> ();
             invalidated_properties = new List<string> ();
         }
@@ -317,15 +324,123 @@ namespace Banshee.Mpris
 
 #endregion
 
+#region IPlaylists
+        public uint PlaylistCount {
+            get {
+                return (uint)ServiceManager.SourceManager.FindSources<AbstractPlaylistSource> ().Count ();
+            }
+        }
+
+        private static string [] supported_playlist_orderings = { "Alphabetical", "UserDefined" };
+        public string [] Orderings {
+            get { return supported_playlist_orderings; }
+        }
+
+        private static Playlist dummy_playlist = new Playlist {
+            Id = new ObjectPath (DBusServiceManager.ObjectRoot),
+            Name = "",
+            Icon = "" };
+        public MaybePlaylist ActivePlaylist {
+            get {
+                // We want the source that is currently playing
+                var playlist_source = ServiceManager.PlaybackController.Source as AbstractPlaylistSource;
+                if (playlist_source == null) {
+                    return new MaybePlaylist { Valid = false, Playlist = dummy_playlist };
+                } else {
+                    return new MaybePlaylist { Valid = true,
+                        Playlist = BuildPlaylistFromSource (playlist_source) };
+                }
+            }
+        }
+
+        private ObjectPath MakeObjectPath (AbstractPlaylistSource playlist)
+        {
+            StringBuilder object_path_builder = new StringBuilder ();
+
+            object_path_builder.Append (DBusServiceManager.ObjectRoot);
+            object_path_builder.Append ("/Playlists/");
+
+            object_path_builder.Append (DBusServiceManager.MakeDBusSafeString (playlist.UniqueId));
+
+            string object_path = object_path_builder.ToString ();
+            playlist_sources[object_path] = playlist;
+
+            return new ObjectPath (object_path);
+        }
+
+        private string GetIconPath (Source source)
+        {
+            string icon_name = "image-missing";
+            Type icon_type = source.Properties.GetType ("Icon.Name");
+
+            if (icon_type == typeof (string)) {
+                icon_name = source.Properties.Get<string> ("Icon.Name");
+            } else if (icon_type == typeof (string [])) {
+                icon_name = source.Properties.Get<string[]> ("Icon.Name")[0];
+            }
+
+            string icon_path = Paths.Combine (Paths.GetInstalledDataDirectory ("icons"),
+                                       "hicolor", "22x22", "categories",
+                                       String.Concat (icon_name, ".png"));
+
+            return String.Concat ("file://", icon_path);
+        }
+
+        private Playlist BuildPlaylistFromSource (AbstractPlaylistSource source)
+        {
+            var mpris_playlist = new Playlist ();
+            mpris_playlist.Name = source.Name;
+            mpris_playlist.Id = MakeObjectPath (source);
+            mpris_playlist.Icon = GetIconPath (source);
+
+            return mpris_playlist;
+        }
+
+        public void ActivatePlaylist (ObjectPath playlist_id)
+        {
+            // TODO: Maybe try to find the playlist if it's not in the dictionary ?
+            var playlist = playlist_sources[playlist_id.ToString ()];
+
+            if (playlist != null) {
+                Log.DebugFormat ("MPRIS activating playlist {0}", playlist.Name);
+                ServiceManager.SourceManager.SetActiveSource (playlist);
+                ServiceManager.PlaybackController.Source = playlist;
+                ServiceManager.PlaybackController.First ();
+            }
+        }
+
+        public Playlist [] GetPlaylists (uint index, uint max_count, string order, bool reverse_order)
+        {
+            var playlist_sources = ServiceManager.SourceManager.FindSources<AbstractPlaylistSource> ();
+
+            switch (order) {
+                case "Alphabetical":
+                    playlist_sources = playlist_sources.OrderBy (p => p.Name);
+                    break;
+                case "UserDefined":
+                    playlist_sources = playlist_sources.OrderBy (p => p.Order);
+                    break;
+            }
+            if (reverse_order) {
+                playlist_sources = playlist_sources.Reverse ();
+            }
+
+            var playlists = new List<Playlist> ();
+            foreach (var pl in playlist_sources.Skip ((int)index).Take ((int)max_count)) {
+                playlists.Add (BuildPlaylistFromSource (pl));
+            }
+            return playlists.ToArray ();
+        }
+#endregion
+
 #region Signals
 
-        public void HandlePropertiesChange ()
+        private void HandlePropertiesChange (string interface_name)
         {
             PropertiesChangedHandler handler = properties_changed;
             if (handler != null) {
                 lock (changed_properties) {
-                    // Properties that trigger this signal are all on the Player interface
-                    handler (player_interface_name, changed_properties, invalidated_properties.ToArray ());
+                    handler (interface_name, changed_properties, invalidated_properties.ToArray ());
                     changed_properties.Clear ();
                     invalidated_properties.Clear ();
                 }
@@ -347,9 +462,20 @@ namespace Banshee.Mpris
                     string prop_name = prop.ToString ();
                     changed_properties[prop_name] = Get (player_interface_name, prop_name);
                 }
+                // TODO We could check if a property really has changed and only fire the event in that case
+                HandlePropertiesChange (player_interface_name);
+            }
+        }
+
+        public void AddPropertyChange (params PlaylistProperties [] properties)
+        {
+            lock (changed_properties) {
+                foreach (PlaylistProperties prop in properties) {
+                    string prop_name = prop.ToString ();
+                    changed_properties[prop_name] = Get (playlists_interface_name, prop_name);
+                }
+                HandlePropertiesChange (playlists_interface_name);
             }
-            // TODO We could check if a property really has changed and only fire the event in that case
-            HandlePropertiesChange ();
         }
 
 #endregion
@@ -363,6 +489,8 @@ namespace Banshee.Mpris
             "CanPlay", "CanSeek", "LoopStatus", "MaximumRate", "Metadata", "MinimumRate", "PlaybackStatus",
             "Position", "Rate", "Shuffle", "Volume" };
 
+        private static string [] playlist_properties = { "Orderings", "PlaylistCount", "ActivePlaylist" };
+
         public object Get (string interface_name, string propname)
         {
             if (interface_name == mediaplayer_interface_name) {
@@ -419,6 +547,17 @@ namespace Banshee.Mpris
                     default:
                         return null;
                 }
+            } else if (interface_name == playlists_interface_name) {
+                switch (propname) {
+                    case "Orderings":
+                        return Orderings;
+                    case "PlaylistCount":
+                        return PlaylistCount;
+                    case "ActivePlaylist":
+                        return ActivePlaylist;
+                    default:
+                        return null;
+                }
             } else {
                 return null;
             }
@@ -463,6 +602,10 @@ namespace Banshee.Mpris
                 foreach (string prop in player_properties) {
                     props.Add (prop, Get (interface_name, prop));
                 }
+            } else if (interface_name == playlists_interface_name) {
+                foreach (string prop in playlist_properties) {
+                    props.Add (prop, Get (interface_name, prop));
+                }
             }
 
             return props;
@@ -471,7 +614,7 @@ namespace Banshee.Mpris
 
 #endregion
 
-    // Those are all the properties that can trigger the PropertiesChanged signal
+    // Those are all the properties from the Player interface that can trigger the PropertiesChanged signal
     // The names must match exactly the names of the properties
     public enum PlayerProperties
     {
@@ -490,4 +633,12 @@ namespace Banshee.Mpris
         Metadata,
         Volume
     }
+
+    // Those are all the properties from the Playlist interface that can trigger the PropertiesChanged signal
+    // The names must match exactly the names of the properties
+    public enum PlaylistProperties
+    {
+        PlaylistCount,
+        ActivePlaylist
+    }
 }
diff --git a/src/Extensions/Banshee.Mpris/Banshee.Mpris/MprisService.cs b/src/Extensions/Banshee.Mpris/Banshee.Mpris/MprisService.cs
index 84a9ea8..b251080 100644
--- a/src/Extensions/Banshee.Mpris/Banshee.Mpris/MprisService.cs
+++ b/src/Extensions/Banshee.Mpris/Banshee.Mpris/MprisService.cs
@@ -34,7 +34,9 @@ using NDesk.DBus;
 using Hyena;
 using Banshee.MediaEngine;
 using Banshee.PlaybackController;
+using Banshee.Playlist;
 using Banshee.ServiceStack;
+using Banshee.Sources;
 
 namespace Banshee.Mpris
 {
@@ -63,6 +65,10 @@ namespace Banshee.Mpris
             ServiceManager.PlaybackController.RepeatModeChanged += OnRepeatModeChanged;
             ServiceManager.PlaybackController.ShuffleModeChanged += OnShuffleModeChanged;
 
+            ServiceManager.SourceManager.SourceAdded += OnSourceCountChanged;
+            ServiceManager.SourceManager.SourceRemoved += OnSourceCountChanged;
+            ServiceManager.PlaybackController.SourceChanged += OnPlayingSourceChanged;
+
             player = new MediaPlayer();
             Bus.Session.Register (MediaPlayer.Path, player);
 
@@ -80,6 +86,10 @@ namespace Banshee.Mpris
             ServiceManager.PlaybackController.RepeatModeChanged -= OnRepeatModeChanged;
             ServiceManager.PlaybackController.ShuffleModeChanged -= OnShuffleModeChanged;
 
+            ServiceManager.SourceManager.SourceAdded -= OnSourceCountChanged;
+            ServiceManager.SourceManager.SourceRemoved -= OnSourceCountChanged;
+            ServiceManager.PlaybackController.SourceChanged -= OnPlayingSourceChanged;
+
             Bus.Session.ReleaseName (bus_name);
         }
 
@@ -112,6 +122,18 @@ namespace Banshee.Mpris
             player.AddPropertyChange (PlayerProperties.Shuffle);
         }
 
+        private void OnSourceCountChanged (SourceEventArgs args)
+        {
+            if (args.Source is AbstractPlaylistSource) {
+                player.AddPropertyChange (PlaylistProperties.PlaylistCount);
+            }
+        }
+
+        private void OnPlayingSourceChanged (object o, EventArgs args)
+        {
+            player.AddPropertyChange (PlaylistProperties.ActivePlaylist);
+        }
+
         string IService.ServiceName {
             get { return "MprisService"; }
         }
diff --git a/src/Extensions/Banshee.Mpris/Makefile.am b/src/Extensions/Banshee.Mpris/Makefile.am
index d3f35ea..f418ee6 100644
--- a/src/Extensions/Banshee.Mpris/Makefile.am
+++ b/src/Extensions/Banshee.Mpris/Makefile.am
@@ -6,6 +6,7 @@ INSTALL_DIR = $(EXTENSIONS_INSTALL_DIR)
 SOURCES =  \
 	Banshee.Mpris/IMediaPlayer.cs \
 	Banshee.Mpris/IPlayer.cs \
+	Banshee.Mpris/IPlaylists.cs \
 	Banshee.Mpris/MediaPlayer.cs \
 	Banshee.Mpris/Metadata.cs \
 	Banshee.Mpris/MprisService.cs



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