From cf56c702b958e6d941d71807d2e457cb760cd414 Mon Sep 17 00:00:00 2001 From: Deji Akingunola Date: Feb 21 2014 05:52:00 +0000 Subject: Update to 3.3.2 --- diff --git a/.gitignore b/.gitignore index 2a19c8e..974efc6 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ exaile-0.3.2.0.tar.gz /exaile-0.3.2.2.tar.gz /exaile-3.3.0.tar.gz /exaile-3.3.1.tar.gz +/exaile-3.3.2.tar.gz diff --git a/exaile-3.3.1-udisks.patch b/exaile-3.3.1-udisks.patch deleted file mode 100644 index ef808a7..0000000 --- a/exaile-3.3.1-udisks.patch +++ /dev/null @@ -1,223 +0,0 @@ ---- plugins/cd/__init__.py 2011-07-18 20:32:50.690850000 -0400 -+++ plugins/cd/__init__.py.new 2011-08-31 13:41:50.277924078 -0400 -@@ -27,12 +27,12 @@ - - from xl.nls import gettext as _ - from xl import providers, event --from xl.hal import Handler -+from xl.hal import Handler, UDisksProvider - from xl.devices import Device - import logging - logger = logging.getLogger(__name__) - --PROVIDER = None -+PROVIDER = PROVIDER_UDISKS = None - - import dbus, threading, os, struct - from fcntl import ioctl -@@ -61,15 +61,17 @@ - CDROM_DATA_TRACK = 0x04 - - def enable(exaile): -- global PROVIDER -- PROVIDER = CDHandler() -- providers.register("hal", PROVIDER) -- -+ global PROVIDER, PROVIDER_UDISKS -+ #~ PROVIDER = CDHandler() -+ #~ providers.register("hal", PROVIDER) -+ PROVIDER_UDISKS = UDisksCdProvider() -+ providers.register("udisks", PROVIDER_UDISKS) - - def disable(exaile): -- global PROVIDER -+ global PROVIDER, PROVIDER_UDISKS - providers.unregister("hal", PROVIDER) -- PROVIDER = None -+ providers.unregister("udisks", PROVIDER_UDISKS) -+ PROVIDER = PROVIDER_UDISKS = None - - class CDTocParser(object): - #based on code from http://carey.geek.nz/code/python-cdrom/cdtoc.py -@@ -244,6 +246,24 @@ - - return cddev - -+class UDisksCdProvider(UDisksProvider): -+ PRIORITY = UDisksProvider.NORMAL -+ -+ name = 'cd' -+ -+ def get_priority(self, obj): -+ props = dbus.Interface(obj, 'org.freedesktop.DBus.Properties') -+ iface = 'org.freedesktop.UDisks.Device' -+ # XXX: We use the number of audio tracks to identify audio CDs. -+ # There may be a better way.... -+ compat = props.Get(iface, 'DriveMediaCompatibility') -+ return self.PRIORITY if 'optical_cd' in compat else None -+ -+ def create_device(self, obj): -+ props = dbus.Interface(obj, 'org.freedesktop.DBus.Properties') -+ iface = 'org.freedesktop.UDisks.Device' -+ return CDDevice(dev=props.Get(iface, 'DeviceFile')) -+ - - # vim: et sts=4 sw=4 - ---- xl/main.py 2012-09-09 15:07:41.000000000 -0400 -+++ xl/main.py.new 2012-09-24 11:26:49.132168406 -0400 -@@ -254,6 +254,8 @@ - # Initialize HAL - if self.options.Hal: - from xl import hal -+ self.udisks = hal.UDisks(self.devices) -+ self.udisks.connect() - self.hal = hal.HAL(self.devices) - self.hal.connect() - else: ---- xl/hal.py 2011-07-18 20:32:50.690850000 -0400 -+++ xl/hal.py.new 2011-08-31 13:49:02.489520690 -0400 -@@ -24,7 +24,7 @@ - # do so. If you do not wish to do so, delete this exception statement - # from your version. - --import logging -+import logging, threading, time - import dbus - - from xl import common, providers, event, devices, settings -@@ -32,6 +32,120 @@ - - logger = logging.getLogger(__name__) - -+class UDisks(providers.ProviderHandler): -+ """Provides support for UDisks devices. -+ -+ If the D-Bus connection fails, this object will grow a "failed" attribute -+ with True as the value. Plugins should check for this attribute when -+ registering if they want to provide HAL fallback. FIXME: There's a race -+ condition here. -+ """ -+ -+ # States: start -> init -> addremove <-> listening -> end. -+ # The addremove state acts as a lock against concurrent changes. -+ -+ def __init__(self, devicemanager): -+ self._lock = lock = threading.Lock() -+ self._state = 'init' -+ -+ providers.ProviderHandler.__init__(self, 'udisks') -+ self.devicemanager = devicemanager -+ -+ self.bus = self.obj = self.iface = None -+ self.devices = {} -+ self.providers = {} -+ -+ @common.threaded -+ def connect(self): -+ assert self._state == 'init' -+ logger.debug("Connecting to UDisks") -+ try: -+ self.bus = bus = dbus.SystemBus() -+ self.obj = obj = bus.get_object('org.freedesktop.UDisks', '/org/freedesktop/UDisks') -+ self.iface = iface = dbus.Interface(obj, 'org.freedesktop.UDisks') -+ iface.connect_to_signal('DeviceAdded', self._device_added, path_keyword='path') -+ iface.connect_to_signal('DeviceRemoved', self._device_removed, path_keyword='path') -+ logger.info("Connected to UDisks") -+ event.log_event("hal_connected", self, None) -+ except Exception: -+ logger.warning("Failed to connect to UDisks, " \ -+ "autodetection of devices will be disabled.") -+ self._state = 'listening' -+ self.failed = True -+ return -+ self._state = 'addremove' -+ self._add_all() -+ self._state = 'listening' -+ -+ def _add_all(self): -+ assert self._state == 'addremove' -+ for path in self.iface.EnumerateDevices(): -+ self._add_path(path) -+ -+ def _add_path(self, path): -+ assert self._state == 'addremove' -+ obj = self.bus.get_object('org.freedesktop.UDisks', path) -+ old, new = self._get_provider_for(obj) -+ if new is not old: -+ if old[0]: -+ self.devicemanager.remove_device(self.devices[path]) -+ device = new[0].create_device(obj) -+ device.autoconnect() -+ self.devicemanager.add_device(device) -+ self.providers[path] = new -+ self.devices[path] = device -+ -+ def _get_provider_for(self, obj): -+ """Return (old_provider, old_priority), (new_provider, new_priority)""" -+ assert self._state == 'addremove' -+ path = obj.object_path -+ highest = old = self.providers.get(path, (None, -1)) -+ for provider in self.get_providers(): -+ priority = provider.get_priority(obj) -+ if priority is not None and priority > highest[1]: -+ highest = (provider, priority) -+ return old, highest -+ -+ def _remove_path(self, path): -+ assert self._state == 'addremove' -+ self.devicemanager.remove_device(self.devices[path]) -+ del self.devices[path] -+ -+ def _device_added(self, path): -+ import pdb; pdb.set_trace() -+ self._addremove() -+ self._add_path(path) -+ self._state = 'listening' -+ -+ def _device_removed(self, path): -+ self._addremove() -+ try: -+ self._remove_path(path) -+ except KeyError: # Not ours -+ pass -+ self._state = 'listening' -+ -+ def on_provider_added(self, provider): -+ self._addremove() -+ self._connect_all() -+ self._state = 'listening' -+ -+ def on_provider_removed(self, provider): -+ self._addremove() -+ for path, provider_ in self.providers.iteritems(): -+ if provider_ is provider: -+ self._remove_path(path) -+ self._state = 'listening' -+ -+ def _addremove(self): -+ """Helper to transition safely to the addremove state""" -+ while True: -+ with self._lock: -+ if self._state == 'listening': -+ self._state = 'addremove' -+ break -+ time.sleep(1) -+ - class HAL(providers.ProviderHandler): - """ - HAL interface -@@ -144,6 +258,13 @@ - def device_from_udi(self, hal, udi): - pass - -+class UDisksProvider: -+ VERY_LOW, LOW, NORMAL, HIGH, VERY_HIGH = range(0, 101, 25) -+ def get_priority(self, obj): -+ pass # return: int [0..100] or None -+ def get_device(self, obj): -+ pass # return: xl.devices.Device -+ - - # vim: et sts=4 sw=4 - diff --git a/exaile-3.3.2-udisks.patch b/exaile-3.3.2-udisks.patch new file mode 100644 index 0000000..303ff51 --- /dev/null +++ b/exaile-3.3.2-udisks.patch @@ -0,0 +1,343 @@ +=== modified file 'plugins/cd/__init__.py' +--- plugins/cd/__init__.py 2012-10-03 04:43:15 +0000 ++++ plugins/cd/__init__.py 2012-11-09 04:25:00 +0000 +@@ -27,12 +27,12 @@ + + from xl.nls import gettext as _ + from xl import providers, event +-from xl.hal import Handler ++from xl.hal import Handler, UDisksProvider + from xl.devices import Device + import logging + logger = logging.getLogger(__name__) + +-PROVIDER = None ++PROVIDER = PROVIDER_UDISKS = None + + import dbus, threading, os, struct + from fcntl import ioctl +@@ -61,15 +61,17 @@ + CDROM_DATA_TRACK = 0x04 + + def enable(exaile): +- global PROVIDER +- PROVIDER = CDHandler() +- providers.register("hal", PROVIDER) +- ++ global PROVIDER, PROVIDER_UDISKS ++ #~ PROVIDER = CDHandler() ++ #~ providers.register("hal", PROVIDER) ++ PROVIDER_UDISKS = UDisksCdProvider() ++ providers.register("udisks", PROVIDER_UDISKS) + + def disable(exaile): +- global PROVIDER ++ global PROVIDER, PROVIDER_UDISKS + providers.unregister("hal", PROVIDER) +- PROVIDER = None ++ providers.unregister("udisks", PROVIDER_UDISKS) ++ PROVIDER = PROVIDER_UDISKS = None + + class CDTocParser(object): + #based on code from http://carey.geek.nz/code/python-cdrom/cdtoc.py +@@ -186,7 +188,7 @@ + tr.set_tag_raw('genre', + info['DGENRE']) + +- self._set_name(title[1].decode('iso-8859-15', 'replace')) ++ self.name = title[1].decode('iso-8859-15', 'replace') + event.log_event('cddb_info_retrieved', self, True) + + class CDDevice(Device): +@@ -244,8 +246,25 @@ + + return cddev + ++class UDisksCdProvider(UDisksProvider): ++ name = 'cd' ++ PRIORITY = UDisksProvider.NORMAL ++ ++ def get_priority(self, obj): ++ props = dbus.Interface(obj, 'org.freedesktop.DBus.Properties') ++ iface = 'org.freedesktop.UDisks.Device' ++ # DeviceChanged is called before and after tracks are read. We only want ++ # the second case, so use number of audio tracks to identify supported ++ # media. As a bonus, this means we never have to care about the type of ++ # disc (CD, DVD, etc.). ++ ntracks = props.Get(iface, 'OpticalDiscNumAudioTracks') ++ return self.PRIORITY if ntracks > 0 else None ++ ++ def get_device(self, obj): ++ # TODO: If this is the same disc, return old device object. ++ props = dbus.Interface(obj, 'org.freedesktop.DBus.Properties') ++ iface = 'org.freedesktop.UDisks.Device' ++ return CDDevice(dev=str(props.Get(iface, 'DeviceFile'))) ++ + + # vim: et sts=4 sw=4 +- +- +- + +=== modified file 'xl/hal.py' +--- xl/hal.py 2012-10-03 04:43:15 +0000 ++++ xl/hal.py 2012-11-09 04:25:00 +0000 +@@ -24,7 +24,7 @@ + # do so. If you do not wish to do so, delete this exception statement + # from your version. + +-import logging ++import logging, threading, time + import dbus + + from xl import common, providers, event, devices, settings +@@ -32,6 +32,183 @@ + + logger = logging.getLogger(__name__) + ++class UDisks(providers.ProviderHandler): ++ """ ++ Provides support for UDisks devices. ++ ++ If the D-Bus connection fails, this object will grow a "failed" ++ attribute with True as the value. Plugins should check for this when ++ registering if they want to provide HAL fallback. FIXME: There's a race ++ condition here. ++ """ ++ ++ # States: start -> init -> addremove <-> listening -> end. ++ # The addremove state acts as a lock against concurrent changes. ++ ++ def __init__(self, devicemanager): ++ self._lock = lock = threading.Lock() ++ self._state = 'init' ++ logger.debug("UDisks: state = init") ++ ++ providers.ProviderHandler.__init__(self, 'udisks') ++ self.devicemanager = devicemanager ++ ++ self.bus = self.obj = self.iface = None ++ self.devices = {} ++ self.providers = {} ++ ++ #~ @common.threaded ++ def connect(self): ++ assert self._state == 'init' ++ logger.debug("Connecting to UDisks") ++ try: ++ self.bus = bus = dbus.SystemBus() ++ self.obj = obj = bus.get_object('org.freedesktop.UDisks', '/org/freedesktop/UDisks') ++ self.iface = iface = dbus.Interface(obj, 'org.freedesktop.UDisks') ++ iface.connect_to_signal('DeviceAdded', self._udisks_device_added) ++ iface.connect_to_signal('DeviceRemoved', self._udisks_device_removed) ++ iface.connect_to_signal('DeviceChanged', self._udisks_device_added) ++ logger.info("Connected to UDisks") ++ event.log_event("hal_connected", self, None) ++ except Exception: ++ logger.warning("Failed to connect to UDisks, " \ ++ "autodetection of devices will be disabled.") ++ common.log_exception() ++ self._state = 'listening' ++ logger.debug("UDisks: state = listening") ++ self.failed = True ++ return ++ self._state = 'addremove' ++ logger.debug("UDisks: state = addremove") ++ self._add_all() ++ self._state = 'listening' ++ logger.debug("UDisks: state = listening") ++ ++ def _add_all(self): ++ assert self._state == 'addremove' ++ for path in self.iface.EnumerateDevices(): ++ self._add_device(path) ++ ++ def _add_device(self, path=None, obj=None): ++ """ ++ Call with either path or obj (obj gets priority). Not thread-safe. ++ """ ++ assert self._state == 'addremove' ++ if obj is None: ++ obj = self.bus.get_object('org.freedesktop.UDisks', path) ++ ++ # In the following code, `old` and `new` are providers, while ++ # `self.devices[path]` and `device` are old/new devices. There are ++ # several possible code paths that should be correctly handled: ++ # - No old nor new provider for this path. ++ # - Provider changes (nothing to something, something to nothing, ++ # something to something else); obviously device changes as well. ++ # - Provider stays the same, but device changes (i.e. instant media- ++ # swapping; not sure it can happen). ++ # - Provider and device stay the same. ++ old, new = self._get_provider_for(obj) ++ if new is None: ++ if old is not None: ++ self.devicemanager.remove_device(self.devices[path]) ++ del self.devices[path] ++ return ++ device = new.get_device(obj) ++ if new is old and device is self.devices[path]: ++ return # Exactly the same device ++ if old is not None: ++ self.devicemanager.remove_device(self.devices[path]) ++ if new is None: ++ return ++ try: ++ device.autoconnect() ++ except: ++ logger.exception("Failed autoconnecting device " + str(device)) ++ else: ++ self.devicemanager.add_device(device) ++ self.providers[path] = new ++ self.devices[path] = device ++ ++ def _get_provider_for(self, obj): ++ """ ++ Return (old_provider, old_priority), (new_provider, new_priority). ++ Not thread-safe. ++ """ ++ assert self._state == 'addremove' ++ highest_prio = -1 ++ highest = None ++ old = self.providers.get(obj.object_path) ++ for provider in self.get_providers(): ++ priority = provider.get_priority(obj) ++ if priority is None: continue ++ # Find highest priority, preferring old provider. ++ if priority > highest_prio or \ ++ (priority == highest_prio and provider is old): ++ highest_prio = priority ++ highest = provider ++ return old, highest ++ ++ def _remove_device(self, path): ++ assert self._state == 'addremove' ++ try: ++ self.devicemanager.remove_device(self.devices[path]) ++ del self.devices[path] ++ except KeyError: ++ logger.warning("UDisks: Can't remove device (not found): " + path) ++ ++ def _udisks_device_added(self, path): ++ logger.debug("UDisks: Device added: " + str(path)) ++ if self._addremove(): ++ self._add_device(path) ++ self._state = 'listening' ++ logger.debug("UDisks: state = listening (_device_added)") ++ ++ def _udisks_device_removed(self, path): ++ if self._addremove(): ++ try: ++ self._remove_device(path) ++ logger.debug("UDisks: Device removed: " + str(path)) ++ except KeyError: # Not ours ++ pass ++ self._state = 'listening' ++ logger.debug("UDisks: state = listening") ++ ++ # FIXME: Handle provider add/remove (following code unused & untested). ++ ++ def on_provider_added(self, provider): ++ if self._addremove(): ++ self._connect_all() ++ self._state = 'listening' ++ logger.debug("UDisks: state = listening") ++ ++ def on_provider_removed(self, provider): ++ if self._addremove(): ++ for path, provider_ in self.providers.iteritems(): ++ if provider_ is provider: ++ self._remove_device(path) ++ self._state = 'listening' ++ logger.debug("UDisks: state = listening") ++ ++ def _addremove(self): ++ """ ++ Helper to transition safely from listening to addremove state. ++ ++ Returns whether the transition happens. ++ """ ++ i = 0 ++ while True: ++ with self._lock: ++ if self._state == 'listening': ++ self._state = 'addremove' ++ logger.debug("UDisks: state = addremove") ++ return True ++ # If active state is init, we sleep and try again a few times. ++ # TODO: Whose thread is this we are blocking? ++ if i == 5: ++ logger.error("UDisks: Failed to acquire lock. Ignoring device event.") ++ return False ++ i += 1 ++ time.sleep(1) ++ + class HAL(providers.ProviderHandler): + """ + HAL interface +@@ -144,6 +321,12 @@ + def device_from_udi(self, hal, udi): + pass + ++class UDisksProvider: ++ VERY_LOW, LOW, NORMAL, HIGH, VERY_HIGH = range(0, 101, 25) ++ def get_priority(self, obj): ++ pass # return: int [0..100] or None ++ def get_device(self, obj): ++ pass # return: xl.devices.Device ++ + + # vim: et sts=4 sw=4 +- + +=== modified file 'xl/main.py' +--- xl/main.py 2012-10-24 04:33:53 +0000 ++++ xl/main.py 2012-11-09 04:25:00 +0000 +@@ -254,6 +254,8 @@ + # Initialize HAL + if self.options.Hal: + from xl import hal ++ self.udisks = hal.UDisks(self.devices) ++ self.udisks.connect() + self.hal = hal.HAL(self.devices) + self.hal.connect() + else: + +=== modified file 'xl/trax/util.py' +--- xl/trax/util.py 2012-10-03 04:43:15 +0000 ++++ xl/trax/util.py 2012-11-09 04:25:00 +0000 +@@ -65,14 +65,17 @@ + tracks = [] + + gloc = gio.File(uri) ++ + # don't do advanced checking on streaming-type uris as it can fail or + # otherwise be terribly slow. + # TODO: move uri definition somewhere more common for easy reuse? +- + if gloc.get_uri_scheme() in ('http', 'mms', 'cdda'): + return [Track(uri)] + +- file_type = gloc.query_info("standard::type").get_file_type() ++ try: ++ file_type = gloc.query_info("standard::type").get_file_type() ++ except gio.Error: # E.g. cdda ++ file_type = None + if file_type == gio.FILE_TYPE_DIRECTORY: + # TODO: refactor Library so we dont need the collection obj + from xl.collection import Library, Collection + +=== modified file 'xlgui/panel/flatplaylist.py' +--- xlgui/panel/flatplaylist.py 2012-10-03 04:43:15 +0000 ++++ xlgui/panel/flatplaylist.py 2012-11-09 04:25:00 +0000 +@@ -121,8 +121,7 @@ + def set_playlist(self, playlist): + self.model.clear() + +- tracks = [track for track in playlist] +- self.tracks = tracks ++ self.tracks = tracks = list(playlist) + for i, track in enumerate(tracks): + self.model.append([i + 1, track.get_tag_display("title"), track]) + + diff --git a/exaile.spec b/exaile.spec index 2fec552..c8eac3d 100644 --- a/exaile.spec +++ b/exaile.spec @@ -1,13 +1,13 @@ Summary: A music player Name: exaile -Version: 3.3.1 -Release: 4%{?dist} +Version: 3.3.2 +Release: 1%{?dist} Group: Applications/Multimedia License: GPLv2+ URL: http://www.exaile.org -Source0: http://launchpad.net/exaile/3.3.x/3.3.1/+download/exaile-%{version}.tar.gz +Source0: http://launchpad.net/exaile/3.3.x/3.3.2/+download/exaile-%{version}.tar.gz Patch0: exaile-3.3.1-makefile.patch -Patch1: exaile-3.3.1-udisks.patch +Patch1: exaile-3.3.2-udisks.patch BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) BuildRequires: pygobject2-devel BuildRequires: desktop-file-utils @@ -70,6 +70,9 @@ rm -rf %{buildroot} %{_mandir}/man1/exaile*.* %changelog +* Fri Feb 21 2014 Deji Akingunola - 3.3.2-1 +- Update to 3.3.2 + * Sat Aug 03 2013 Fedora Release Engineering - 3.3.1-4 - Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild diff --git a/sources b/sources index 21b30fd..8f007e3 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -cd7ea267a9d53cdd5231912f19ae03f8 exaile-3.3.1.tar.gz +117994e96065b4f767ac552c80808040 exaile-3.3.2.tar.gz