diff --git a/.cvsignore b/.cvsignore index 6ff345a..f9177d4 100644 --- a/.cvsignore +++ b/.cvsignore @@ -1 +1 @@ -rhythmbox-0.11.4.tar.bz2 +rhythmbox-0.11.5.tar.bz2 diff --git a/gsd-media-player-api.patch b/gsd-media-player-api.patch deleted file mode 100644 index b0c71e7..0000000 --- a/gsd-media-player-api.patch +++ /dev/null @@ -1,35 +0,0 @@ -Index: plugins/mmkeys/rb-mmkeys-plugin.c -=================================================================== ---- plugins/mmkeys/rb-mmkeys-plugin.c (revision 5546) -+++ plugins/mmkeys/rb-mmkeys-plugin.c (working copy) -@@ -295,9 +295,9 @@ - GError *error = NULL; - - plugin->proxy = dbus_g_proxy_new_for_name (bus, -- "org.gnome.SettingsDaemon", -- "/org/gnome/SettingsDaemon", -- "org.gnome.SettingsDaemon"); -+ "org.gnome.SettingsDaemon", -+ "/org/gnome/SettingsDaemon/MediaKeys", -+ "org.gnome.SettingsDaemon.MediaKeys"); - if (plugin->proxy != NULL) { - dbus_g_proxy_call (plugin->proxy, - "GrabMediaPlayerKeys", &error, -@@ -308,7 +308,7 @@ - if (error == NULL) { - GtkWindow *window; - -- rb_debug ("created dbus proxy for org.gnome.SettingsDaemon; grabbing keys"); -+ rb_debug ("created dbus proxy for org.gnome.SettingsDaemon.MediaKeys; grabbing keys"); - dbus_g_object_register_marshaller (rb_marshal_VOID__STRING_STRING, - G_TYPE_NONE, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID); - -@@ -336,7 +336,7 @@ - /* settings daemon dbus service doesn't exist. - * just silently fail. - */ -- rb_debug ("org.gnome.SettingsDaemon dbus service not found"); -+ rb_debug ("org.gnome.SettingsDaemon.MediaKeys dbus service not found"); - g_error_free (error); - } else { - g_warning ("Unable to grab media player keys: %s", error->message); diff --git a/rb-activate-generic-players-from-uri.patch b/rb-activate-generic-players-from-uri.patch deleted file mode 100644 index 8f5465a..0000000 --- a/rb-activate-generic-players-from-uri.patch +++ /dev/null @@ -1,92 +0,0 @@ -Index: sources/rb-removable-media-source.c -=================================================================== ---- sources/rb-removable-media-source.c (revision 5601) -+++ sources/rb-removable-media-source.c (working copy) -@@ -62,6 +62,8 @@ - static void impl_paste (RBSource *source, GList *entries); - #endif - static gboolean impl_receive_drag (RBSource *asource, GtkSelectionData *data); -+static guint impl_want_uri (RBSource *source, const char *uri); -+static gboolean impl_uri_is_source (RBSource *source, const char *uri); - - typedef struct - { -@@ -102,6 +104,8 @@ - source_class->impl_delete = NULL; - source_class->impl_get_config_widget = NULL; - source_class->impl_show_popup = (RBSourceFeatureFunc) rb_false_function; -+ source_class->impl_want_uri = impl_want_uri; -+ source_class->impl_uri_is_source = impl_uri_is_source; - - browser_source_class->impl_get_paned_key = NULL; - browser_source_class->impl_has_drop_support = (RBBrowserSourceFeatureFunc) rb_false_function; -@@ -367,6 +371,50 @@ - - #endif - -+static guint -+impl_want_uri (RBSource *source, const char *uri) -+{ -+ GnomeVFSVolume *volume; -+ char *activation_uri; -+ int retval, len; -+ -+ retval = 0; -+ -+ /* A default version for use with the audio players -+ * that use mass storage */ -+ if (g_str_has_prefix (uri, "file://") == FALSE) -+ return 0; -+ -+ g_object_get (G_OBJECT (source), -+ "volume", &volume, -+ NULL); -+ if (volume == NULL) -+ return 0; -+ -+ activation_uri = gnome_vfs_volume_get_activation_uri (volume); -+ if (activation_uri == NULL) -+ return 0; -+ -+ len = strlen (uri); -+ if (uri[len - 1] == '/') { -+ if (strncmp (uri, activation_uri, len - 1) == 0) -+ retval = 100; -+ } else if (strcmp (uri, activation_uri) == 0) -+ retval = 100; -+ -+ g_free (activation_uri); -+ -+ return retval; -+} -+ -+static gboolean -+impl_uri_is_source (RBSource *source, const char *uri) -+{ -+ if (impl_want_uri (source, uri) == 100) -+ return TRUE; -+ return FALSE; -+} -+ - static RhythmDB * - get_db_for_source (RBSource *source) - { -Index: shell/rb-shell.c -=================================================================== ---- shell/rb-shell.c (revision 5601) -+++ shell/rb-shell.c (working copy) -@@ -3327,6 +3327,14 @@ - uri, error)) - return FALSE; - } -+ } else if (result == TOTEM_PL_PARSER_RESULT_IGNORED && rb_uri_is_local (uri)) { -+ /* That happens for directories */ -+ playlist_source = rb_shell_guess_source_for_uri (shell, uri); -+ if (playlist_source == NULL || rb_source_want_uri (playlist_source, uri) < 100) { -+ rb_debug ("%s is a directory, but doesn't have a source, adding as a dir", uri); -+ if (!rb_shell_add_uri (shell, uri, NULL, NULL, error)) -+ return FALSE; -+ } - } else { - rb_debug ("%s didn't parse as a playlist", uri); - if (!rb_shell_add_uri (shell, uri, NULL, NULL, error)) diff --git a/rb-automake-warning.patch b/rb-automake-warning.patch deleted file mode 100644 index 76ef5a0..0000000 --- a/rb-automake-warning.patch +++ /dev/null @@ -1,14 +0,0 @@ -Index: podcast/Makefile.am -=================================================================== ---- podcast/Makefile.am (revision 5561) -+++ podcast/Makefile.am (working copy) -@@ -55,8 +55,7 @@ - librhythmbox_itms_detection_plugin_la_LDFLAGS = \ - -avoid-version \ - -export-symbols $(srcdir)/plugin.symbols \ -- -module \ -- $(AM_LDFLAGS) -+ -module - - endif - diff --git a/rb-ipod-save-artwork.patch b/rb-ipod-save-artwork.patch deleted file mode 100644 index 7363eee..0000000 --- a/rb-ipod-save-artwork.patch +++ /dev/null @@ -1,204 +0,0 @@ ---- trunk/plugins/ipod/rb-ipod-source.c 2008/01/01 14:11:29 5526 -+++ trunk/plugins/ipod/rb-ipod-source.c 2008/01/02 12:08:13 5527 -@@ -48,15 +48,11 @@ - #include "rhythmdb.h" - #include "rb-cut-and-paste-code.h" - --static GObject *rb_ipod_source_constructor (GType type, -+static GObject *rb_ipod_source_constructor (GType type, - guint n_construct_properties, - GObjectConstructParam *construct_properties); - static void rb_ipod_source_dispose (GObject *object); - --static GObject *rb_ipod_source_constructor (GType type, guint n_construct_properties, -- GObjectConstructParam *construct_properties); --static void rb_ipod_source_dispose (GObject *object); -- - static gboolean impl_show_popup (RBSource *source); - static void impl_move_to_trash (RBSource *asource); - static void rb_ipod_load_songs (RBiPodSource *source); -@@ -82,6 +78,11 @@ - const gchar *extension); - static gchar* ipod_path_from_unix_path (const gchar *mount_point, - const gchar *unix_path); -+static gboolean rb_ipod_song_artwork_add_cb (RhythmDB *db, -+ RhythmDBEntry *entry, -+ const gchar *property_name, -+ const GValue *metadata, -+ RBiPodSource *isource); - #endif - static RhythmDB *get_db_for_source (RBiPodSource *source); - -@@ -108,6 +109,13 @@ - GQueue *offline_plays; - } RBiPodSourcePrivate; - -+#ifdef ENABLE_IPOD_WRITING -+typedef struct { -+ RBiPodSourcePrivate *priv; -+ GdkPixbuf *pixbuf; -+} RBiPodSongArtworkAddData; -+#endif -+ - RB_PLUGIN_DEFINE_TYPE(RBiPodSource, - rb_ipod_source, - RB_TYPE_REMOVABLE_MEDIA_SOURCE) -@@ -186,6 +194,15 @@ - - rb_ipod_load_songs (source); - -+#ifdef ENABLE_IPOD_WRITING -+ RhythmDB *db = get_db_for_source (RB_IPOD_SOURCE (source)); -+ g_signal_connect_object (db, -+ "entry-extra-metadata-notify::rb:coverArt", -+ G_CALLBACK (rb_ipod_song_artwork_add_cb), -+ RB_IPOD_SOURCE(source), 0); -+ g_object_unref (G_OBJECT (db)); -+#endif -+ - return G_OBJECT (source); - } - -@@ -1140,6 +1157,82 @@ - g_hash_table_remove (priv->artwork_request_map, entry); - } - -+static gboolean -+rb_add_artwork_whole_album_cb (GtkTreeModel *query_model, -+ GtkTreePath *path, -+ GtkTreeIter *iter, -+ RBiPodSongArtworkAddData *artwork_data) -+{ -+ RhythmDBEntry *entry; -+ Itdb_Track *song; -+ -+ entry = rhythmdb_query_model_iter_to_entry (RHYTHMDB_QUERY_MODEL (query_model), iter); -+ -+ song = g_hash_table_lookup (artwork_data->priv->entry_map, entry); -+ -+ if (song->has_artwork == 0x01) { -+ return FALSE; -+ } -+ -+ rb_ipod_db_set_thumbnail (artwork_data->priv->ipod_db, song, artwork_data->pixbuf); -+ -+ return FALSE; -+} -+ -+static gboolean -+rb_ipod_song_artwork_add_cb (RhythmDB *db, -+ RhythmDBEntry *entry, -+ const gchar *property_name, -+ const GValue *metadata, -+ RBiPodSource *isource) -+{ -+ RBiPodSourcePrivate *priv = IPOD_SOURCE_GET_PRIVATE (isource); -+ Itdb_Device *device; -+ Itdb_Track *song; -+ GdkPixbuf *pixbuf; -+ GtkTreeModel *query_model; -+ RBiPodSongArtworkAddData artwork_data; -+ -+ if (metadata == NULL) { -+ return FALSE; -+ } -+ -+ if (G_VALUE_HOLDS (metadata, GDK_TYPE_PIXBUF) == FALSE) { -+ return FALSE; -+ } -+ -+ song = g_hash_table_lookup (priv->entry_map, entry); -+ if (song == NULL) { -+ return FALSE; -+ } -+ -+ device = rb_ipod_db_get_device (priv->ipod_db); -+ if (device == NULL || itdb_device_supports_artwork (device) == FALSE) { -+ return FALSE; -+ } -+ -+ pixbuf = GDK_PIXBUF (g_value_get_object (metadata)); -+ -+ query_model = GTK_TREE_MODEL (rhythmdb_query_model_new_empty (db)); -+ -+ rhythmdb_do_full_query (db, RHYTHMDB_QUERY_RESULTS (query_model), -+ RHYTHMDB_QUERY_PROP_EQUALS, -+ RHYTHMDB_PROP_ARTIST, song->artist, -+ RHYTHMDB_QUERY_PROP_EQUALS, -+ RHYTHMDB_PROP_ALBUM, song->album, -+ RHYTHMDB_QUERY_END); -+ -+ artwork_data.priv = priv; -+ artwork_data.pixbuf = pixbuf; -+ -+ gtk_tree_model_foreach (query_model, -+ (GtkTreeModelForeachFunc) rb_add_artwork_whole_album_cb, -+ &artwork_data); -+ -+ g_object_unref(query_model); -+ return FALSE; -+} -+ - static void - request_artwork (RBiPodSource *isource, - RhythmDBEntry *entry, ---- trunk/plugins/ipod/rb-ipod-source.c 2008/01/02 12:08:13 5527 -+++ trunk/plugins/ipod/rb-ipod-source.c 2008/01/02 13:34:52 5528 -@@ -1169,7 +1169,8 @@ - entry = rhythmdb_query_model_iter_to_entry (RHYTHMDB_QUERY_MODEL (query_model), iter); - - song = g_hash_table_lookup (artwork_data->priv->entry_map, entry); -- -+ g_return_val_if_fail (song != NULL, FALSE); -+ - if (song->has_artwork == 0x01) { - return FALSE; - } -@@ -1192,7 +1193,8 @@ - GdkPixbuf *pixbuf; - GtkTreeModel *query_model; - RBiPodSongArtworkAddData artwork_data; -- -+ RhythmDBEntryType entry_type; -+ - if (metadata == NULL) { - return FALSE; - } -@@ -1210,12 +1212,16 @@ - if (device == NULL || itdb_device_supports_artwork (device) == FALSE) { - return FALSE; - } -- -+ -+ g_object_get (G_OBJECT (isource), "entry-type", &entry_type, NULL); -+ - pixbuf = GDK_PIXBUF (g_value_get_object (metadata)); - - query_model = GTK_TREE_MODEL (rhythmdb_query_model_new_empty (db)); - - rhythmdb_do_full_query (db, RHYTHMDB_QUERY_RESULTS (query_model), -+ RHYTHMDB_QUERY_PROP_EQUALS, -+ RHYTHMDB_PROP_ENTRY_ID, entry_type, - RHYTHMDB_QUERY_PROP_EQUALS, - RHYTHMDB_PROP_ARTIST, song->artist, - RHYTHMDB_QUERY_PROP_EQUALS, -@@ -1228,7 +1234,7 @@ - gtk_tree_model_foreach (query_model, - (GtkTreeModelForeachFunc) rb_add_artwork_whole_album_cb, - &artwork_data); -- -+ g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE, entry_type); - g_object_unref(query_model); - return FALSE; - } ---- trunk/plugins/ipod/rb-ipod-source.c 2008/02/02 19:24:40 5558 -+++ trunk/plugins/ipod/rb-ipod-source.c 2008/02/14 14:11:22 5579 -@@ -1233,7 +1233,7 @@ - - rhythmdb_do_full_query (db, RHYTHMDB_QUERY_RESULTS (query_model), - RHYTHMDB_QUERY_PROP_EQUALS, -- RHYTHMDB_PROP_ENTRY_ID, entry_type, -+ RHYTHMDB_PROP_TYPE, entry_type, - RHYTHMDB_QUERY_PROP_EQUALS, - RHYTHMDB_PROP_ARTIST, song->artist, - RHYTHMDB_QUERY_PROP_EQUALS, diff --git a/rhythmbox-0.11.4-bigger-pane.patch b/rhythmbox-0.11.4-bigger-pane.patch deleted file mode 100644 index a1bf22b..0000000 --- a/rhythmbox-0.11.4-bigger-pane.patch +++ /dev/null @@ -1,31 +0,0 @@ -Index: data/rhythmbox.schemas -=================================================================== ---- data/rhythmbox.schemas (revision 5614) -+++ data/rhythmbox.schemas (working copy) -@@ -106,7 +106,7 @@ - /apps/rhythmbox/state/paned_position - rhythmbox - int -- 88 -+ 160 - - Position of main window pane - Position of main window pane. -@@ -117,7 +117,7 @@ - /apps/rhythmbox/state/window_width - rhythmbox - int -- 600 -+ 800 - - Main window width - Main window width. -@@ -155,7 +155,7 @@ - /apps/rhythmbox/state/window_height - rhythmbox - int -- 600 -+ 550 - - Main window height - Main window height. diff --git a/rhythmbox-0.11.4-updated-upnp.patch b/rhythmbox-0.11.4-updated-upnp.patch deleted file mode 100644 index 80ab074..0000000 --- a/rhythmbox-0.11.4-updated-upnp.patch +++ /dev/null @@ -1,1618 +0,0 @@ -diff -uprN rhythmbox-0.11.4.old/plugins/coherence/coherence.rb-plugin.desktop.in rhythmbox-0.11.4/plugins/coherence/coherence.rb-plugin.desktop.in ---- rhythmbox-0.11.4.old/plugins/coherence/coherence.rb-plugin.desktop.in 2007-07-08 11:12:43.000000000 +0100 -+++ rhythmbox-0.11.4/plugins/coherence/coherence.rb-plugin.desktop.in 2008-03-13 16:55:54.000000000 +0000 -@@ -2,8 +2,8 @@ - Loader=python - Module=upnp_coherence - IAge=1 --_Name=UPnP sharing support --_Description=Adds support for playing media from, and sending media to UPnP/DLNA network devices --Authors=James Livingston --Copyright=Copyright © 2007 James Livingston --Website=http://www.rhythmbox.org/ -+_Name=DLNA/UPnP sharing and control support -+_Description=Adds support for playing media from and sending media to DLNA/UPnP network devices, and enables Rhythmbox to be controlled by a DLNA/UPnP ControlPoint -+Authors=James Livingston , Frank Scholz -+Copyright=Copyright © 2007,2008 James Livingston & Frank Scholz -+Website=http://www.rhythmbox.org/ - https://coherence.beebits.net -diff -uprN rhythmbox-0.11.4.old/plugins/coherence/upnp_coherence/__init__.py rhythmbox-0.11.4/plugins/coherence/upnp_coherence/__init__.py ---- rhythmbox-0.11.4.old/plugins/coherence/upnp_coherence/__init__.py 2007-12-17 09:26:53.000000000 +0000 -+++ rhythmbox-0.11.4/plugins/coherence/upnp_coherence/__init__.py 2008-02-21 14:50:19.000000000 +0000 -@@ -1,140 +1,164 @@ - import rhythmdb, rb - import gobject, gtk -+ - import louie -+ -+from coherence import log -+ - # For the icon - import os.path, urllib, gnomevfs, gtk.gdk - -+class CoherencePlugin(rb.Plugin,log.Loggable): -+ -+ logCategory = 'rb_coherence_plugin' - --class CoherencePlugin(rb.Plugin): -- def __init__(self): -- rb.Plugin.__init__(self) -- -- def activate(self, shell): -- from twisted.internet import gtk2reactor -- try: -- gtk2reactor.install() -- except AssertionError, e: -- # sometimes it's already installed -- print e -- -- self.coherence = self.get_coherence() -- if self.coherence is None: -- print "Coherence is not installed or too old, aborting" -- return -- -- print "coherence UPnP plugin activated" -- self.shell = shell -- self.sources = {} -- -- # watch for media servers -- louie.connect(self.detected_media_server, -- 'Coherence.UPnP.ControlPoint.MediaServer.detected', -- louie.Any) -- louie.connect(self.removed_media_server, -- 'Coherence.UPnP.ControlPoint.MediaServer.removed', -- louie.Any) -- -- # Set up our icon -- face_path = os.path.join(os.path.expanduser('~'), ".face") -- if os.path.exists(face_path): -- url = "file://" + urllib.pathname2url(face_path) -- -- if url: -- mimetype = gnomevfs.get_mime_type(url) -- pixbuf = gtk.gdk.pixbuf_new_from_file(face_path) -- width = "%s" % pixbuf.get_width() -- height = "%s" % pixbuf.get_height() -- depth = '24' -- the_icon = { -- 'url':url, -- 'mimetype':mimetype, -- 'width':width, -- 'height':height, -- 'depth':depth -- } -- -- # create our own media server -- from coherence.upnp.devices.media_server import MediaServer -- from MediaStore import MediaStore -- if the_icon: -- server = MediaServer(self.coherence, MediaStore, no_thread_needed=True, db=self.shell.props.db, plugin=self, icon=the_icon) -- else: -- server = MediaServer(self.coherence, MediaStore, no_thread_needed=True, db=self.shell.props.db, plugin=self) -- -- def deactivate(self, shell): -- print "coherence UPnP plugin deactivated" -- if self.coherence is None: -- return -- -- self.coherence.shutdown() -- -- louie.disconnect(self.detected_media_server, -- 'Coherence.UPnP.ControlPoint.MediaServer.detected', -- louie.Any) -- louie.disconnect(self.removed_media_server, -- 'Coherence.UPnP.ControlPoint.MediaServer.removed', -- louie.Any) -- -- del self.shell -- del self.coherence -- -- for usn, source in self.sources.iteritems(): -- source.delete_thyself() -- del self.sources -- -- # uninstall twisted reactor? probably not, since other thigngs may have used it -- -- -- def get_coherence (self): -- coherence_instance = None -- required_version = (0, 3, 2) -- -- try: -- from coherence.base import Coherence -- from coherence import __version_info__ -- except ImportError, e: -- print "Coherence not found" -- return None -- -- if __version_info__ < required_version: -- required = '.'.join([str(i) for i in required_version]) -- found = '.'.join([str(i) for i in __version_info__]) -- print "Coherence %s required. %s found. Please upgrade" % (required, found) -- return None -- -- coherence_config = { -- #'logmode': 'info', -- 'controlpoint': 'yes', -- 'plugins':{} -- } -- coherence_instance = Coherence(coherence_config) -- -- return coherence_instance -- -- -- def removed_media_server(self, usn): -- print "upnp server went away %s" % usn -- if self.sources.has_key(usn): -- self.sources[usn].delete_thyself() -- del self.sources[usn] -- -- def detected_media_server(self, client, usn): -- print "found upnp server %s (%s)" % (client.device.get_friendly_name(), usn) -- -- db = self.shell.props.db -- group = rb.rb_source_group_get_by_name ("shared") -- entry_type = db.entry_register_type("CoherenceUpnp:" + usn) -- -- from UpnpSource import UpnpSource -- source = gobject.new (UpnpSource, -- shell=self.shell, -- entry_type=entry_type, -- source_group=group, -- plugin=self, -- client=client, -- usn=usn) -+ def __init__(self): -+ rb.Plugin.__init__(self) - -- self.sources[usn] = source -+ def activate(self, shell): -+ from twisted.internet import gtk2reactor -+ try: -+ gtk2reactor.install() -+ except AssertionError, e: -+ # sometimes it's already installed -+ print e -+ -+ self.coherence = self.get_coherence() -+ if self.coherence is None: -+ print "Coherence is not installed or too old, aborting" -+ return -+ -+ print "coherence UPnP plugin activated" -+ self.shell = shell -+ self.sources = {} -+ -+ # Set up our icon -+ the_icon = None -+ face_path = os.path.join(os.path.expanduser('~'), ".face") -+ if os.path.exists(face_path): -+ url = "file://" + urllib.pathname2url(face_path) -+ mimetype = gnomevfs.get_mime_type(url) -+ pixbuf = gtk.gdk.pixbuf_new_from_file(face_path) -+ width = "%s" % pixbuf.get_width() -+ height = "%s" % pixbuf.get_height() -+ depth = '24' -+ the_icon = { -+ 'url':url, -+ 'mimetype':mimetype, -+ 'width':width, -+ 'height':height, -+ 'depth':depth -+ } -+ else: -+ the_icon = None -+ -+ # create our own media server -+ from coherence.upnp.devices.media_server import MediaServer -+ from MediaStore import MediaStore -+ if the_icon: -+ server = MediaServer(self.coherence, MediaStore, no_thread_needed=True, db=self.shell.props.db, plugin=self, icon=the_icon) -+ else: -+ server = MediaServer(self.coherence, MediaStore, no_thread_needed=True, db=self.shell.props.db, plugin=self) -+ -+ self.uuid = str(server.uuid) -+ -+ if self.coherence_version >= (0,5,2): -+ # create our own media renderer -+ # but only if we have a matching Coherence package installed -+ from coherence.upnp.devices.media_renderer import MediaRenderer -+ from MediaPlayer import RhythmboxPlayer -+ if the_icon: -+ MediaRenderer(self.coherence, RhythmboxPlayer, no_thread_needed=True, shell=self.shell, icon=the_icon) -+ else: -+ MediaRenderer(self.coherence, RhythmboxPlayer, no_thread_needed=True, shell=self.shell) -+ -+ # watch for media servers -+ louie.connect(self.detected_media_server, -+ 'Coherence.UPnP.ControlPoint.MediaServer.detected', -+ louie.Any) -+ louie.connect(self.removed_media_server, -+ 'Coherence.UPnP.ControlPoint.MediaServer.removed', -+ louie.Any) -+ -+ -+ def deactivate(self, shell): -+ print "coherence UPnP plugin deactivated" -+ if self.coherence is None: -+ return -+ -+ self.coherence.shutdown() -+ -+ louie.disconnect(self.detected_media_server, -+ 'Coherence.UPnP.ControlPoint.MediaServer.detected', -+ louie.Any) -+ louie.disconnect(self.removed_media_server, -+ 'Coherence.UPnP.ControlPoint.MediaServer.removed', -+ louie.Any) -+ -+ del self.shell -+ del self.coherence -+ -+ for usn, source in self.sources.iteritems(): -+ source.delete_thyself() -+ del self.sources -+ -+ # uninstall twisted reactor? probably not, since other thigngs may have used it -+ -+ -+ def get_coherence (self): -+ coherence_instance = None -+ required_version = (0, 3, 2) -+ -+ try: -+ from coherence.base import Coherence -+ from coherence import __version_info__ -+ except ImportError, e: -+ print "Coherence not found" -+ return None -+ -+ if __version_info__ < required_version: -+ required = '.'.join([str(i) for i in required_version]) -+ found = '.'.join([str(i) for i in __version_info__]) -+ print "Coherence %s required. %s found. Please upgrade" % (required, found) -+ return None -+ -+ self.coherence_version = __version_info__ -+ -+ coherence_config = { -+ #'logmode': 'info', -+ 'controlpoint': 'yes', -+ 'plugins': {}, -+ } -+ coherence_instance = Coherence(coherence_config) -+ -+ return coherence_instance -+ -+ def removed_media_server(self, usn): -+ print "upnp server went away %s" % usn -+ if self.sources.has_key(usn): -+ self.sources[usn].delete_thyself() -+ del self.sources[usn] -+ -+ def detected_media_server(self, client, usn): -+ print "found upnp server %s (%s)" % (client.device.get_friendly_name(), usn) -+ self.warning("found upnp server %s (%s)" % (client.device.get_friendly_name(), usn)) -+ if client.device.get_id() == self.uuid: -+ """ don't react on our own MediaServer""" -+ return -+ -+ db = self.shell.props.db -+ group = rb.rb_source_group_get_by_name ("shared") -+ entry_type = db.entry_register_type("CoherenceUpnp:" + client.device.get_id()[5:]) -+ -+ from UpnpSource import UpnpSource -+ source = gobject.new (UpnpSource, -+ shell=self.shell, -+ entry_type=entry_type, -+ source_group=group, -+ plugin=self, -+ client=client, -+ usn=usn) - -- self.shell.append_source (source, None) -+ self.sources[usn] = source - -+ self.shell.append_source (source, None) -diff -uprN rhythmbox-0.11.4.old/plugins/coherence/upnp_coherence/Makefile.am rhythmbox-0.11.4/plugins/coherence/upnp_coherence/Makefile.am ---- rhythmbox-0.11.4.old/plugins/coherence/upnp_coherence/Makefile.am 2007-07-13 08:58:49.000000000 +0100 -+++ rhythmbox-0.11.4/plugins/coherence/upnp_coherence/Makefile.am 2008-02-05 12:35:57.000000000 +0000 -@@ -4,4 +4,5 @@ plugindir = $(PLUGINDIR)/upnp_coherence - plugin_PYTHON = \ - UpnpSource.py \ - MediaStore.py \ -+ MediaPlayer.py \ - __init__.py -diff -uprN rhythmbox-0.11.4.old/plugins/coherence/upnp_coherence/MediaPlayer.py rhythmbox-0.11.4/plugins/coherence/upnp_coherence/MediaPlayer.py ---- rhythmbox-0.11.4.old/plugins/coherence/upnp_coherence/MediaPlayer.py 1970-01-01 01:00:00.000000000 +0100 -+++ rhythmbox-0.11.4/plugins/coherence/upnp_coherence/MediaPlayer.py 2008-02-05 12:35:57.000000000 +0000 -@@ -0,0 +1,437 @@ -+# Licensed under the MIT license -+# http://opensource.org/licenses/mit-license.php -+ -+# Copyright 2008, Frank Scholz -+ -+import urllib -+ -+import rhythmdb -+ -+from coherence.upnp.core.soap_service import errorCode -+from coherence.upnp.core import DIDLLite -+ -+import louie -+ -+from coherence.extern.simple_plugin import Plugin -+ -+from coherence import log -+ -+TRACK_COUNT = 1000000 -+ -+class RhythmboxPlayer(log.Loggable): -+ -+ """ a backend to the Rhythmbox -+ -+ """ -+ logCategory = 'rb_media_renderer' -+ -+ implements = ['MediaRenderer'] -+ vendor_value_defaults = {'RenderingControl': {'A_ARG_TYPE_Channel':'Master'}} -+ vendor_range_defaults = {'RenderingControl': {'Volume': {'maximum':100}}} -+ -+ def __init__(self, device, **kwargs): -+ self.warning("__init__ RhythmboxPlayer %r", kwargs) -+ self.shell = kwargs['shell'] -+ self.server = device -+ -+ self.player = None -+ self.metadata = None -+ self.host = '127.0.0.1' -+ self.name = "Rhythmbox on %s" % self.server.coherence.hostname -+ -+ self.player = self.shell.get_player() -+ self.player.connect ('playing-song-changed', -+ self.playing_song_changed), -+ self.player.connect ('playing-changed', -+ self.playing_changed) -+ self.player.connect ('elapsed-changed', -+ self.elapsed_changed) -+ self.player.connect("notify::volume", self.volume_changed) -+ louie.send('Coherence.UPnP.Backend.init_completed', None, backend=self) -+ -+ self.playing = False -+ self.state = None -+ self.duration = None -+ self.volume = 1.0 -+ self.muted_volume = None -+ self.view = [] -+ self.tags = {} -+ -+ def __repr__(self): -+ return str(self.__class__).split('.')[-1] -+ -+ def volume_changed(self, player, parameter): -+ self.volume = self.player.props.volume -+ self.warning('volume_changed to %r', self.volume) -+ if self.volume > 0: -+ rcs_id = self.server.connection_manager_server.lookup_rcs_id(self.current_connection_id) -+ self.server.rendering_control_server.set_variable(rcs_id, 'Volume', self.volume*100) -+ -+ def playing_song_changed(self, player, entry): -+ self.warning("playing_song_changed %r", entry) -+ if self.server != None: -+ connection_id = self.server.connection_manager_server.lookup_avt_id(self.current_connection_id) -+ if entry == None: -+ self.update('STOPPED') -+ self.playing = False -+ #self.entry = None -+ self.metadata = None -+ self.duration = None -+ else: -+ self.id = self.shell.props.db.entry_get (entry, rhythmdb.PROP_ENTRY_ID) -+ bitrate = self.shell.props.db.entry_get(entry, rhythmdb.PROP_BITRATE) * 1024 / 8 -+ # Duration is in HH:MM:SS format -+ seconds = self.shell.props.db.entry_get(entry, rhythmdb.PROP_DURATION) -+ hours = seconds / 3600 -+ seconds = seconds - hours * 3600 -+ minutes = seconds / 60 -+ seconds = seconds - minutes * 60 -+ self.duration = "%02d:%02d:%02d" % (hours, minutes, seconds) -+ -+ mimetype = self.shell.props.db.entry_get(entry, rhythmdb.PROP_MIMETYPE) -+ # This isn't a real mime-type -+ if mimetype == "application/x-id3": -+ mimetype = "audio/mpeg" -+ size = self.shell.props.db.entry_get(entry, rhythmdb.PROP_FILE_SIZE) -+ -+ # create item -+ item = DIDLLite.MusicTrack(self.id + TRACK_COUNT) -+ item.album = self.shell.props.db.entry_get(entry, rhythmdb.PROP_ALBUM) -+ item.artist = self.shell.props.db.entry_get(entry, rhythmdb.PROP_ARTIST) -+ item.genre = self.shell.props.db.entry_get(entry, rhythmdb.PROP_GENRE) -+ item.originalTrackNumber = str(self.shell.props.db.entry_get (entry, rhythmdb.PROP_TRACK_NUMBER)) -+ item.title = self.shell.props.db.entry_get(entry, rhythmdb.PROP_TITLE) # much nicer if it was entry.title -+ -+ item.res = [] -+ -+ uri = self.shell.props.db.entry_get(entry, rhythmdb.PROP_LOCATION) -+ if uri.startswith("file://"): -+ location = unicode(urllib.unquote(uri[len("file://"):])) -+ -+ # add a fake resource for the moment -+ res = DIDLLite.Resource(location, 'http-get:*:%s:*' % mimetype) -+ if size > 0: -+ res.size = size -+ if self.duration > 0: -+ res.duration = self.duration -+ if bitrate > 0: -+ res.bitrate = str(bitrate) -+ item.res.append(res) -+ -+ elt = DIDLLite.DIDLElement() -+ elt.addItem(item) -+ self.metadata = elt.toString() -+ self.entry = entry -+ if self.server != None: -+ self.server.av_transport_server.set_variable(connection_id, 'AVTransportURIMetaData',self.metadata) -+ self.server.av_transport_server.set_variable(connection_id, 'CurrentTrackMetaData',self.metadata) -+ self.warning("playing_song_changed %r", self.metadata) -+ if self.server != None: -+ self.server.av_transport_server.set_variable(connection_id, 'RelativeTimePosition', '00:00:00') -+ self.server.av_transport_server.set_variable(connection_id, 'AbsoluteTimePosition', '00:00:00') -+ -+ def playing_changed(self, player, state): -+ self.warning("playing_changed", state) -+ if state is True: -+ transport_state = 'PLAYING' -+ else: -+ if self.playing is False: -+ transport_state = 'STOPPED' -+ else: -+ transport_state = 'PAUSED_PLAYBACK' -+ self.update(transport_state) -+ try: -+ position = player.get_playing_time() -+ except: -+ position = None -+ try: -+ duration = player.get_playing_song_duration() -+ except: -+ duration = None -+ self.update_position(position,duration) -+ self.warning("playing_changed %r %r ", position, duration) -+ -+ def elapsed_changed(self, player, time): -+ self.warning("elapsed_changed %r %r", player, time) -+ try: -+ duration = player.get_playing_song_duration() -+ except: -+ duration = None -+ self.update_position(time,duration) -+ -+ def update(self, state): -+ -+ self.warning("update %r", state) -+ -+ if state in ('STOPPED','READY'): -+ transport_state = 'STOPPED' -+ if state == 'PLAYING': -+ transport_state = 'PLAYING' -+ if state == 'PAUSED_PLAYBACK': -+ transport_state = 'PAUSED_PLAYBACK' -+ -+ if self.state != transport_state: -+ self.state = transport_state -+ if self.server != None: -+ connection_id = self.server.connection_manager_server.lookup_avt_id(self.current_connection_id) -+ self.server.av_transport_server.set_variable(connection_id, -+ 'TransportState', -+ transport_state) -+ -+ -+ def update_position(self, position,duration): -+ self.warning("update_position %r %r", position,duration) -+ -+ if self.server != None: -+ connection_id = self.server.connection_manager_server.lookup_avt_id(self.current_connection_id) -+ self.server.av_transport_server.set_variable(connection_id, 'CurrentTrack', 0) -+ -+ if position is not None: -+ m,s = divmod( position, 60) -+ h,m = divmod(m,60) -+ if self.server != None: -+ self.server.av_transport_server.set_variable(connection_id, 'RelativeTimePosition', '%02d:%02d:%02d' % (h,m,s)) -+ self.server.av_transport_server.set_variable(connection_id, 'AbsoluteTimePosition', '%02d:%02d:%02d' % (h,m,s)) -+ -+ if duration <= 0: -+ duration = None -+ -+ if duration is not None: -+ m,s = divmod( duration, 60) -+ h,m = divmod(m,60) -+ -+ if self.server != None: -+ self.server.av_transport_server.set_variable(connection_id, 'CurrentTrackDuration', '%02d:%02d:%02d' % (h,m,s)) -+ self.server.av_transport_server.set_variable(connection_id, 'CurrentMediaDuration', '%02d:%02d:%02d' % (h,m,s)) -+ -+ if self.duration is None: -+ if self.metadata is not None: -+ self.warning("update_position %r", self.metadata) -+ elt = DIDLLite.DIDLElement.fromString(self.metadata) -+ for item in elt: -+ for res in item.findall('res'): -+ res.attrib['duration'] = "%d:%02d:%02d" % (h,m,s) -+ self.metadata = elt.toString() -+ -+ if self.server != None: -+ self.server.av_transport_server.set_variable(connection_id, 'AVTransportURIMetaData',self.metadata) -+ self.server.av_transport_server.set_variable(connection_id, 'CurrentTrackMetaData',self.metadata) -+ -+ self.duration = duration -+ -+ def load( self, uri, metadata): -+ self.warning("player load %r %r", uri, metadata) -+ #self.shell.load_uri(uri,play=False) -+ self.duration = None -+ self.metadata = metadata -+ self.tags = {} -+ -+ if len(self.metadata)>0: -+ elt = DIDLLite.DIDLElement.fromString(self.metadata) -+ if elt.numItems() == 1: -+ item = elt.getItems()[0] -+ -+ self.entry = self.shell.props.db.entry_lookup_by_location(uri) -+ self.warning("check for entry %r %r", self.entry, item.server_uuid) -+ if self.entry == None: -+ if item.server_uuid is not None: -+ entry_type = self.shell.props.db.entry_register_type("CoherenceUpnp:" + item.server_uuid) -+ self.entry = self.shell.props.db.entry_new(entry_type, uri) -+ self.warning("create new entry %r", self.entry) -+ else: -+ self.shell.load_uri(uri,play=False) -+ self.entry = self.shell.props.db.entry_lookup_by_location(uri) -+ self.warning("load and check for entry %r", self.entry) -+ -+ -+ duration = None -+ size = None -+ bitrate = None -+ for res in item.res: -+ if res.data == uri: -+ duration = res.duration -+ size = res.size -+ bitrate = res.bitrate -+ break -+ -+ self.shell.props.db.set(self.entry, rhythmdb.PROP_TITLE, item.title) -+ try: -+ if item.artist is not None: -+ self.shell.props.db.set(self.entry, rhythmdb.PROP_ARTIST, item.artist) -+ except AttributeError: -+ pass -+ try: -+ if item.album is not None: -+ self.shell.props.db.set(self.entry, rhythmdb.PROP_ALBUM, item.album) -+ except AttributeError: -+ pass -+ -+ try: -+ self.info("%r %r", item.title,item.originalTrackNumber) -+ if item.originalTrackNumber is not None: -+ self.shell.props.db.set(self.entry, rhythmdb.PROP_TRACK_NUMBER, int(item.originalTrackNumber)) -+ except AttributeError: -+ pass -+ -+ if duration is not None: -+ h,m,s = duration.split(':') -+ seconds = int(h)*3600 + int(m)*60 + int(s) -+ self.info("%r %r:%r:%r %r", duration, h, m , s, seconds) -+ self.shell.props.db.set(self.entry, rhythmdb.PROP_DURATION, seconds) -+ -+ if size is not None: -+ self.shell.props.db.set(self.entry, rhythmdb.PROP_FILE_SIZE,int(size)) -+ -+ else: -+ self.shell.load_uri(uri,play=False) -+ self.entry = self.shell.props.db.entry_lookup_by_location(uri) -+ -+ self.playing = False -+ -+ connection_id = self.server.connection_manager_server.lookup_avt_id(self.current_connection_id) -+ self.server.av_transport_server.set_variable(connection_id, 'CurrentTransportActions','Play,Stop,Pause') -+ self.server.av_transport_server.set_variable(connection_id, 'NumberOfTracks',1) -+ self.server.av_transport_server.set_variable(connection_id, 'CurrentTrackURI',uri) -+ self.server.av_transport_server.set_variable(connection_id, 'AVTransportURI',uri) -+ self.server.av_transport_server.set_variable(connection_id, 'AVTransportURIMetaData',metadata) -+ self.server.av_transport_server.set_variable(connection_id, 'CurrentTrackURI',uri) -+ self.server.av_transport_server.set_variable(connection_id, 'CurrentTrackMetaData',metadata) -+ -+ def start(self, uri): -+ self.load(uri) -+ self.play() -+ -+ def stop(self): -+ self.warning("player stop") -+ -+ self.player.stop() -+ self.playing = False -+ #self.server.av_transport_server.set_variable( \ -+ # self.server.connection_manager_server.lookup_avt_id(self.current_connection_id),\ -+ # 'TransportState', 'STOPPED') -+ -+ def play(self): -+ self.warning("player play") -+ -+ if self.playing == False: -+ self.player.play_entry(self.entry) -+ self.playing = True -+ else: -+ self.player.playpause() -+ #self.server.av_transport_server.set_variable( \ -+ # self.server.connection_manager_server.lookup_avt_id(self.current_connection_id),\ -+ # 'TransportState', 'PLAYING') -+ -+ def pause(self): -+ self.player.pause() -+ #self.server.av_transport_server.set_variable( \ -+ # self.server.connection_manager_server.lookup_avt_id(self.current_connection_id),\ -+ # 'TransportState', 'PAUSED_PLAYBACK') -+ -+ def seek(self, location): -+ """ -+ @param location: simple number = time to seek to, in seconds -+ +nL = relative seek forward n seconds -+ -nL = relative seek backwards n seconds -+ """ -+ -+ def mute(self): -+ self.muted_volume = self.volume -+ self.player.set_volume(0) -+ rcs_id = self.server.connection_manager_server.lookup_rcs_id(self.current_connection_id) -+ self.server.rendering_control_server.set_variable(rcs_id, 'Mute', 'True') -+ -+ def unmute(self): -+ if self.muted_volume is not None: -+ self.player.set_volume(self.muted_volume) -+ self.muted_volume = None -+ self.player.set_mute(False) -+ rcs_id = self.server.connection_manager_server.lookup_rcs_id(self.current_connection_id) -+ self.server.rendering_control_server.set_variable(rcs_id, 'Mute', 'False') -+ -+ def get_mute(self): -+ return self.player.get_mute() -+ -+ def get_volume(self): -+ self.volume = self.player.get_volume() -+ self.warning("get_volume %r", self.volume) -+ return self.volume * 100 -+ -+ def set_volume(self, volume): -+ self.warning("set_volume %r", volume) -+ volume = int(volume) -+ if volume < 0: -+ volume=0 -+ if volume > 100: -+ volume=100 -+ -+ self.player.set_volume(float(volume/100.0)) -+ -+ def upnp_init(self): -+ self.current_connection_id = None -+ self.server.connection_manager_server.set_variable(0, 'SinkProtocolInfo', -+ ['internal:%s:*:*' % self.host, -+ 'http-get:*:audio/mpeg:*'], -+ default=True) -+ self.server.av_transport_server.set_variable(0, 'TransportState', 'NO_MEDIA_PRESENT', default=True) -+ self.server.av_transport_server.set_variable(0, 'TransportStatus', 'OK', default=True) -+ self.server.av_transport_server.set_variable(0, 'CurrentPlayMode', 'NORMAL', default=True) -+ self.server.av_transport_server.set_variable(0, 'CurrentTransportActions', '', default=True) -+ self.server.rendering_control_server.set_variable(0, 'Volume', self.get_volume()) -+ self.server.rendering_control_server.set_variable(0, 'Mute', self.get_mute()) -+ -+ def upnp_Play(self, *args, **kwargs): -+ InstanceID = int(kwargs['InstanceID']) -+ Speed = int(kwargs['Speed']) -+ self.play() -+ return {} -+ -+ def upnp_Pause(self, *args, **kwargs): -+ InstanceID = int(kwargs['InstanceID']) -+ self.pause() -+ return {} -+ -+ def upnp_Stop(self, *args, **kwargs): -+ InstanceID = int(kwargs['InstanceID']) -+ self.stop() -+ return {} -+ -+ def upnp_SetAVTransportURI(self, *args, **kwargs): -+ InstanceID = int(kwargs['InstanceID']) -+ CurrentURI = kwargs['CurrentURI'] -+ CurrentURIMetaData = kwargs['CurrentURIMetaData'] -+ local_protocol_infos=self.server.connection_manager_server.get_variable('SinkProtocolInfo').value.split(',') -+ #print '>>>', local_protocol_infos -+ if len(CurrentURIMetaData)==0: -+ self.load(CurrentURI,CurrentURIMetaData) -+ else: -+ elt = DIDLLite.DIDLElement.fromString(CurrentURIMetaData) -+ #import pdb; pdb.set_trace() -+ if elt.numItems() == 1: -+ item = elt.getItems()[0] -+ res = item.res.get_matching(local_protocol_infos, protocol_type='internal') -+ if len(res) == 0: -+ res = item.res.get_matching(local_protocol_infos) -+ if len(res) > 0: -+ res = res[0] -+ remote_protocol,remote_network,remote_content_format,_ = res.protocolInfo.split(':') -+ self.load(res.data,CurrentURIMetaData) -+ return {} -+ return failure.Failure(errorCode(714)) -+ -+ def upnp_SetMute(self, *args, **kwargs): -+ InstanceID = int(kwargs['InstanceID']) -+ Channel = kwargs['Channel'] -+ DesiredMute = kwargs['DesiredMute'] -+ if DesiredMute in ['TRUE', 'True', 'true', '1','Yes','yes']: -+ self.mute() -+ else: -+ self.unmute() -+ return {} -+ -+ def upnp_SetVolume(self, *args, **kwargs): -+ InstanceID = int(kwargs['InstanceID']) -+ Channel = kwargs['Channel'] -+ DesiredVolume = int(kwargs['DesiredVolume']) -+ self.set_volume(DesiredVolume) -+ return {} -diff -uprN rhythmbox-0.11.4.old/plugins/coherence/upnp_coherence/MediaStore.py rhythmbox-0.11.4/plugins/coherence/upnp_coherence/MediaStore.py ---- rhythmbox-0.11.4.old/plugins/coherence/upnp_coherence/MediaStore.py 2007-10-02 14:48:59.000000000 +0100 -+++ rhythmbox-0.11.4/plugins/coherence/upnp_coherence/MediaStore.py 2008-02-05 12:35:57.000000000 +0000 -@@ -1,10 +1,12 @@ - # Copyright 2007, James Livingston -+# Copyright 2007, Frank Scholz - - import rhythmdb - import louie - import urllib - from coherence.upnp.core import DIDLLite - -+from coherence import log - - ROOT_CONTAINER_ID = 0 - AUDIO_CONTAINER = 10 -@@ -12,12 +14,16 @@ AUDIO_ALL_CONTAINER_ID = 11 - AUDIO_ARTIST_CONTAINER_ID = 12 - AUDIO_ALBUM_CONTAINER_ID = 13 - --CONTAINER_COUNT = 1000 -+CONTAINER_COUNT = 10000 - -+TRACK_COUNT = 1000000 -+ -+# most of this class is from Coherence, originally under the MIT licence -+ -+class Container(log.Loggable): -+ -+ logCategory = 'rb_media_store' - --# this class is from Coherence, originally under the MIT licence --# Copyright 2007, Frank Scholz --class Container(object): - def __init__(self, id, parent_id, name, children_callback=None): - self.id = id - self.parent_id = parent_id -@@ -25,11 +31,11 @@ class Container(object): - self.mimetype = 'directory' - self.item = DIDLLite.Container(id, parent_id,self.name) - self.update_id = 0 -+ self.item.childCount = 0 - if children_callback != None: - self.children = children_callback - else: - self.children = [] -- self.item.childCount = self.get_child_count() - - def add_child(self, child): - self.children.append(child) -@@ -40,18 +46,22 @@ class Container(object): - children = self.children() - else: - children = self.children -+ -+ self.info("Container get_children %r (%r,%r)", children, start, request_count) - if request_count == 0: - return children[start:] - else: - return children[start:request_count] - - def get_child_count(self): -+ - if callable(self.children): - return len(self.children()) - else: - return len(self.children) - - def get_item(self): -+ self.item.childCount = self.get_child_count() - return self.item - - def get_name(self): -@@ -61,160 +71,349 @@ class Container(object): - return self.id - - --class Track: -- def __init__(self, store, id): -- self.id = id -- self.store = store -- -- def get_children(self, start=0, request_count=0): -- return [] -- -- def get_child_count(self): -- return 0 -- -- def get_item(self): -- host = "" -- -- # load common values -- entry = self.store.db.entry_lookup_by_id (self.id) -- # Bitrate is in bytes/second, not kilobits/second -- bitrate = self.store.db.entry_get (entry, rhythmdb.PROP_BITRATE) * 1024 / 8 -- # Duration is in HH:MM:SS format -- seconds = self.store.db.entry_get (entry, rhythmdb.PROP_DURATION) -- hours = seconds / 3600 -- seconds = seconds - hours * 3600 -- minutes = seconds / 60 -- seconds = seconds - minutes * 60 -- duration = ("%02d:%02d:%02d") % (hours, minutes, seconds) -- -- location = self.store.db.entry_get (entry, rhythmdb.PROP_LOCATION) -- if location.startswith("file://"): -- location = unicode(urllib.url2pathname(location)[len("file://"):]) -- else: -- location = None -- mimetype = self.store.db.entry_get (entry, rhythmdb.PROP_MIMETYPE) -- # This isn't a real mime-type -- if mimetype == "application/x-id3": -- mimetype = "audio/mpeg" -- size = self.store.db.entry_get (entry, rhythmdb.PROP_FILE_SIZE) -- -- # create item -- item = DIDLLite.MusicTrack(self.id + CONTAINER_COUNT) -- item.album = self.store.db.entry_get (entry, rhythmdb.PROP_ALBUM) -- #item.albumArtURI = ## can we somehow store art in the upnp share?? -- item.artist = self.store.db.entry_get (entry, rhythmdb.PROP_ARTIST) -- #item.date = -- item.genre = self.store.db.entry_get (entry, rhythmdb.PROP_GENRE) -- item.originalTrackNumber = str(self.store.db.entry_get (entry, rhythmdb.PROP_TRACK_NUMBER)) -- item.title = self.store.db.entry_get (entry, rhythmdb.PROP_TITLE) # much nicer if it was entry.title -- item.res = [] -- -- # add internal resource -- #res = DIDLLite.Resource(location, 'internal:%s:%s:*' % (host, mimetype)) -- #res.size = size -- #res.duration = duration -- #res.bitrate = bitrate -- #item.res.append(res) -- -- # add http resource -- res = DIDLLite.Resource(self.get_url(), 'http-get:*:%s:*' % mimetype) -- if size > 0: -- res.size = size -- if duration > 0: -- res.duration = str(duration) -- if bitrate > 0: -- res.bitrate = str(bitrate) -- item.res.append(res) -- -- return item -- -- def get_id(self): -- return self.id -- -- def get_name(self): -- entry = self.store.db.entry_lookup_by_id (self.id) -- return self.store.db.entry_get (entry, rhythmdb.PROP_TITLE) -- -- def get_url(self): -- return self.store.urlbase + str(self.id + CONTAINER_COUNT) -- -- def get_path(self): -- entry = self.store.db.entry_lookup_by_id (self.id) -- uri = self.store.db.entry_get (entry, rhythmdb.PROP_LOCATION) -- if uri.startswith("file://"): -- return unicode(urllib.url2pathname(uri)[len("file://"):]) -- else: -- return None -- --class MediaStore: -- implements = ['MediaServer'] -- -- def __init__(self, server, **kwargs): -- print "creating UPnP MediaStore" -- self.server = server -- self.db = kwargs['db'] -- self.plugin = kwargs['plugin'] -- -- self.urlbase = kwargs.get('urlbase','') -- if( len(self.urlbase) > 0 and self.urlbase[len(self.urlbase)-1] != '/'): -- self.urlbase += '/' -- -- self.name = self.server.coherence.hostname -- -- self.containers = {} -- self.containers[ROOT_CONTAINER_ID] = \ -- Container( ROOT_CONTAINER_ID,-1, self.server.coherence.hostname) -- -- self.containers[AUDIO_ALL_CONTAINER_ID] = \ -- Container( AUDIO_ALL_CONTAINER_ID,ROOT_CONTAINER_ID, 'All tracks', -- children_callback=self.children_tracks) -- self.containers[ROOT_CONTAINER_ID].add_child(self.containers[AUDIO_ALL_CONTAINER_ID]) -- -- #self.containers[AUDIO_ALBUM_CONTAINER_ID] = \ -- # Container( AUDIO_ALBUM_CONTAINER_ID,ROOT_CONTAINER_ID, 'Albums', -- # children_callback=self.children_albums) -- #self.containers[ROOT_CONTAINER_ID].add_child(self.containers[AUDIO_ALBUM_CONTAINER_ID]) -- -- #self.containers[AUDIO_ARTIST_CONTAINER_ID] = \ -- # Container( AUDIO_ARTIST_CONTAINER_ID,ROOT_CONTAINER_ID, 'Artists', -- # children_callback=self.children_artists) -- #self.containers[ROOT_CONTAINER_ID].add_child(self.containers[AUDIO_ARTIST_CONTAINER_ID]) -- -- louie.send('Coherence.UPnP.Backend.init_completed', None, backend=self) -- -- def get_by_id(self,id): -- print "getting resource id " + str(id) -- if id.startswith('artist_all_tracks_'): -- return self.containers[id] -- -- id = int(id) -- if id < 1000: -- item = self.containers[id] -- else: -- item = Track(self, (id - CONTAINER_COUNT)) -- -- return item -- -- def upnp_init(self): -- if self.server: -- self.server.connection_manager_server.set_variable(0, 'SourceProtocolInfo', [ -- #'internal:%s:*:*' % self.name, -- 'http-get:*:audio/mpeg:*', -- ]) -- -- def children_tracks(self): -- tracks = [] -- -- def track_cb (entry): -- id = self.db.entry_get (entry, rhythmdb.PROP_ENTRY_ID) -- tracks.append(Track(self, id)) -- self.db.entry_foreach_by_type (self.db.entry_type_get_by_name('song'), track_cb) -+class Album(log.Loggable): -+ -+ logCategory = 'rb_media_store' -+ -+ def __init__(self, store, title, id): -+ self.id = id -+ self.title = title -+ self.store = store -+ -+ query = self.store.db.query_new() -+ self.store.db.query_append(query,[rhythmdb.QUERY_PROP_EQUALS, rhythmdb.PROP_TYPE, self.store.db.entry_type_get_by_name('song')], -+ [rhythmdb.QUERY_PROP_EQUALS, rhythmdb.PROP_ALBUM, self.title]) -+ self.tracks_per_album_query = self.store.db.query_model_new(query) -+ #self.tracks_per_album_query.set_sort_order(rhythmdb.rhythmdb_query_model_track_sort_func) -+ self.store.db.do_full_query_async_parsed(self.tracks_per_album_query, query) -+ -+ def get_children(self,start=0,request_count=0): -+ children = [] -+ -+ def track_sort(x,y): -+ entry = self.store.db.entry_lookup_by_id (x.id) -+ x_track = self.store.db.entry_get (entry, rhythmdb.PROP_TRACK_NUMBER) -+ entry = self.store.db.entry_lookup_by_id (y.id) -+ y_track = self.store.db.entry_get (entry, rhythmdb.PROP_TRACK_NUMBER) -+ return cmp(x_track,y_track) -+ -+ def collate (model, path, iter): -+ self.info("Album get_children %r %r %r" %(model, path, iter)) -+ id = model.get(iter, 0)[0] -+ children.append(Track(self.store,id)) -+ -+ self.tracks_per_album_query.foreach(collate) -+ -+ children.sort(cmp=track_sort) -+ -+ if request_count == 0: -+ return children[start:] -+ else: -+ return children[start:request_count] -+ -+ def get_child_count(self): -+ return len(self.get_children()) -+ -+ def get_item(self): -+ item = DIDLLite.MusicAlbum(self.id, AUDIO_ALBUM_CONTAINER_ID, self.title) -+ return item -+ -+ def get_id(self): -+ return self.id -+ -+ def get_name(self): -+ return self.title -+ -+ def get_cover(self): -+ return self.cover -+ -+ -+class Artist(log.Loggable): -+ -+ logCategory = 'rb_media_store' -+ -+ def __init__(self, store, name, id): -+ self.id = id -+ self.name = name -+ self.store = store -+ -+ query = self.store.db.query_new() -+ self.store.db.query_append(query,[rhythmdb.QUERY_PROP_EQUALS, rhythmdb.PROP_TYPE, self.store.db.entry_type_get_by_name('song')], -+ [rhythmdb.QUERY_PROP_EQUALS, rhythmdb.PROP_ARTIST, self.name]) -+ qm = self.store.db.query_model_new(query) -+ self.store.db.do_full_query_async_parsed(qm, query) -+ -+ self.albums_per_artist_query = self.store.db.property_model_new(rhythmdb.PROP_ALBUM) -+ self.albums_per_artist_query.props.query_model = qm -+ -+ def get_children(self,start=0,request_count=0): -+ children = [] -+ -+ def collate (model, path, iter): -+ name = model.get(iter, 0)[0] -+ priority = model.get(iter, 1)[0] -+ self.info("get_children collate %r %r", name, priority) -+ if priority is False: -+ try: -+ album = self.store.albums[name] -+ children.append(album) -+ except: -+ self.warning("hmm, a new album %r, that shouldn't happen", name) -+ -+ self.albums_per_artist_query.foreach(collate) -+ -+ if request_count == 0: -+ return children[start:] -+ else: -+ return children[start:request_count] -+ -+ def get_child_count(self): -+ return len(self.get_children()) -+ -+ def get_item(self): -+ item = DIDLLite.MusicArtist(self.id, AUDIO_ARTIST_CONTAINER_ID, self.name) -+ return item -+ -+ def get_id(self): -+ return self.id -+ -+ def get_name(self): -+ return self.name -+ -+ -+class Track(log.Loggable): -+ -+ logCategory = 'rb_media_store' -+ -+ def __init__(self, store, id): -+ self.store = store -+ if type(id) == int: -+ self.id = id -+ else: -+ self.id = self.store.db.entry_get (id, rhythmdb.PROP_ENTRY_ID) -+ -+ def get_children(self, start=0, request_count=0): -+ return [] -+ -+ def get_child_count(self): -+ return 0 -+ -+ def get_item(self): -+ -+ self.info("Track get_item %r" %(self.id)) -+ -+ host = "" -+ -+ # load common values -+ entry = self.store.db.entry_lookup_by_id(self.id) -+ # Bitrate is in bytes/second, not kilobits/second -+ bitrate = self.store.db.entry_get(entry, rhythmdb.PROP_BITRATE) * 1024 / 8 -+ # Duration is in HH:MM:SS format -+ seconds = self.store.db.entry_get(entry, rhythmdb.PROP_DURATION) -+ hours = seconds / 3600 -+ seconds = seconds - hours * 3600 -+ minutes = seconds / 60 -+ seconds = seconds - minutes * 60 -+ duration = ("%02d:%02d:%02d") % (hours, minutes, seconds) -+ -+ location = self.get_path(entry) -+ mimetype = self.store.db.entry_get(entry, rhythmdb.PROP_MIMETYPE) -+ # This isn't a real mime-type -+ if mimetype == "application/x-id3": -+ mimetype = "audio/mpeg" -+ size = self.store.db.entry_get(entry, rhythmdb.PROP_FILE_SIZE) -+ -+ # create item -+ item = DIDLLite.MusicTrack(self.id + TRACK_COUNT) -+ item.album = self.store.db.entry_get(entry, rhythmdb.PROP_ALBUM) -+ item.artist = self.store.db.entry_get(entry, rhythmdb.PROP_ARTIST) -+ #item.date = -+ item.genre = self.store.db.entry_get(entry, rhythmdb.PROP_GENRE) -+ item.originalTrackNumber = str(self.store.db.entry_get (entry, rhythmdb.PROP_TRACK_NUMBER)) -+ item.title = self.store.db.entry_get(entry, rhythmdb.PROP_TITLE) # much nicer if it was entry.title -+ -+ #cover = self.store.db.entry_request_extra_metadata(entry, "rb:coverArt") -+ #self.warning("cover for %r is %r", item.title, cover) -+ #item.albumArtURI = ## can we somehow store art in the upnp share?? -+ -+ # add internal resource -+ #res = DIDLLite.Resource(location, 'internal:%s:%s:*' % (host, mimetype)) -+ #res.size = size -+ #res.duration = duration -+ #res.bitrate = bitrate -+ #item.res.append(res) -+ -+ # add http resource -+ res = DIDLLite.Resource(self.get_url(), 'http-get:*:%s:*' % mimetype) -+ if size > 0: -+ res.size = size -+ if duration > 0: -+ res.duration = str(duration) -+ if bitrate > 0: -+ res.bitrate = str(bitrate) -+ item.res.append(res) -+ -+ return item -+ -+ def get_id(self): -+ return self.id -+ -+ def get_name(self): -+ entry = self.store.db.entry_lookup_by_id (self.id) -+ return self.store.db.entry_get(entry, rhythmdb.PROP_TITLE) -+ -+ def get_url(self): -+ return self.store.urlbase + str(self.id + TRACK_COUNT) -+ -+ def get_path(self, entry = None): -+ if entry is None: -+ entry = self.store.db.entry_lookup_by_id (self.id) -+ uri = self.store.db.entry_get(entry, rhythmdb.PROP_LOCATION) -+ self.warning("Track get_path uri = %r", uri) -+ location = None -+ if uri.startswith("file://"): -+ location = unicode(urllib.unquote(uri[len("file://"):])) -+ self.warning("Track get_path location = %r", location) -+ -+ return location -+ -+class MediaStore(log.Loggable): -+ -+ logCategory = 'rb_media_store' -+ implements = ['MediaServer'] -+ -+ def __init__(self, server, **kwargs): -+ print "creating UPnP MediaStore" -+ self.server = server -+ self.db = kwargs['db'] -+ self.plugin = kwargs['plugin'] - -- return tracks -+ self.update_id = 0 -+ -+ self.next_id = CONTAINER_COUNT -+ self.albums = None -+ self.artists = None -+ self.tracks = None -+ -+ self.urlbase = kwargs.get('urlbase','') -+ if( len(self.urlbase) > 0 and self.urlbase[len(self.urlbase)-1] != '/'): -+ self.urlbase += '/' -+ -+ self.name = "Rhythmbox on %s" % self.server.coherence.hostname -+ -+ query = self.db.query_new() -+ self.info(query) -+ self.db.query_append(query, [rhythmdb.QUERY_PROP_EQUALS, rhythmdb.PROP_TYPE, self.db.entry_type_get_by_name('song')]) -+ qm = self.db.query_model_new(query) -+ self.db.do_full_query_async_parsed(qm, query) -+ -+ self.album_query = self.db.property_model_new(rhythmdb.PROP_ALBUM) -+ self.album_query.props.query_model = qm -+ -+ self.artist_query = self.db.property_model_new(rhythmdb.PROP_ARTIST) -+ self.artist_query.props.query_model = qm -+ -+ self.containers = {} -+ self.containers[ROOT_CONTAINER_ID] = \ -+ Container( ROOT_CONTAINER_ID,-1, "Rhythmbox on %s" % self.server.coherence.hostname) -+ -+ self.containers[AUDIO_ALL_CONTAINER_ID] = \ -+ Container( AUDIO_ALL_CONTAINER_ID,ROOT_CONTAINER_ID, 'All tracks', -+ children_callback=self.children_tracks) -+ self.containers[ROOT_CONTAINER_ID].add_child(self.containers[AUDIO_ALL_CONTAINER_ID]) -+ -+ self.containers[AUDIO_ALBUM_CONTAINER_ID] = \ -+ Container( AUDIO_ALBUM_CONTAINER_ID,ROOT_CONTAINER_ID, 'Albums', -+ children_callback=self.children_albums) -+ self.containers[ROOT_CONTAINER_ID].add_child(self.containers[AUDIO_ALBUM_CONTAINER_ID]) -+ -+ self.containers[AUDIO_ARTIST_CONTAINER_ID] = \ -+ Container( AUDIO_ARTIST_CONTAINER_ID,ROOT_CONTAINER_ID, 'Artists', -+ children_callback=self.children_artists) -+ self.containers[ROOT_CONTAINER_ID].add_child(self.containers[AUDIO_ARTIST_CONTAINER_ID]) -+ -+ louie.send('Coherence.UPnP.Backend.init_completed', None, backend=self) -+ -+ def get_by_id(self,id): -+ -+ self.info("looking for id %r", id) -+ id = int(id) -+ if id < TRACK_COUNT: -+ item = self.containers[id] -+ else: -+ item = Track(self, (id - TRACK_COUNT)) - -- def children_albums(self): -- return [] -+ return item - -- def children_artists(self): -- return [] -+ def get_next_container_id(self): -+ ret = self.next_id -+ self.next_id += 1 -+ return ret -+ -+ def upnp_init(self): -+ if self.server: -+ self.server.connection_manager_server.set_variable(0, 'SourceProtocolInfo', [ -+ #'internal:%s:*:*' % self.name, -+ 'http-get:*:audio/mpeg:*', -+ ]) -+ -+ def children_tracks(self): -+ tracks = [] -+ -+ def track_cb (entry): -+ if self.db.entry_get (entry, rhythmdb.PROP_HIDDEN): -+ return -+ id = self.db.entry_get (entry, rhythmdb.PROP_ENTRY_ID) -+ track = Track(self, id) -+ tracks.append(track) -+ -+ self.db.entry_foreach_by_type (self.db.entry_type_get_by_name('song'), track_cb) -+ return tracks -+ -+ def children_albums(self): -+ albums = {} -+ -+ self.info('children_albums') -+ -+ def album_sort(x,y): -+ r = cmp(x.title,y.title) -+ self.info("sort %r - %r = %r", x.title, y.title, r) -+ return r -+ -+ def collate (model, path, iter): -+ name = model.get(iter, 0)[0] -+ priority = model.get(iter, 1)[0] -+ self.info("children_albums collate %r %r", name, priority) -+ if priority is False: -+ id = self.get_next_container_id() -+ album = Album(self, name, id) -+ self.containers[id] = album -+ albums[name] = album -+ -+ if self.albums is None: -+ self.album_query.foreach(collate) -+ self.albums = albums -+ -+ albums = self.albums.values() #.sort(cmp=album_sort) -+ albums.sort(cmp=album_sort) -+ return albums -+ -+ def children_artists(self,killbug=False): -+ artists = [] -+ -+ self.info('children_artists') -+ -+ def collate (model, path, iter): -+ name = model.get(iter, 0)[0] -+ priority = model.get(iter, 1)[0] -+ if priority is False: -+ id = self.get_next_container_id() -+ artist = Artist(self,name, id) -+ self.containers[id] = artist -+ artists.append(artist) -+ -+ if self.artists is None: -+ self.artist_query.foreach(collate) -+ self.artists = artists - -+ return self.artists -diff -uprN rhythmbox-0.11.4.old/plugins/coherence/upnp_coherence/UpnpSource.py rhythmbox-0.11.4/plugins/coherence/upnp_coherence/UpnpSource.py ---- rhythmbox-0.11.4.old/plugins/coherence/upnp_coherence/UpnpSource.py 2007-07-24 12:54:04.000000000 +0100 -+++ rhythmbox-0.11.4/plugins/coherence/upnp_coherence/UpnpSource.py 2008-02-05 12:35:57.000000000 +0000 -@@ -1,101 +1,177 @@ -+# Copyright 2007, James Livingston -+# Copyright 2007, Frank Scholz -+ - import rb, rhythmdb - import gobject, gtk - --class UpnpSource(rb.BrowserSource): -- __gproperties__ = { -- 'plugin': (rb.Plugin, 'plugin', 'plugin', gobject.PARAM_WRITABLE|gobject.PARAM_CONSTRUCT_ONLY), -- 'client': (gobject.TYPE_PYOBJECT, 'client', 'client', gobject.PARAM_WRITABLE|gobject.PARAM_CONSTRUCT_ONLY), -- 'usn': (gobject.TYPE_PYOBJECT, 'usn', 'usn', gobject.PARAM_WRITABLE|gobject.PARAM_CONSTRUCT_ONLY), -- } -- -- def __init__(self): -- rb.BrowserSource.__init__(self) -- self.__db = None -- self.__activated = False -- -- -- def do_set_property(self, property, value): -- if property.name == 'plugin': -- self.__plugin = value -- elif property.name == 'client': -- self.__client = value -- self.props.name = self.__client.device.get_friendly_name() -- elif property.name == 'usn': -- self.__usn = value -- else: -- raise AttributeError, 'unknown property %s' % property.name -- -- -- def do_impl_activate(self): -- if not self.__activated: -- print "activating upnp source" -- self.__activated = True -- -- shell = self.get_property('shell') -- self.__db = shell.get_property('db') -- self.__entry_type = self.get_property('entry-type') -- -- # load upnp db -- self.load_db(0) -- self.__client.content_directory.subscribe_for_variable('ContainerUpdateIDs', self.state_variable_change) -- self.__client.content_directory.subscribe_for_variable('SystemUpdateID', self.state_variable_change) -- -- -- def load_db(self, id): -- d = self.__client.content_directory.browse(id, browse_flag='BrowseDirectChildren', backward_compatibility=False) -- d.addCallback(self.process_media_server_browse, self.__usn) -- -- -- def state_variable_change(self, variable, usn): -- print "%s changed from %s to %s" % (variable.name, variable.old_value, variable.value) -- if variable.old_value == '': -- return -- -- if variable.name == 'SystemUpdateID': -- self.load_db(0) -- elif variable.name == 'ContainerUpdateIDs': -- changes = variable.value.split(',') -- while len(changes) > 1: -- container = changes.pop(0).strip() -- update_id = changes.pop(0).strip() -- if container in self.container_watch: -- print "we have a change in %s, container needs a reload" % container -- self.load_db(container) -- -- -- def process_media_server_browse(self, results, usn): -- for k,v in results.iteritems(): -- if k == 'items': -- for id, values in v.iteritems(): -- if values['upnp_class'].startswith('object.container'): -- self.load_db(id) -- if values['upnp_class'].startswith('object.item.audioItem'): -- # (url, [method, something which is in asterix, format, semicolon delimited key=value map of something]) -- resources = [(k, v.split(':')) for (k, v) in values['resources'].iteritems()] -- # break data into map -- for r in resources: -- if r[1][3] is not '*': -- r[1][3] = dict([v.split('=') for v in r[1][3].split(';')]) -- else: -- r[1][3] = dict() -- -- url = None -- for r in resources: -- if r[1][3].has_key('DLNA.ORG_CI') and r[1][3]['DLNA.ORG_CI'] is not '1': -- url = r[0] -- break -- -- if url is None: -- # use transcoded format, since we can't find a normal one -- url = resources[0][0] -- -- entry = self.__db.entry_lookup_by_location (url) -- if entry == None: -- entry = self.__db.entry_new(self.__entry_type, url) -- -- self.__db.set(entry, rhythmdb.PROP_TITLE, values['title']) -- -- self.__db.commit() -+from coherence import __version_info__ as coherence_version - --gobject.type_register(UpnpSource) -+from coherence import log -+ -+class UpnpSource(rb.BrowserSource,log.Loggable): -+ -+ logCategory = 'rb_media_store' - -+ __gproperties__ = { -+ 'plugin': (rb.Plugin, 'plugin', 'plugin', gobject.PARAM_WRITABLE|gobject.PARAM_CONSTRUCT_ONLY), -+ 'client': (gobject.TYPE_PYOBJECT, 'client', 'client', gobject.PARAM_WRITABLE|gobject.PARAM_CONSTRUCT_ONLY), -+ 'usn': (gobject.TYPE_PYOBJECT, 'usn', 'usn', gobject.PARAM_WRITABLE|gobject.PARAM_CONSTRUCT_ONLY), -+ } -+ -+ def __init__(self): -+ rb.BrowserSource.__init__(self) -+ self.__db = None -+ self.__activated = False -+ self.container_watch = [] -+ if coherence_version < (0,5,1): -+ self.process_media_server_browse = self.old_process_media_server_browse -+ else: -+ self.process_media_server_browse = self.new_process_media_server_browse -+ -+ def do_set_property(self, property, value): -+ if property.name == 'plugin': -+ self.__plugin = value -+ elif property.name == 'client': -+ self.__client = value -+ self.props.name = self.__client.device.get_friendly_name() -+ elif property.name == 'usn': -+ self.__usn = value -+ else: -+ raise AttributeError, 'unknown property %s' % property.name -+ -+ -+ def do_impl_activate(self): -+ if not self.__activated: -+ print "activating upnp source" -+ self.__activated = True -+ -+ shell = self.get_property('shell') -+ self.__db = shell.get_property('db') -+ self.__entry_type = self.get_property('entry-type') -+ -+ # load upnp db -+ self.load_db(0) -+ self.__client.content_directory.subscribe_for_variable('ContainerUpdateIDs', self.state_variable_change) -+ self.__client.content_directory.subscribe_for_variable('SystemUpdateID', self.state_variable_change) -+ -+ -+ def load_db(self, id): -+ if coherence_version < (0,5,1): -+ d = self.__client.content_directory.browse(id, browse_flag='BrowseDirectChildren', backward_compatibility=False) -+ else: -+ d = self.__client.content_directory.browse(id, browse_flag='BrowseDirectChildren', process_result=False, backward_compatibility=False) -+ d.addCallback(self.process_media_server_browse, self.__usn) -+ -+ -+ def state_variable_change(self, variable, usn=None): -+ print "%s changed from %s to %s" % (variable.name, variable.old_value, variable.value) -+ if variable.old_value == '': -+ return -+ -+ if variable.name == 'SystemUpdateID': -+ self.load_db(0) -+ elif variable.name == 'ContainerUpdateIDs': -+ changes = variable.value.split(',') -+ while len(changes) > 1: -+ container = changes.pop(0).strip() -+ update_id = changes.pop(0).strip() -+ if container in self.container_watch: -+ print "we have a change in %s, container needs a reload" % container -+ self.load_db(container) -+ -+ -+ def new_process_media_server_browse(self, results, usn): -+ for item in results: -+ self.info("process_media_server_browse %r %r", item.id, item) -+ if item.upnp_class.startswith('object.container'): -+ self.load_db(item.id) -+ if item.upnp_class.startswith('object.item.audioItem'): -+ -+ url = None -+ duration = None -+ size = None -+ bitrate = None -+ -+ for res in item.res: -+ remote_protocol,remote_network,remote_content_format,remote_flags = res.protocolInfo.split(':') -+ self.info("%r %r %r %r",remote_protocol,remote_network,remote_content_format,remote_flags) -+ if remote_protocol == 'http-get': -+ url = res.data -+ duration = res.duration -+ size = res.size -+ bitrate = res.bitrate -+ break -+ -+ if url is not None: -+ self.info("url %r %r",url,item.title) -+ -+ entry = self.__db.entry_lookup_by_location (url) -+ if entry == None: -+ entry = self.__db.entry_new(self.__entry_type, url) -+ -+ self.__db.set(entry, rhythmdb.PROP_TITLE, item.title) -+ try: -+ if item.artist is not None: -+ self.__db.set(entry, rhythmdb.PROP_ARTIST, item.artist) -+ except AttributeError: -+ pass -+ try: -+ if item.album is not None: -+ self.__db.set(entry, rhythmdb.PROP_ALBUM, item.album) -+ except AttributeError: -+ pass -+ -+ try: -+ self.info("%r %r", item.title,item.originalTrackNumber) -+ if item.originalTrackNumber is not None: -+ self.__db.set(entry, rhythmdb.PROP_TRACK_NUMBER, int(item.originalTrackNumber)) -+ except AttributeError: -+ pass -+ -+ if duration is not None: -+ h,m,s = duration.split(':') -+ seconds = int(h)*3600 + int(m)*60 + int(s) -+ self.info("%r %r:%r:%r %r", duration, h, m , s, seconds) -+ self.__db.set(entry, rhythmdb.PROP_DURATION, seconds) -+ -+ if size is not None: -+ self.__db.set(entry, rhythmdb.PROP_FILE_SIZE,int(size)) -+ -+ self.__db.commit() -+ -+ -+ def old_process_media_server_browse(self, results, usn): -+ for k,v in results.iteritems(): -+ if k == 'items': -+ for id, values in v.iteritems(): -+ if values['upnp_class'].startswith('object.container'): -+ self.load_db(id) -+ if values['upnp_class'].startswith('object.item.audioItem'): -+ # (url, [method, something which is in asterix, format, semicolon delimited key=value map of something]) -+ resources = [(k, v.split(':')) for (k, v) in values['resources'].iteritems()] -+ # break data into map -+ for r in resources: -+ if r[1][3] is not '*': -+ r[1][3] = dict([v.split('=') for v in r[1][3].split(';')]) -+ else: -+ r[1][3] = dict() -+ -+ url = None -+ for r in resources: -+ if r[1][3].has_key('DLNA.ORG_CI') and r[1][3]['DLNA.ORG_CI'] is not '1': -+ url = r[0] -+ break -+ -+ if url is None: -+ # use transcoded format, since we can't find a normal one -+ url = resources[0][0] -+ -+ entry = self.__db.entry_lookup_by_location (url) -+ if entry == None: -+ entry = self.__db.entry_new(self.__entry_type, url) -+ -+ self.__db.set(entry, rhythmdb.PROP_TITLE, values['title']) -+ -+ self.__db.commit() -+ -+gobject.type_register(UpnpSource) diff --git a/rhythmbox.spec b/rhythmbox.spec index 8da495a..04f9376 100644 --- a/rhythmbox.spec +++ b/rhythmbox.spec @@ -2,8 +2,8 @@ Name: rhythmbox Summary: Music Management Application -Version: 0.11.4 -Release: 13%{?dist} +Version: 0.11.5 +Release: 1%{?dist} License: GPLv2+ and GFDL+ Group: Applications/Multimedia URL: http://www.gnome.org/projects/rhythmbox/ @@ -55,27 +55,6 @@ ExcludeArch: s390 s390x Patch0: rb-disable-power-plugin-by-default.patch # http://bugzilla.gnome.org/show_bug.cgi?id=499208 Patch1: rhythmbox-0.11.3-force-python-thread-init.patch -# http://bugzilla.gnome.org/show_bug.cgi?id=510323 -Patch2: x-content.patch -# http://bugzilla.gnome.org/show_bug.cgi?id=509701 -Patch3: soup24.patch -# http://bugzilla.gnome.org/show_bug.cgi?id=510406 -Patch4: gsd-media-player-api.patch - -Patch5: rb-automake-warning.patch - -# http://bugzilla.gnome.org/show_bug.cgi?id=519737 -Patch6: rb-activate-generic-players-from-uri.patch - -# http://bugzilla.gnome.org/show_bug.cgi?id=493996 -Patch7: rb-ipod-save-artwork.patch - -# https://bugzilla.redhat.com/show_bug.cgi?id=437066 -Patch8: rhythmbox-0.11.4-bigger-pane.patch - -# http://bugzilla.gnome.org/show_bug.cgi?id=512870 -# https://bugzilla.redhat.com/show_bug.cgi?id=436112 -Patch9: rhythmbox-0.11.4-updated-upnp.patch %description Rhythmbox is an integrated music management application based on the powerful @@ -106,14 +85,6 @@ UPnP/DLNA network devices. %patch0 -p0 -b .dont-disable-suspend %patch1 -p1 -b .python-threading -%patch2 -p1 -b .x-content -%patch3 -p1 -b .soup24 -%patch4 -p0 -b .gsd -%patch5 -p0 -b .automake -%patch6 -p0 -b .player-activate -%patch7 -p1 -b .ipod-artwork -%patch8 -p0 -b .bigger-pane -%patch9 -p1 -b .updated-upnp %build autoconf @@ -231,6 +202,10 @@ fi %{_libdir}/rhythmbox/plugins/upnp_coherence %changelog +* Mon Mar 17 2008 - Bastien Nocera - 0.11.5-1 +- Update to 0.11.5 +- Remove outdated patches + * Thu Mar 13 2008 - Bastien Nocera - 0.11.4-13 - Big update of the UPNP plugin, with MediaRenderer support - Add patch to make the pane window bigger by default (#437066) diff --git a/soup24.patch b/soup24.patch deleted file mode 100644 index d257f63..0000000 --- a/soup24.patch +++ /dev/null @@ -1,1743 +0,0 @@ -diff -urN rhythmbox-0.11.4/configure.ac rhythmbox-0.11.4.new/configure.ac ---- rhythmbox-0.11.4/configure.ac 2007-12-20 11:56:44.000000000 +0000 -+++ rhythmbox-0.11.4.new/configure.ac 2008-02-06 00:57:27.000000000 +0000 -@@ -441,18 +441,25 @@ - dnl Check for libsoup, needed for DAAP and audioscrobbler - if test "x$enable_daap" = "xyes" || test "x$enable_audioscrobbler" != "xno"; then - PKG_CHECK_MODULES(SOUP, \ -- libsoup-2.2, -- have_libsoup=yes, -- have_libsoup=no) -- if test x"$have_libsoup" = "xno"; then -+ libsoup-2.4, -+ have_libsoup24=yes, -+ have_libsoup24=no) -+ if test x"$have_libsoup24" = "xno"; then - PKG_CHECK_MODULES(SOUP, -- libsoup-2.4, -- have_libsoup=yes, -- have_libsoup=no) -+ libsoup-2.2, -+ have_libsoup22=yes, -+ have_libsoup22=no) - fi -- if test x"$have_libsoup" = "xyes"; then -+ if test x"$have_libsoup24" = "xyes" || test x"$have_libsoup22" = "xyes"; then -+ have_libsoup=yes - AC_DEFINE(HAVE_LIBSOUP, 1, [Define if libsoup support is enabled]) - fi -+ if test x"$have_libsoup24" = "xyes"; then -+ AC_DEFINE(HAVE_LIBSOUP_2_4, 1, [Define if libsoup 2.4 support is enabled]) -+ fi -+ if test x"$have_libsoup22" = "xyes"; then -+ AC_DEFINE(HAVE_LIBSOUP_2_2, 1, [Define if libsoup 2.2 support is enabled]) -+ fi - fi - - AM_CONDITIONAL(USE_LIBSOUP, test x"$have_libsoup" = "xyes") -@@ -513,27 +520,9 @@ - - - --AC_PATH_X -- --if test x"$x_includes" != x"NONE" && test -n "$x_includes" ; then -- CFLAGS=$CFLAGS -I`echo $x_includes | sed -e "s/:/ -I/g"` --fi --if test x"$x_libraries" != x"NONE" && test -n "$x_libraries" ; then -- LIBS=-L`echo $x_libraries | sed -e "s/:/ -L/g"` --fi -- --have_xidle=no --AC_COMPILE_IFELSE([ -- #include --int main(int argc,char **argv) { -- return 0; --} --], have_xidle=yes) --AC_MSG_CHECKING(for XIDLE extension) --AC_MSG_RESULT($have_xidle) --if test x"$have_xidle" = "xyes" ; then -- AC_DEFINE(HAVE_XIDLE_EXTENSION, 1, [defined if you have X11/extensions/xidle.h]) --fi -+AC_PATH_XTRA -+CFLAGS="$CFLAGS $X_CFLAGS" -+#LIBS=$X_LIBS - - dnl Multimedia keys - have_xfree=no -@@ -1167,7 +1156,11 @@ - AC_MSG_NOTICE([ CD burning support disabled]) - fi - if test x"$enable_daap" = xyes; then -- AC_MSG_NOTICE([** DAAP (music sharing) support is enabled]) -+ if test x"$have_libsoup24" = "xyes"; then -+ AC_MSG_NOTICE([** DAAP (music sharing) support is enabled (using libsoup 2.4)]) -+ elif test x"$have_libsoup22" = "xyes"; then -+ AC_MSG_NOTICE([** DAAP (music sharing) support is enabled (using libsoup 2.2)]) -+ fi - else - AC_MSG_NOTICE([ DAAP (music sharing) support is disabled]) - fi -@@ -1197,7 +1190,11 @@ - AC_MSG_NOTICE([ gnome-keyring support disabled]) - fi - if test x"$enable_audioscrobbler" != xno; then -- AC_MSG_NOTICE([** Audioscrobbler support enabled]) -+ if test x"$have_libsoup24" = "xyes"; then -+ AC_MSG_NOTICE([** Audioscrobbler support enabled (using libsoup 2.4)]) -+ elif test x"$have_libsoup22" = "xyes"; then -+ AC_MSG_NOTICE([** Audioscrobbler support enabled (using libsoup 2.2)]) -+ fi - else - AC_MSG_NOTICE([ Audioscrobbler support disabled]) - fi -diff -urN rhythmbox-0.11.4/lib/Makefile.am rhythmbox-0.11.4.new/lib/Makefile.am ---- rhythmbox-0.11.4/lib/Makefile.am 2007-08-01 09:04:11.000000000 +0100 -+++ rhythmbox-0.11.4.new/lib/Makefile.am 2008-02-06 00:57:27.000000000 +0000 -@@ -34,7 +34,8 @@ - rb-string-value-map.c \ - rb-string-value-map.h \ - rb-async-queue-watch.c \ -- rb-async-queue-watch.h -+ rb-async-queue-watch.h \ -+ rb-soup-compat.h - - if WITH_INTERNAL_GSEQUENCE - librb_la_SOURCES += gsequence.c gsequence.h -diff -urN rhythmbox-0.11.4/lib/rb-proxy-config.c rhythmbox-0.11.4.new/lib/rb-proxy-config.c ---- rhythmbox-0.11.4/lib/rb-proxy-config.c 2007-06-03 02:55:02.000000000 +0100 -+++ rhythmbox-0.11.4.new/lib/rb-proxy-config.c 2008-02-06 00:57:27.000000000 +0000 -@@ -231,7 +231,28 @@ - } - } - --#if defined(HAVE_LIBSOUP) -+#if defined(HAVE_LIBSOUP_2_4) -+SoupURI * -+rb_proxy_config_get_libsoup_uri (RBProxyConfig *config) -+{ -+ SoupURI *uri = NULL; -+ -+ if (!config->enabled) -+ return NULL; -+ -+ uri = soup_uri_new (NULL); -+ soup_uri_set_scheme (uri, SOUP_URI_SCHEME_HTTP); -+ soup_uri_set_host (uri, config->host); -+ soup_uri_set_port (uri, config->port); -+ -+ if (config->auth_enabled) { -+ soup_uri_set_user (uri, config->username); -+ soup_uri_set_password (uri, config->password); -+ } -+ -+ return uri; -+} -+#elif defined(HAVE_LIBSOUP_2_2) - SoupUri * - rb_proxy_config_get_libsoup_uri (RBProxyConfig *config) - { -diff -urN rhythmbox-0.11.4/lib/rb-proxy-config.h rhythmbox-0.11.4.new/lib/rb-proxy-config.h ---- rhythmbox-0.11.4/lib/rb-proxy-config.h 2007-06-03 02:55:02.000000000 +0100 -+++ rhythmbox-0.11.4.new/lib/rb-proxy-config.h 2008-02-06 00:57:27.000000000 +0000 -@@ -24,8 +24,8 @@ - #include - - #if defined(HAVE_LIBSOUP) -+#include "rb-soup-compat.h" - #include --#include - #endif - - G_BEGIN_DECLS -@@ -65,9 +65,7 @@ - - RBProxyConfig * rb_proxy_config_new (void); - --#if defined(HAVE_LIBSOUP) --SoupUri * rb_proxy_config_get_libsoup_uri (RBProxyConfig *config); --#endif -+SoupURI * rb_proxy_config_get_libsoup_uri (RBProxyConfig *config); - - #endif /* RB_PROXY_CONFIG_H */ - -diff -urN rhythmbox-0.11.4/lib/rb-soup-compat.h rhythmbox-0.11.4.new/lib/rb-soup-compat.h ---- rhythmbox-0.11.4/lib/rb-soup-compat.h 1970-01-01 01:00:00.000000000 +0100 -+++ rhythmbox-0.11.4.new/lib/rb-soup-compat.h 2008-02-06 00:57:27.000000000 +0000 -@@ -0,0 +1,59 @@ -+/* -+ * Copyright (C) 2008 Jonathan Matthew -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program 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 General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -+ * 02110-1301 USA -+ */ -+ -+#ifndef RB_SOUP_COMPAT_H -+#define RB_SOUP_COMPAT_H -+ -+#include -+ -+/* compatibility junk for libsoup 2.2. -+ * not intended to obviate the need for #ifdefs in code, but -+ * should remove a lot of the trivial ones and make it easier -+ * to drop libsoup 2.2 -+ */ -+#if defined(HAVE_LIBSOUP_2_2) -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+ -+typedef SoupUri SoupURI; -+typedef SoupMessageCallbackFn SoupSessionCallback; -+typedef SoupServerContext SoupClientContext; -+ -+#define SOUP_MEMORY_TAKE SOUP_BUFFER_SYSTEM_OWNED -+#define SOUP_MEMORY_TEMPORARY SOUP_BUFFER_USER_OWNED -+ -+#define soup_message_headers_append soup_message_add_header -+#define soup_message_headers_get soup_message_get_header -+ -+#define soup_client_context_get_host soup_server_context_get_client_host -+ -+#endif /* HAVE_LIBSOUP_2_2 */ -+ -+#endif /* RB_SOUP_COMPAT_H */ -+ -diff -urN rhythmbox-0.11.4/plugins/audioscrobbler/rb-audioscrobbler.c rhythmbox-0.11.4.new/plugins/audioscrobbler/rb-audioscrobbler.c ---- rhythmbox-0.11.4/plugins/audioscrobbler/rb-audioscrobbler.c 2007-12-17 09:26:53.000000000 +0000 -+++ rhythmbox-0.11.4.new/plugins/audioscrobbler/rb-audioscrobbler.c 2008-02-06 00:59:48.000000000 +0000 -@@ -24,6 +24,8 @@ - - #define __EXTENSIONS__ - -+#include "config.h" -+ - #include - - #include -@@ -35,10 +37,9 @@ - #include - #include - -+#include "rb-soup-compat.h" - #include --#include - --#include "config.h" - #include "eel-gconf-extensions.h" - #include "rb-audioscrobbler.h" - #include "rb-debug.h" -@@ -193,14 +194,20 @@ - - static gchar * mkmd5 (char *string); - static void rb_audioscrobbler_parse_response (RBAudioscrobbler *audioscrobbler, SoupMessage *msg); --static void rb_audioscrobbler_perform (RBAudioscrobbler *audioscrobbler, -- char *url, -- char *post_data, -- SoupMessageCallbackFn response_handler); -+ - static void rb_audioscrobbler_do_handshake (RBAudioscrobbler *audioscrobbler); --static void rb_audioscrobbler_do_handshake_cb (SoupMessage *msg, gpointer user_data); - static void rb_audioscrobbler_submit_queue (RBAudioscrobbler *audioscrobbler); -+static void rb_audioscrobbler_perform (RBAudioscrobbler *audioscrobbler, -+ char *url, -+ char *post_data, -+ SoupSessionCallback response_handler); -+#if defined(HAVE_LIBSOUP_2_4) -+static void rb_audioscrobbler_do_handshake_cb (SoupSession *session, SoupMessage *msg, gpointer user_data); -+static void rb_audioscrobbler_submit_queue_cb (SoupSession *session, SoupMessage *msg, gpointer user_data); -+#else -+static void rb_audioscrobbler_do_handshake_cb (SoupMessage *msg, gpointer user_data); - static void rb_audioscrobbler_submit_queue_cb (SoupMessage *msg, gpointer user_data); -+#endif - - static void rb_audioscrobbler_import_settings (RBAudioscrobbler *audioscrobbler); - static void rb_audioscrobbler_preferences_sync (RBAudioscrobbler *audioscrobbler); -@@ -688,18 +695,31 @@ - static void - rb_audioscrobbler_parse_response (RBAudioscrobbler *audioscrobbler, SoupMessage *msg) - { -+ gboolean successful; - rb_debug ("Parsing response, status=%d", msg->status_code); -- -- if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code) && (msg->response).body != NULL) { -- gchar *body; -+ -+ successful = FALSE; -+#if defined(HAVE_LIBSOUP_2_4) -+ if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code) && msg->response_body->length != 0) -+ successful = TRUE; -+#else -+ if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code) && (msg->response).body != NULL) -+ successful = TRUE; -+#endif -+ if (successful) { - gchar **breaks; -+ int i; -+#if defined(HAVE_LIBSOUP_2_2) -+ gchar *body; - - body = g_malloc0 ((msg->response).length + 1); - memcpy (body, (msg->response).body, (msg->response).length); - - g_strstrip (body); - breaks = g_strsplit (body, "\n", 4); -- int i; -+#else -+ breaks = g_strsplit (msg->response_body->data, "\n", 4); -+#endif - - g_free (audioscrobbler->priv->status_msg); - audioscrobbler->priv->status = STATUS_OK; -@@ -771,10 +791,12 @@ - audioscrobbler->priv->submit_next = time(NULL) + audioscrobbler->priv->submit_interval; - - g_strfreev (breaks); -+#if defined(HAVE_LIBSOUP_2_2) - g_free (body); -+#endif - } else { - audioscrobbler->priv->status = REQUEST_FAILED; -- audioscrobbler->priv->status_msg = g_strdup (soup_status_get_phrase (msg->status_code)); -+ audioscrobbler->priv->status_msg = g_strdup (msg->reason_phrase); - } - } - -@@ -793,24 +815,25 @@ - rb_audioscrobbler_perform (RBAudioscrobbler *audioscrobbler, - char *url, - char *post_data, -- SoupMessageCallbackFn response_handler) -+ SoupSessionCallback response_handler) - { - SoupMessage *msg; - - msg = soup_message_new (post_data == NULL ? "GET" : "POST", url); -+ soup_message_headers_append (msg->request_headers, "User-Agent", "Rhythmbox/" VERSION); - - if (post_data != NULL) { - rb_debug ("Submitting to Audioscrobbler: %s", post_data); - soup_message_set_request (msg, - "application/x-www-form-urlencoded", -- SOUP_BUFFER_SYSTEM_OWNED, -+ SOUP_MEMORY_TAKE, - post_data, - strlen (post_data)); - } - - /* create soup session, if we haven't got one yet */ - if (!audioscrobbler->priv->soup_session) { -- SoupUri *uri; -+ SoupURI *uri; - - uri = rb_proxy_config_get_libsoup_uri (audioscrobbler->priv->proxy_config); - audioscrobbler->priv->soup_session = soup_session_async_new_with_options ( -@@ -891,8 +914,13 @@ - } - - -+#if defined(HAVE_LIBSOUP_2_4) -+static void -+rb_audioscrobbler_do_handshake_cb (SoupSession *session, SoupMessage *msg, gpointer user_data) -+#else - static void - rb_audioscrobbler_do_handshake_cb (SoupMessage *msg, gpointer user_data) -+#endif - { - RBAudioscrobbler *audioscrobbler = RB_AUDIOSCROBBLER(user_data); - -@@ -1049,8 +1077,13 @@ - } - } - -+#if defined(HAVE_LIBSOUP_2_4) -+static void -+rb_audioscrobbler_submit_queue_cb (SoupSession *session, SoupMessage *msg, gpointer user_data) -+#else - static void - rb_audioscrobbler_submit_queue_cb (SoupMessage *msg, gpointer user_data) -+#endif - { - RBAudioscrobbler *audioscrobbler = RB_AUDIOSCROBBLER (user_data); - -@@ -1232,7 +1265,7 @@ - rb_audioscrobbler_proxy_config_changed_cb (RBProxyConfig *config, - RBAudioscrobbler *audioscrobbler) - { -- SoupUri *uri; -+ SoupURI *uri; - - if (audioscrobbler->priv->soup_session) { - uri = rb_proxy_config_get_libsoup_uri (config); -diff -urN rhythmbox-0.11.4/plugins/audioscrobbler/rb-lastfm-gst-src.c rhythmbox-0.11.4.new/plugins/audioscrobbler/rb-lastfm-gst-src.c ---- rhythmbox-0.11.4/plugins/audioscrobbler/rb-lastfm-gst-src.c 2007-06-19 12:38:41.000000000 +0100 -+++ rhythmbox-0.11.4.new/plugins/audioscrobbler/rb-lastfm-gst-src.c 2008-02-06 00:57:27.000000000 +0000 -@@ -25,7 +25,6 @@ - - #include "rb-debug.h" - --#include - #include - - #define RB_TYPE_LASTFM_SRC (rb_lastfm_src_get_type()) -diff -urN rhythmbox-0.11.4/plugins/audioscrobbler/rb-lastfm-source.c rhythmbox-0.11.4.new/plugins/audioscrobbler/rb-lastfm-source.c ---- rhythmbox-0.11.4/plugins/audioscrobbler/rb-lastfm-source.c 2007-12-04 03:05:39.000000000 +0000 -+++ rhythmbox-0.11.4.new/plugins/audioscrobbler/rb-lastfm-source.c 2008-02-06 01:01:13.000000000 +0000 -@@ -42,8 +42,8 @@ - - #include - -+#include "rb-soup-compat.h" - #include --#include - - #include "md5.h" - -@@ -100,8 +100,12 @@ - static void rb_lastfm_perform (RBLastfmSource *lastfm, - const char *url, - char *post_data, /* this takes ownership */ -- SoupMessageCallbackFn response_handler); -+ SoupSessionCallback response_handler); -+#if defined(HAVE_LIBSOUP_2_4) -+static void rb_lastfm_message_cb (SoupSession *session, SoupMessage *req, gpointer user_data); -+#else - static void rb_lastfm_message_cb (SoupMessage *req, gpointer user_data); -+#endif - static void rb_lastfm_change_station (RBLastfmSource *source, const char *station); - - static void rb_lastfm_proxy_config_changed_cb (RBProxyConfig *config, -@@ -724,30 +728,29 @@ - rb_lastfm_perform (RBLastfmSource *source, - const char *url, - char *post_data, -- SoupMessageCallbackFn response_handler) -+ SoupSessionCallback response_handler) - { - SoupMessage *msg; - msg = soup_message_new ("GET", url); -+ soup_message_headers_append (msg->request_headers, "User-Agent", "Rhythmbox/" VERSION); - - if (msg == NULL) - return; - -- soup_message_set_http_version (msg, SOUP_HTTP_1_1); -- - rb_debug ("Last.fm communicating with %s", url); - - if (post_data != NULL) { - rb_debug ("POST data: %s", post_data); - soup_message_set_request (msg, - "application/x-www-form-urlencoded", -- SOUP_BUFFER_SYSTEM_OWNED, -+ SOUP_MEMORY_TAKE, - post_data, - strlen (post_data)); - } - - /* create soup session, if we haven't got one yet */ - if (!source->priv->soup_session) { -- SoupUri *uri; -+ SoupURI *uri; - - uri = rb_proxy_config_get_libsoup_uri (source->priv->proxy_config); - source->priv->soup_session = soup_session_async_new_with_options ( -@@ -759,27 +762,45 @@ - - soup_session_queue_message (source->priv->soup_session, - msg, -- (SoupMessageCallbackFn) response_handler, -+ response_handler, - source); - source->priv->status = COMMUNICATING; - rb_source_notify_status_changed (RB_SOURCE(source)); - } - -+#if defined(HAVE_LIBSOUP_2_4) -+static void -+rb_lastfm_message_cb (SoupSession *session, SoupMessage *req, gpointer user_data) -+#else - static void - rb_lastfm_message_cb (SoupMessage *req, gpointer user_data) -+#endif - { - RBLastfmSource *source = RB_LASTFM_SOURCE (user_data); -- char *body; - char **pieces; - int i; -+ const char *body; -+ -+#if defined(HAVE_LIBSOUP_2_2) -+ char *free_body; - - if ((req->response).body == NULL) { - rb_debug ("Lastfm: Server failed to respond"); - return; - } - -- body = g_malloc0 ((req->response).length + 1); -- memcpy (body, (req->response).body, (req->response).length); -+ free_body = g_malloc0 ((req->response).length + 1); -+ memcpy (free_body, (req->response).body, (req->response).length); -+ g_strstrip (free_body); -+ -+ body = free_body; -+#else -+ if (req->response_body->length == 0) { -+ rb_debug ("Lastfm: Server failed to respond"); -+ return; -+ } -+ body = req->response_body->data; -+#endif - - rb_debug ("response body: %s", body); - -@@ -787,7 +808,6 @@ - source->priv->status = NO_ARTIST; - } - -- g_strstrip (body); - pieces = g_strsplit (body, "\n", 0); - for (i = 0; pieces[i] != NULL; i++) { - gchar **values = g_strsplit (pieces[i], "=", 2); -@@ -856,10 +876,14 @@ - rhythmdb_commit (source->priv->db); - - } -+ -+ g_strfreev (values); - } - - g_strfreev (pieces); -- g_free (body); -+#if defined(HAVE_LIBSOUP_2_2) -+ g_free (free_body); -+#endif - - /* doesn't work yet - if (source->priv->pending_entry) { -@@ -898,7 +922,7 @@ - rb_lastfm_proxy_config_changed_cb (RBProxyConfig *config, - RBLastfmSource *source) - { -- SoupUri *uri; -+ SoupURI *uri; - - if (source->priv->soup_session) { - uri = rb_proxy_config_get_libsoup_uri (config); -@@ -1163,10 +1187,16 @@ - g_free(title); - } - -+#if defined(HAVE_LIBSOUP_2_4) -+static void -+rb_lastfm_source_metadata_cb (SoupSession *session, SoupMessage *req, RBLastfmSource *source) -+#else - static void - rb_lastfm_source_metadata_cb (SoupMessage *req, RBLastfmSource *source) -+#endif - { -- char *body; -+ const char *body; -+ char *free_body; - char **pieces; - int p; - RhythmDBEntry *entry; -@@ -1179,10 +1209,16 @@ - } - - rb_debug ("got response to metadata request"); -- body = g_malloc0 ((req->response).length + 1); -- memcpy (body, (req->response).body, (req->response).length); -+#if defined(HAVE_LIBSOUP_2_4) -+ body = req->response_body->data; -+ free_body = NULL; -+#else -+ free_body = g_malloc0 ((req->response).length + 1); -+ memcpy (free_body, (req->response).body, (req->response).length); -+ g_strstrip (free_body); -+ body = free_body; -+#endif - -- g_strstrip (body); - pieces = g_strsplit (body, "\n", 0); - found_cover = FALSE; - -@@ -1238,7 +1274,9 @@ - } - - g_strfreev (pieces); -- g_free (body); -+#if defined(HAVE_LIBSOUP_2_2) -+ g_free (free_body); -+#endif - - if (found_cover == FALSE) { - GValue v = {0,}; -@@ -1271,7 +1309,7 @@ - source->priv->base_url, - source->priv->base_path, - source->priv->session); -- rb_lastfm_perform (source, uri, NULL, (SoupMessageCallbackFn) rb_lastfm_source_metadata_cb); -+ rb_lastfm_perform (source, uri, NULL, (SoupSessionCallback) rb_lastfm_source_metadata_cb); - g_free (uri); - - /* re-enable actions */ -diff -urN rhythmbox-0.11.4/plugins/daap/rb-daap-connection.c rhythmbox-0.11.4.new/plugins/daap/rb-daap-connection.c ---- rhythmbox-0.11.4/plugins/daap/rb-daap-connection.c 2007-11-22 08:23:50.000000000 +0000 -+++ rhythmbox-0.11.4.new/plugins/daap/rb-daap-connection.c 2008-02-06 00:57:27.000000000 +0000 -@@ -33,10 +33,8 @@ - #include - #include - -+#include "rb-soup-compat.h" - #include --#include --#include --#include - - #include "rb-daap-hash.h" - #include "rb-daap-connection.h" -@@ -84,7 +82,7 @@ - gboolean is_connecting; - - SoupSession *session; -- SoupUri *base_uri; -+ SoupURI *base_uri; - gchar *daap_base_uri; - - gdouble daap_version; -@@ -329,7 +327,7 @@ - { - RBDAAPConnectionPrivate *priv = connection->priv; - SoupMessage *message = NULL; -- SoupUri *uri = NULL; -+ SoupURI *uri = NULL; - - uri = soup_uri_new_with_base (priv->base_uri, path); - if (uri == NULL) { -@@ -337,14 +335,13 @@ - } - - message = soup_message_new_from_uri (SOUP_METHOD_GET, uri); -- soup_message_set_http_version (message, SOUP_HTTP_1_1); - -- soup_message_add_header (message->request_headers, "Client-DAAP-Version", "3.0"); -- soup_message_add_header (message->request_headers, "Accept-Language", "en-us, en;q=5.0"); -+ soup_message_headers_append (message->request_headers, "Client-DAAP-Version", "3.0"); -+ soup_message_headers_append (message->request_headers, "Accept-Language", "en-us, en;q=5.0"); - #ifdef HAVE_LIBZ -- soup_message_add_header (message->request_headers, "Accept-Encoding", "gzip"); -+ soup_message_headers_append (message->request_headers, "Accept-Encoding", "gzip"); - #endif -- soup_message_add_header (message->request_headers, "Client-DAAP-Access-Index", "2"); -+ soup_message_headers_append (message->request_headers, "Client-DAAP-Access-Index", "2"); - - if (priv->password_protected) { - char *h; -@@ -352,13 +349,17 @@ - char *token; - - user_pass = g_strdup_printf ("%s:%s", priv->username, priv->password); -+#if defined(HAVE_LIBSOUP_2_4) -+ token = g_base64_encode ((guchar *)user_pass, strlen (user_pass)); -+#else - token = soup_base64_encode (user_pass, strlen (user_pass)); -+#endif - h = g_strdup_printf ("Basic %s", token); - - g_free (token); - g_free (user_pass); - -- soup_message_add_header (message->request_headers, "Authorization", h); -+ soup_message_headers_append (message->request_headers, "Authorization", h); - g_free (h); - } - -@@ -372,10 +373,10 @@ - - rb_daap_hash_generate ((short)floor (version), (const guchar*)no_daap_path, 2, (guchar*)hash, req_id); - -- soup_message_add_header (message->request_headers, "Client-DAAP-Validation", hash); -+ soup_message_headers_append (message->request_headers, "Client-DAAP-Validation", hash); - } - if (send_close) { -- soup_message_add_header (message->request_headers, "Connection", "close"); -+ soup_message_headers_append (message->request_headers, "Connection", "close"); - } - - soup_uri_free (uri); -@@ -425,16 +426,22 @@ - { - RBDAAPConnectionPrivate *priv; - GNode *structure; -- char *response; -+ char *new_response = NULL; -+ const char *response; - const char *encoding_header; - char *message_path; - int response_length; - - priv = data->connection->priv; - structure = NULL; -- response = data->message->response.body; - encoding_header = NULL; -+#if defined(HAVE_LIBSOUP_2_4) -+ response = data->message->response_body->data; -+ response_length = data->message->response_body->length; -+#else -+ response = data->message->response.body; - response_length = data->message->response.length; -+#endif - - message_path = soup_uri_to_string (soup_message_get_uri (data->message), FALSE); - -@@ -444,13 +451,12 @@ - data->message->reason_phrase); - - if (data->message->response_headers) { -- encoding_header = soup_message_get_header (data->message->response_headers, "Content-Encoding"); -+ encoding_header = soup_message_headers_get (data->message->response_headers, "Content-Encoding"); - } - - if (SOUP_STATUS_IS_SUCCESSFUL (data->status) && encoding_header && strcmp (encoding_header, "gzip") == 0) { - #ifdef HAVE_LIBZ - z_stream stream; -- char *new_response; - unsigned int factor = 4; - unsigned int unc_size = response_length * factor; - -@@ -575,19 +581,23 @@ - rb_daap_structure_destroy (structure); - } - -- if (response != data->message->response.body) { -- g_free (response); -- } -- -+ g_free (new_response); - g_free (message_path); - g_object_unref (G_OBJECT (data->connection)); - g_object_unref (G_OBJECT (data->message)); - g_free (data); - } - -+#if defined(HAVE_LIBSOUP_2_4) -+static void -+http_response_handler (SoupSession *session, -+ SoupMessage *message, -+ RBDAAPConnection *connection) -+#else - static void - http_response_handler (SoupMessage *message, - RBDAAPConnection *connection) -+#endif - { - DAAPResponseData *data; - int response_length; -@@ -599,7 +609,11 @@ - - data = g_new0 (DAAPResponseData, 1); - data->status = message->status_code; -+#if defined(HAVE_LIBSOUP_2_4) -+ response_length = message->response_body->length; -+#else - response_length = message->response.length; -+#endif - - g_object_ref (G_OBJECT (connection)); - data->connection = connection; -@@ -655,7 +669,7 @@ - priv->use_response_handler_thread = use_thread; - priv->response_handler = handler; - soup_session_queue_message (priv->session, message, -- (SoupMessageCallbackFn) http_response_handler, -+ (SoupSessionCallback) http_response_handler, - connection); - rb_debug ("Queued message for http://%s:%d/%s", - priv->base_uri->host, -@@ -1662,7 +1676,11 @@ - char *token; - - user_pass = g_strdup_printf ("%s:%s", priv->username, priv->password); -+#if defined(HAVE_LIBSOUP_2_4) -+ token = g_base64_encode ((guchar *)user_pass, strlen (user_pass)); -+#else - token = soup_base64_encode (user_pass, strlen (user_pass)); -+#endif - g_string_append_printf (headers, "Authentication: Basic %s\r\n", token); - g_free (token); - g_free (user_pass); -diff -urN rhythmbox-0.11.4/plugins/daap/rb-daap-plugin.c rhythmbox-0.11.4.new/plugins/daap/rb-daap-plugin.c ---- rhythmbox-0.11.4/plugins/daap/rb-daap-plugin.c 2007-09-03 08:24:09.000000000 +0100 -+++ rhythmbox-0.11.4.new/plugins/daap/rb-daap-plugin.c 2008-02-06 00:57:27.000000000 +0000 -@@ -29,8 +29,8 @@ - #include - #include - -+#include "rb-soup-compat.h" - #include --#include - - #include "rb-daap-plugin.h" - #include "rb-debug.h" -@@ -647,6 +647,9 @@ - g_free (host); - - soup_address_resolve_async (addr, -+#if defined(HAVE_LIBSOUP_2_4) -+ NULL, NULL, -+#endif - (SoupAddressCallback) new_daap_share_resolve_cb, - data); - } -diff -urN rhythmbox-0.11.4/plugins/daap/rb-daap-share.c rhythmbox-0.11.4.new/plugins/daap/rb-daap-share.c ---- rhythmbox-0.11.4/plugins/daap/rb-daap-share.c 2007-11-08 13:09:20.000000000 +0000 -+++ rhythmbox-0.11.4.new/plugins/daap/rb-daap-share.c 2008-02-06 00:57:27.000000000 +0000 -@@ -27,13 +27,9 @@ - - #include - #include -+ -+#include "rb-soup-compat.h" - #include --#include --#include --#include --#include --#include --#include - #include - - #include "rb-daap-share.h" -@@ -513,20 +509,22 @@ - static void - message_add_standard_headers (SoupMessage *message) - { -+#if defined(HAVE_LIBSOUP_2_2) - gchar *s; - time_t t; - struct tm *tm; - -- soup_message_add_header (message->response_headers, "DAAP-Server", "Rhythmbox " VERSION); -- -- soup_message_add_header (message->response_headers, "Content-Type", "application/x-dmap-tagged"); -- - t = time (NULL); - tm = gmtime (&t); - s = g_new (gchar, 100); - strftime (s, 100, "%a, %d %b %Y %T GMT", tm); -- soup_message_add_header (message->response_headers, "Date", s); -+ soup_message_headers_append (message->response_headers, "Date", s); - g_free (s); -+#endif -+ -+ soup_message_headers_append (message->response_headers, "DAAP-Server", "Rhythmbox " VERSION); -+ -+ soup_message_headers_append (message->response_headers, "Content-Type", "application/x-dmap-tagged"); - } - - static void -@@ -543,14 +541,15 @@ - return; - } - -- message->response.owner = SOUP_BUFFER_SYSTEM_OWNED; -- message->response.length = length; -- message->response.body = resp; -+ soup_message_set_response (message, "application/x-dmap-tagged", SOUP_MEMORY_TAKE, resp, length); -+ -+#if defined(HAVE_LIBSOUP_2_2) -+ soup_server_message_set_encoding (SOUP_SERVER_MESSAGE (message), SOUP_TRANSFER_CONTENT_LENGTH); -+#endif - - message_add_standard_headers (message); - - soup_message_set_status (message, SOUP_STATUS_OK); -- soup_server_message_set_encoding (SOUP_SERVER_MESSAGE (message), SOUP_TRANSFER_CONTENT_LENGTH); - } - - #define DMAP_STATUS_OK 200 -@@ -559,10 +558,20 @@ - #define DAAP_VERSION 3.0 - #define DMAP_TIMEOUT 1800 - -+#if defined(HAVE_LIBSOUP_2_4) -+static void -+server_info_cb (SoupServer *server, -+ SoupMessage *message, -+ const char *path, -+ GHashTable *query, -+ SoupClientContext *context, -+ RBDAAPShare *share) -+#else - static void - server_info_cb (RBDAAPShare *share, - SoupServerContext *context, - SoupMessage *message) -+#endif - { - /* MSRV server info response - * MSTT status -@@ -619,10 +628,20 @@ - rb_daap_structure_destroy (msrv); - } - -+#if defined(HAVE_LIBSOUP_2_4) -+static void -+content_codes_cb (SoupServer *server, -+ SoupMessage *message, -+ const char *path, -+ GHashTable *query, -+ SoupClientContext *context, -+ RBDAAPShare *share) -+#else - static void - content_codes_cb (RBDAAPShare *share, - SoupServerContext *context, - SoupMessage *message) -+#endif - { - /* MCCR content codes response - * MSTT status -@@ -656,6 +675,48 @@ - rb_daap_structure_destroy (mccr); - } - -+#if defined(HAVE_LIBSOUP_2_4) -+static gboolean -+get_session_id (GHashTable *query, -+ guint32 *id) -+{ -+ char *session_id_str; -+ guint32 session_id; -+ -+ session_id_str = g_hash_table_lookup (query, "session-id"); -+ if (session_id_str == NULL) { -+ rb_debug ("session id not found"); -+ return FALSE; -+ } -+ -+ session_id = (guint32) strtoul (session_id_str, NULL, 10); -+ if (id != NULL) { -+ *id = session_id; -+ } -+ return TRUE; -+} -+ -+static gboolean -+get_revision_number (GHashTable *query, -+ guint *number) -+{ -+ char *revision_number_str; -+ guint revision_number; -+ -+ revision_number_str = g_hash_table_lookup (query, "revision-number"); -+ if (revision_number_str == NULL) { -+ rb_debug ("client asked for an update without a revision number?!?"); -+ return FALSE; -+ } -+ -+ revision_number = strtoul (revision_number_str, NULL, 10); -+ if (number != NULL) { -+ *number = revision_number; -+ } -+ return TRUE; -+} -+ -+#else - static gboolean - message_get_session_id (SoupMessage *message, - guint32 *id) -@@ -729,11 +790,13 @@ - - return TRUE; - } -+#endif - - static gboolean - session_id_validate (RBDAAPShare *share, -- SoupServerContext *context, -+ SoupClientContext *context, - SoupMessage *message, -+ GHashTable *query, /* NULL w/ libsoup 2.2 */ - guint32 *id) - { - guint32 session_id; -@@ -745,7 +808,11 @@ - *id = 0; - } - -+#if defined(HAVE_LIBSOUP_2_4) -+ res = get_session_id (query, &session_id); -+#else - res = message_get_session_id (message, &session_id); -+#endif - if (! res) { - rb_debug ("Validation failed: Unable to parse session id from message"); - return FALSE; -@@ -758,7 +825,7 @@ - return FALSE; - } - -- remote_address = soup_server_context_get_client_host (context); -+ remote_address = soup_client_context_get_host (context); - rb_debug ("Validating session id %u from %s matches %s", - session_id, remote_address, addr); - if (remote_address == NULL || strcmp (addr, remote_address) != 0) { -@@ -775,7 +842,7 @@ - - static guint32 - session_id_generate (RBDAAPShare *share, -- SoupServerContext *context) -+ SoupClientContext *context) - { - guint32 id; - -@@ -786,7 +853,7 @@ - - static guint32 - session_id_create (RBDAAPShare *share, -- SoupServerContext *context) -+ SoupClientContext *context) - { - guint32 id; - const char *addr; -@@ -802,7 +869,7 @@ - } while (addr != NULL); - - /* store session id and remote address */ -- remote_address = g_strdup (soup_server_context_get_client_host (context)); -+ remote_address = g_strdup (soup_client_context_get_host (context)); - g_hash_table_insert (share->priv->session_ids, GUINT_TO_POINTER (id), remote_address); - - return id; -@@ -810,16 +877,26 @@ - - static void - session_id_remove (RBDAAPShare *share, -- SoupServerContext *context, -+ SoupClientContext *context, - guint32 id) - { - g_hash_table_remove (share->priv->session_ids, GUINT_TO_POINTER (id)); - } - -+#if defined(HAVE_LIBSOUP_2_4) - static void --login_cb (RBDAAPShare *share, -+login_cb (SoupServer *server, -+ SoupMessage *message, -+ const char *path, -+ GHashTable *query, -+ SoupClientContext *context, -+ RBDAAPShare *share) -+#else -+static void -+login_cb (RBDAAPShare *share, - SoupServerContext *context, -- SoupMessage *message) -+ SoupMessage *message) -+#endif - { - /* MLOG login response - * MSTT status -@@ -840,15 +917,28 @@ - rb_daap_structure_destroy (mlog); - } - -+#if defined(HAVE_LIBSOUP_2_4) -+static void -+logout_cb (SoupServer *server, -+ SoupMessage *message, -+ const char *path, -+ GHashTable *query, -+ SoupClientContext *context, -+ RBDAAPShare *share) -+#else - static void - logout_cb (RBDAAPShare *share, - SoupServerContext *context, - SoupMessage *message) -+#endif - { - int status; - guint32 id; -+#if defined(HAVE_LIBSOUP_2_2) -+ GHashTable *query = NULL; -+#endif - -- if (session_id_validate (share, context, message, &id)) { -+ if (session_id_validate (share, context, message, query, &id)) { - rb_debug ("Handling logout session id %u", id); - session_id_remove (share, context, id); - -@@ -858,18 +948,34 @@ - } - - soup_message_set_status (message, status); -+#if defined(HAVE_LIBSOUP_2_2) - soup_server_message_set_encoding (SOUP_SERVER_MESSAGE (message), SOUP_TRANSFER_CONTENT_LENGTH); -+#endif - } - -+#if defined(HAVE_LIBSOUP_2_4) -+static void -+update_cb (SoupServer *server, -+ SoupMessage *message, -+ const char *path, -+ GHashTable *query, -+ SoupClientContext *context, -+ RBDAAPShare *share) -+#else - static void - update_cb (RBDAAPShare *share, - SoupServerContext *context, - SoupMessage *message) -+#endif - { - guint revision_number; - gboolean res; - -+#if defined(HAVE_LIBSOUP_2_4) -+ res = get_revision_number (query, &revision_number); -+#else - res = message_get_revision_number (message, &revision_number); -+#endif - - if (res && revision_number != share->priv->revision_number) { - /* MUPD update response -@@ -885,8 +991,15 @@ - message_set_from_rb_daap_structure (message, mupd); - rb_daap_structure_destroy (mupd); - } else { -+ /* FIXME: This seems like a bug. It just leaks the -+ * message (and socket) without ever replying. -+ */ - g_object_ref (message); -+#if defined(HAVE_LIBSOUP_2_4) -+ soup_server_pause_message (server, message); -+#else - soup_message_io_pause (message); -+#endif - } - } - -@@ -1148,14 +1261,47 @@ - } - - static bitwise -+parse_meta_str (const char *attrs) -+{ -+ gchar **attrsv; -+ guint i; -+ bitwise bits = 0; -+ -+ attrsv = g_strsplit (attrs, ",", -1); -+ -+ for (i = 0; attrsv[i]; i++) { -+ guint j; -+ -+ for (j = 0; j < G_N_ELEMENTS (meta_data_map); j++) { -+ if (strcmp (meta_data_map[j].tag, attrsv[i]) == 0) { -+ bits |= (((bitwise) 1) << meta_data_map[j].md); -+ } -+ } -+ } -+ -+ g_strfreev (attrsv); -+ -+ return bits; -+} -+ -+#if defined(HAVE_LIBSOUP_2_4) -+static bitwise -+parse_meta (GHashTable *query) -+{ -+ const gchar *attrs; -+ -+ attrs = g_hash_table_lookup (query, "meta"); -+ return parse_meta_str (attrs); -+} -+ -+#else -+static bitwise - parse_meta (const gchar *s) - { -+ bitwise bits; - gchar *start_of_attrs; - gchar *end_of_attrs; - gchar *attrs; -- gchar **attrsv; -- guint i; -- bitwise bits = 0; - - start_of_attrs = strstr (s, "meta="); - if (start_of_attrs == NULL) { -@@ -1170,23 +1316,12 @@ - attrs = g_strdup (start_of_attrs); - } - -- attrsv = g_strsplit (attrs,",",-1); -- -- for (i = 0; attrsv[i]; i++) { -- guint j; -- -- for (j = 0; j < G_N_ELEMENTS (meta_data_map); j++) { -- if (strcmp (meta_data_map[j].tag, attrsv[i]) == 0) { -- bits |= (((bitwise) 1) << meta_data_map[j].md); -- } -- } -- } -- -+ bits = parse_meta_str (attrs); - g_free (attrs); -- g_strfreev (attrsv); - - return bits; - } -+#endif - - static void - write_next_chunk (SoupMessage *message, GnomeVFSHandle *handle) -@@ -1197,10 +1332,18 @@ - - result = gnome_vfs_read (handle, chunk, DAAP_SHARE_CHUNK_SIZE, &read_size); - if (result == GNOME_VFS_OK && read_size > 0) { -+#if defined(HAVE_LIBSOUP_2_4) -+ soup_message_body_append (message->response_body, SOUP_MEMORY_TAKE, chunk, read_size); -+#else - soup_message_add_chunk (message, SOUP_BUFFER_SYSTEM_OWNED, chunk, read_size); -+#endif - } else { - g_free (chunk); -+#if defined(HAVE_LIBSOUP_2_4) -+ soup_message_body_complete (message->response_body); -+#else - soup_message_add_final_chunk (message); -+#endif - } - } - -@@ -1238,7 +1381,11 @@ - file_size -= offset; - } - -+#if defined(HAVE_LIBSOUP_2_4) -+ soup_message_headers_set_encoding (message->response_headers, SOUP_ENCODING_CHUNKED); -+#else - soup_server_message_set_encoding (SOUP_SERVER_MESSAGE (message), SOUP_TRANSFER_CHUNKED); -+#endif - - g_signal_connect (message, "wrote_chunk", G_CALLBACK (write_next_chunk), handle); - g_signal_connect (message, "finished", G_CALLBACK (chunked_message_finished), handle); -@@ -1268,11 +1415,14 @@ - g_warning ("Unable to map file %s: %s", path, error->message); - soup_message_set_status (message, SOUP_STATUS_INTERNAL_SERVER_ERROR); - } else { -- message->response.owner = SOUP_BUFFER_USER_OWNED; -- message->response.length = file_size; -- message->response.body = g_mapped_file_get_contents (mapped_file) + offset; -+ soup_message_set_response (message, "application/x-dmap-tagged", -+ SOUP_MEMORY_TEMPORARY, -+ g_mapped_file_get_contents (mapped_file) + offset, -+ file_size); -+#if defined(HAVE_LIBSOUP_2_2) - soup_server_message_set_encoding (SOUP_SERVER_MESSAGE (message), - SOUP_TRANSFER_CONTENT_LENGTH); -+#endif - - g_signal_connect (message, - "finished", -@@ -1283,22 +1433,40 @@ - } - #endif - -+#if defined(HAVE_LIBSOUP_2_4) -+static void -+databases_cb (SoupServer *server, -+ SoupMessage *message, -+ const char *path, -+ GHashTable *query, -+ SoupClientContext *context, -+ RBDAAPShare *share) -+ -+#else - static void - databases_cb (RBDAAPShare *share, - SoupServerContext *context, - SoupMessage *message) -+#endif - { -- gchar *path; -- gchar *rest_of_path; -+ const char *rest_of_path; - /*guint revision_number;*/ -+#if defined(HAVE_LIBSOUP_2_2) -+ GHashTable *query = NULL; -+ gchar *path; -+#endif - -- if (! session_id_validate (share, context, message, NULL)) { -+ if (! session_id_validate (share, context, message, query, NULL)) { - soup_message_set_status (message, SOUP_STATUS_FORBIDDEN); -+#if defined(HAVE_LIBSOUP_2_2) - soup_server_message_set_encoding (SOUP_SERVER_MESSAGE (message), SOUP_TRANSFER_CONTENT_LENGTH); -+#endif - return; - } - -+#if defined(HAVE_LIBSOUP_2_2) - path = soup_uri_to_string (soup_message_get_uri (message), TRUE); -+#endif - - rest_of_path = strchr (path + 1, '/'); - -@@ -1335,7 +1503,11 @@ - - message_set_from_rb_daap_structure (message, avdb); - rb_daap_structure_destroy (avdb); -+#if defined(HAVE_LIBSOUP_2_4) -+ } else if (g_ascii_strcasecmp ("/1/items", rest_of_path) == 0) { -+#else - } else if (g_ascii_strncasecmp ("/1/items?", rest_of_path, 9) == 0) { -+#endif - /* ADBS database songs - * MSTT status - * MUTY update type -@@ -1351,7 +1523,11 @@ - gint32 num_songs = rhythmdb_entry_count_by_type (share->priv->db, share->priv->entry_type); - struct MLCL_Bits mb = {NULL,0}; - -+#if defined(HAVE_LIBSOUP_2_4) -+ mb.bits = parse_meta (query); -+#else - mb.bits = parse_meta (rest_of_path); -+#endif - - adbs = rb_daap_structure_add (NULL, RB_DAAP_CC_ADBS); - rb_daap_structure_add (adbs, RB_DAAP_CC_MSTT, (gint32) DMAP_STATUS_OK); -@@ -1365,7 +1541,11 @@ - message_set_from_rb_daap_structure (message, adbs); - rb_daap_structure_destroy (adbs); - adbs = NULL; -+#if defined(HAVE_LIBSOUP_2_4) -+ } else if (g_ascii_strcasecmp ("/1/containers", rest_of_path) == 0) { -+#else - } else if (g_ascii_strncasecmp ("/1/containers?", rest_of_path, 14) == 0) { -+#endif - /* APLY database playlists - * MSTT status - * MUTY update type -@@ -1402,6 +1582,7 @@ - - message_set_from_rb_daap_structure (message, aply); - rb_daap_structure_destroy (aply); -+ - } else if (g_ascii_strncasecmp ("/1/containers/", rest_of_path, 14) == 0) { - /* APSO playlist songs - * MSTT status -@@ -1420,7 +1601,11 @@ - struct MLCL_Bits mb = {NULL,0}; - gint pl_id = atoi (rest_of_path + 14); - -+#if defined(HAVE_LIBSOUP_2_4) -+ mb.bits = parse_meta (query); -+#else - mb.bits = parse_meta (rest_of_path); -+#endif - - apso = rb_daap_structure_add (NULL, RB_DAAP_CC_APSO); - rb_daap_structure_add (apso, RB_DAAP_CC_MSTT, (gint32) DMAP_STATUS_OK); -@@ -1447,9 +1632,11 @@ - _find_by_id); - if (idl == NULL) { - soup_message_set_status (message, SOUP_STATUS_NOT_FOUND); -+#if defined(HAVE_LIBSOUP_2_2) - soup_server_message_set_encoding (SOUP_SERVER_MESSAGE (message), - SOUP_TRANSFER_CONTENT_LENGTH); - soup_message_set_response (message, "text/plain", SOUP_BUFFER_USER_OWNED, "", 0); -+#endif - goto out; - } - id = (RBPlaylistID *)idl->data; -@@ -1470,7 +1657,7 @@ - rb_daap_structure_destroy (apso); - } else if (g_ascii_strncasecmp ("/1/items/", rest_of_path, 9) == 0) { - /* just the file :) */ -- gchar *id_str; -+ const gchar *id_str; - gint id; - RhythmDBEntry *entry; - const gchar *location; -@@ -1486,9 +1673,9 @@ - file_size = rhythmdb_entry_get_uint64 (entry, RHYTHMDB_PROP_FILE_SIZE); - - message_add_standard_headers (message); -- soup_message_add_header (message->response_headers, "Accept-Ranges", "bytes"); -+ soup_message_headers_append (message->response_headers, "Accept-Ranges", "bytes"); - -- range_header = soup_message_get_header (message->request_headers, "Range"); -+ range_header = soup_message_headers_get (message->request_headers, "Range"); - if (range_header) { - const gchar *s; - gchar *content_range; -@@ -1497,7 +1684,7 @@ - offset = atoll (s); - - content_range = g_strdup_printf ("bytes %" G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT, offset, file_size, file_size); -- soup_message_add_header (message->response_headers, "Content-Range", content_range); -+ soup_message_headers_append (message->response_headers, "Content-Range", content_range); - g_free (content_range); - - soup_message_set_status (message, SOUP_STATUS_PARTIAL_CONTENT); -@@ -1522,9 +1709,92 @@ - } - - out: -+#if defined(HAVE_LIBSOUP_2_2) - g_free (path); -+#else -+ ; -+#endif -+} -+ -+static void -+db_entry_added_cb (RhythmDB *db, -+ RhythmDBEntry *entry, -+ RBDAAPShare *share) -+{ -+ /* TODO: update our db version number? */ -+} -+ -+static void -+add_db_entry (RhythmDBEntry *entry, -+ RBDAAPShare *share) -+{ -+ db_entry_added_cb (share->priv->db, entry, share); -+} -+ -+static void -+db_entry_deleted_cb (RhythmDB *db, -+ RhythmDBEntry *entry, -+ RBDAAPShare *share) -+{ -+ /* TODO: update our db version number? */ -+} -+ -+static void -+db_entry_changed_cb (RhythmDB *db, -+ RhythmDBEntry *entry, -+ GSList *changes, -+ RBDAAPShare *share) -+{ -+ if (rhythmdb_entry_get_boolean (entry, RHYTHMDB_PROP_HIDDEN)) { -+ db_entry_deleted_cb (db, entry, share); -+ } else { -+ db_entry_added_cb (db, entry, share); -+ } -+} -+ -+#if defined(HAVE_LIBSOUP_2_4) -+ -+static gboolean -+soup_auth_filter (SoupAuthDomain *auth_domain, -+ SoupMessage *msg, -+ gpointer user_data) -+{ -+ const char *path; -+ -+ path = soup_message_get_uri (msg)->path; -+ if (g_str_has_prefix (path, "/databases/")) { -+ /* Subdirectories of /databases don't actually require -+ * authentication -+ */ -+ return FALSE; -+ } else { -+ /* Everything else in auth_domain's paths, including -+ * /databases itself, does require auth. -+ */ -+ return TRUE; -+ } - } - -+static gboolean -+soup_auth_callback (SoupAuthDomain *auth_domain, -+ SoupMessage *msg, -+ const char *username, -+ gpointer password, -+ RBDAAPShare *share) -+{ -+ gboolean allowed; -+ const char *path; -+ -+ path = soup_message_get_uri (msg)->path; -+ rb_debug ("Auth request for %s, user %s", path, username); -+ -+ allowed = !strcmp (password, share->priv->password); -+ rb_debug ("Auth request: %s", allowed ? "ALLOWED" : "DENIED"); -+ -+ return allowed; -+} -+ -+#else - typedef void (* DAAPPathFunction) (RBDAAPShare *share, - SoupServerContext *context, - SoupMessage *message); -@@ -1567,42 +1837,6 @@ - g_free (path); - } - --static void --db_entry_added_cb (RhythmDB *db, -- RhythmDBEntry *entry, -- RBDAAPShare *share) --{ -- /* TODO: update our db version number? */ --} -- --static void --add_db_entry (RhythmDBEntry *entry, -- RBDAAPShare *share) --{ -- db_entry_added_cb (share->priv->db, entry, share); --} -- --static void --db_entry_deleted_cb (RhythmDB *db, -- RhythmDBEntry *entry, -- RBDAAPShare *share) --{ -- /* TODO: update our db version number? */ --} -- --static void --db_entry_changed_cb (RhythmDB *db, -- RhythmDBEntry *entry, -- GSList *changes, -- RBDAAPShare *share) --{ -- if (rhythmdb_entry_get_boolean (entry, RHYTHMDB_PROP_HIDDEN)) { -- db_entry_deleted_cb (db, entry, share); -- } else { -- db_entry_added_cb (db, entry, share); -- } --} -- - static gboolean - soup_auth_callback (SoupServerAuthContext *auth_ctx, - SoupServerAuth *auth, -@@ -1642,12 +1876,14 @@ - return allowed; - } - -+#endif -+ -+ - static gboolean - rb_daap_share_server_start (RBDAAPShare *share) - { - int port = STANDARD_DAAP_PORT; - gboolean password_required; -- SoupServerAuthContext auth_ctx = { 0 }; - - share->priv->server = soup_server_new (SOUP_SERVER_PORT, port, NULL); - if (share->priv->server == NULL) { -@@ -1666,6 +1902,22 @@ - password_required = (share->priv->auth_method != RB_DAAP_SHARE_AUTH_METHOD_NONE); - - if (password_required) { -+#if defined(HAVE_LIBSOUP_2_4) -+ SoupAuthDomain *auth_domain; -+ -+ auth_domain = soup_auth_domain_basic_new (SOUP_AUTH_DOMAIN_REALM, "Music Sharing", -+ SOUP_AUTH_DOMAIN_ADD_PATH, "/login", -+ SOUP_AUTH_DOMAIN_ADD_PATH, "/update", -+ SOUP_AUTH_DOMAIN_ADD_PATH, "/database", -+ SOUP_AUTH_DOMAIN_FILTER, soup_auth_filter, -+ NULL); -+ soup_auth_domain_basic_set_auth_callback (auth_domain, -+ (SoupAuthDomainBasicAuthCallback)soup_auth_callback, -+ g_object_ref (share), -+ g_object_unref); -+ soup_server_add_auth_domain (share->priv->server, auth_domain); -+#else -+ SoupServerAuthContext auth_ctx = { 0 }; - auth_ctx.types = SOUP_AUTH_TYPE_BASIC; - auth_ctx.callback = (SoupServerAuthCallbackFn)soup_auth_callback; - auth_ctx.user_data = share; -@@ -1689,14 +1941,36 @@ - (SoupServerCallbackFn)server_cb, - NULL, - share); -+#endif - } - -+#if defined(HAVE_LIBSOUP_2_4) -+ soup_server_add_handler (share->priv->server, "/server-info", -+ (SoupServerCallback) server_info_cb, -+ share, NULL); -+ soup_server_add_handler (share->priv->server, "/content-codes", -+ (SoupServerCallback) content_codes_cb, -+ share, NULL); -+ soup_server_add_handler (share->priv->server, "/login", -+ (SoupServerCallback) login_cb, -+ share, NULL); -+ soup_server_add_handler (share->priv->server, "/logout", -+ (SoupServerCallback) logout_cb, -+ share, NULL); -+ soup_server_add_handler (share->priv->server, "/update", -+ (SoupServerCallback) update_cb, -+ share, NULL); -+ soup_server_add_handler (share->priv->server, "/databases", -+ (SoupServerCallback) databases_cb, -+ share, NULL); -+#else - soup_server_add_handler (share->priv->server, - NULL, - NULL, - (SoupServerCallbackFn)server_cb, - NULL, - share); -+#endif - soup_server_run_async (share->priv->server); - - /* using direct since there is no g_uint_hash or g_uint_equal */ -diff -urN rhythmbox-0.11.4/plugins/daap/rb-daap-src.c rhythmbox-0.11.4.new/plugins/daap/rb-daap-src.c ---- rhythmbox-0.11.4/plugins/daap/rb-daap-src.c 2007-06-17 12:56:21.000000000 +0100 -+++ rhythmbox-0.11.4.new/plugins/daap/rb-daap-src.c 2008-02-06 00:57:27.000000000 +0000 -@@ -34,8 +34,8 @@ - #include - #include - --#include --#include -+#include "rb-soup-compat.h" -+#include - - #include - #include -@@ -432,7 +432,12 @@ - gchar *host; - guint port; - gchar *path; -+#if defined(HAVE_LIBSOUP_2_4) -+ SoupMessageHeaders *header_table; -+#else - GHashTable *header_table; -+ char *dup_headers; -+#endif - gchar *request; - gchar *response; - gchar *end_headers; -@@ -441,7 +446,6 @@ - guint http_status; - gchar *http_status_phrase = NULL; - gboolean parse_result; -- char *dup_headers; - - if (src->buffer_base) { - g_free (src->buffer_base); -@@ -536,6 +540,15 @@ - return FALSE; - } - -+#if defined(HAVE_LIBSOUP_2_4) -+ header_table = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE); -+ parse_result = soup_headers_parse_response (response, -+ ((end_headers+2) - response), -+ header_table, -+ NULL, -+ &http_status, -+ &http_status_phrase); -+#else - /* for compatibility with older versions of libsoup, we may need to retry - * the soup_headers_parse_response call with slightly different arguments. - * since this function modifies the string passed in, we need to copy it -@@ -567,28 +580,42 @@ - &http_status, - &http_status_phrase); - } -+#endif - - if (parse_result) { - if (http_status == 200 || http_status == 206) { -+ const char *enc_str = NULL; -+ const char *len_str = NULL; -+#if defined(HAVE_LIBSOUP_2_4) -+ enc_str = soup_message_headers_get (header_table, "Transfer-Encoding"); -+ len_str = soup_message_headers_get (header_table, "Content-Length"); -+#else - GSList *val; -- - val = g_hash_table_lookup (header_table, "Transfer-Encoding"); - if (val) { -- if (g_strcasecmp ((gchar *)val->data, "chunked") == 0) { -+ enc_str = ((const char *)val->data); -+ } -+ val = g_hash_table_lookup (header_table, "Content-Length"); -+ if (val) { -+ len_str = ((const char *)val->data); -+ } -+#endif -+ -+ if (enc_str) { -+ if (g_strcasecmp (enc_str, "chunked") == 0) { - src->chunked = TRUE; - } else { - GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL), -- ("Unknown HTTP transfer encoding \"%s\"", val->data)); -+ ("Unknown HTTP transfer encoding \"%s\"", enc_str)); - } - } else { - src->chunked = FALSE; -- val = g_hash_table_lookup (header_table, "Content-Length"); -- if (val) { -+ if (len_str) { - char *e; -- src->size = strtoul ((char *)val->data, &e, 10); -- if (e == val->data) { -+ src->size = strtoul (len_str, &e, 10); -+ if (e == len_str) { - GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL), -- ("Couldn't read HTTP content length \"%s\"", val->data)); -+ ("Couldn't read HTTP content length \"%s\"", len_str)); - ok = FALSE; - } - } else { -@@ -609,8 +636,13 @@ - ok = FALSE; - } - g_free (http_status_phrase); -+ -+#if defined(HAVE_LIBSOUP_2_4) -+ soup_message_headers_free (header_table); -+#else - soup_message_clear_headers (header_table); - g_hash_table_destroy (header_table); -+#endif - - end_headers += 4; - diff --git a/sources b/sources index 0c59fa6..f6259df 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -240f5c9c7986d9feb9d4dfe4f795f1b9 rhythmbox-0.11.4.tar.bz2 +967440dd984ec724e7e7992d5bd57bbd rhythmbox-0.11.5.tar.bz2 diff --git a/x-content.patch b/x-content.patch deleted file mode 100644 index c3193e5..0000000 --- a/x-content.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff -up rhythmbox-0.11.4/data/rhythmbox.desktop.in.in.x-content rhythmbox-0.11.4/data/rhythmbox.desktop.in.in ---- rhythmbox-0.11.4/data/rhythmbox.desktop.in.in.x-content 2008-01-18 00:33:33.000000000 -0500 -+++ rhythmbox-0.11.4/data/rhythmbox.desktop.in.in 2008-01-18 00:34:27.000000000 -0500 -@@ -8,7 +8,7 @@ Type=Application - Icon=rhythmbox - X-GNOME-DocPath=rhythmbox/rhythmbox.xml - Categories=GNOME;GTK;AudioVideo; --MimeType=application/x-ogg;application/ogg;audio/x-scpls;audio/x-mp3;audio/x-mpeg;audio/mpeg;audio/x-mpegurl;application/x-flac; -+MimeType=application/x-ogg;application/ogg;audio/x-scpls;audio/x-mp3;audio/x-mpeg;audio/mpeg;audio/x-mpegurl;application/x-flac;x-content/audio-cdda;x-content/audio-dvd;x-content/audio-player; - StartupNotify=true - X-GNOME-Bugzilla-Bugzilla=GNOME - X-GNOME-Bugzilla-Product=rhythmbox