示例#1
0
    def __init__(self, bus):
        def player_message_cb(bus, message):
            if gst.MESSAGE_EOS == message.type:
                self.__player.set_state(gst.STATE_NULL)
                return True

            if gst.MESSAGE_ERROR == message.type:
                print message.structure and message.structure.to_string() or ''
                self.__player.set_state(gst.STATE_NULL)
                return True

            if gst.MESSAGE_STATE_CHANGED == message.type:
                if message.src == self.__player:
                    self.StateChanged(*self.GetState())
                    self.__favorites.set_state(*self.GetState())

                return True

            if gst.MESSAGE_TAG == message.type:
                valid_types = float, int, str, unicode

                tags = [(k, v) for k, v in dict(message.structure).items()
                        if isinstance(v, valid_types)]

                self.StreamTagsChanged(dict(tags))

                return True

            return True

        self.__data_stage = 0
        self.__player = Player()
        self.__player.get_bus().add_watch(player_message_cb)
        self.__httplib = Http(cache=get_cache_filename())
        self.__favorites = Favorites()
        self.__stations = list()
        self.__stream_tags = dict()

        proxy = SessionBus().get_object('org.freedesktop.Notifications',
                                        '/org/freedesktop/Notifications')
        self.__notifications = Interface(proxy,
                                         'org.freedesktop.Notifications')
        self.__notify_id = 0

        name = BusName(Service.name, bus)
        super(Service, self).__init__(bus, '/', name)
        self.__loop = MainLoop(None, True)

        Thread(target=self.__load).start()
示例#2
0
class AnonymousConnection(object):

    def __init__(self, g_file):
        self._g_file = None
        self._loop = MainLoop()

        # Already mounted ?
        if g_file.query_exists():
            self._g_file = g_file
        else:
            mount_operation = MountOperation()
            mount_operation.connect('ask-password', self._ask_password)
            g_file.mount_enclosing_volume(mount_operation, self._mount_end)

            # Wait
            self._loop.run()


#    def __del__(self):
#        # Umount the archive
#        self.unmount()


    ############################
    # Private API
    ############################
    def _ask_password(self, op, message, default_user, default_domain, flags):
        if not flags & ASK_PASSWORD_ANONYMOUS_SUPPORTED:
            raise IOError, 'This server doest not support anonymous connection'

        op.set_anonymous(True)
        op.reply(MOUNT_OPERATION_HANDLED)


    def _mount_end(self, g_file, result):
        if g_file.mount_enclosing_volume_finish(result):
            self._g_file = g_file
        self._loop.quit()


    def _unmount_end(self, g_mount, result):
        g_mount.unmount_finish(result)
        self._g_file = None
        self._loop.quit()


    ############################
    # Public API
    ############################
    def unmount(self):
        # Unmount the archive
        if self._g_file is not None:
            g_mount = self._g_file.find_enclosing_mount()
            g_mount.unmount(self._unmount_end)

        # Wait
        self._loop.run()
示例#3
0
文件: vfs.py 项目: Nabellaleen/itools
class Archive(Folder):

    def __init__(self, g_file):
        self._folder = None
        self._loop = MainLoop()

        # Make the archive uri
        uri = g_file.get_uri()
        uri = 'archive://' + quote(uri, '')

        # Mount the archive if needed
        g_file = File(uri)
        # Already mounted ?
        if g_file.query_exists():
            self._folder = g_file
        else:
            mount_operation = MountOperation()
            mount_operation.set_anonymous(True)
            g_file.mount_enclosing_volume(mount_operation, self._mount_end)

            # Wait
            self._loop.run()


#    def __del__(self):
#        # Umount the archive
#        self.unmount()


    ############################
    # Private API
    ############################
    def _mount_end(self, g_file, result):
        if g_file.mount_enclosing_volume_finish(result):
            self._folder = g_file
        self._loop.quit()


    def _unmount_end(self, g_mount, result):
        g_mount.unmount_finish(result)
        self._folder = None
        self._loop.quit()


    ############################
    # Public API
    ############################
    def unmount(self):
        # Unmount the archive
        if self._folder is not None:
            g_mount = self._folder.find_enclosing_mount()
            g_mount.unmount(self._unmount_end)

        # Wait
        self._loop.run()
示例#4
0
    def wait(self, stage=STATE_CHANNELS_LOADED):
        loop = MainLoop(None, True)

        def data_ready_cb(new_stage):
            if new_stage >= stage:
                loop.quit()

        self.__service.connect_to_signal('DataReady', data_ready_cb)

        if self.__service.GetDataStage() >= stage:
            loop.quit()

        progress_id = 0

        if loop.is_running():
            if sys.stdout.isatty():
                progress = ['-\r', '\\\r', '|\r', '/\r']

                def progress_cb():
                    c = progress.pop(0)
                    sys.stdout.write(c)
                    sys.stdout.flush()
                    progress.append(c)
                    return True

                progress_id = timeout_add(250, progress_cb)
                sys.stdout.write('  loading...\r')

            loop.run()

        if progress_id:
            source_remove(progress_id)
            sys.stdout.write('\r\033[K')
            sys.stdout.flush()
示例#5
0
    def __init__(self, g_file):
        self._folder = None
        self._loop = MainLoop()

        # Make the archive uri
        uri = g_file.get_uri()
        uri = 'archive://' + quote(uri, '')

        # Mount the archive if needed
        g_file = File(uri)
        # Already mounted ?
        if g_file.query_exists():
            self._folder = g_file
        else:
            mount_operation = MountOperation()
            mount_operation.set_anonymous(True)
            g_file.mount_enclosing_volume(mount_operation, self._mount_end)

            # Wait
            self._loop.run()
示例#6
0
class Archive(Folder):
    def __init__(self, g_file):
        self._folder = None
        self._loop = MainLoop()

        # Make the archive uri
        uri = g_file.get_uri()
        uri = 'archive://' + quote(uri, '')

        # Mount the archive if needed
        g_file = File(uri)
        # Already mounted ?
        if g_file.query_exists():
            self._folder = g_file
        else:
            mount_operation = MountOperation()
            mount_operation.set_anonymous(True)
            g_file.mount_enclosing_volume(mount_operation, self._mount_end)

            # Wait
            self._loop.run()

#    def __del__(self):
#        # Umount the archive
#        self.unmount()

############################
# Private API
############################

    def _mount_end(self, g_file, result):
        if g_file.mount_enclosing_volume_finish(result):
            self._folder = g_file
        self._loop.quit()

    def _unmount_end(self, g_mount, result):
        g_mount.unmount_finish(result)
        self._folder = None
        self._loop.quit()

    ############################
    # Public API
    ############################
    def unmount(self):
        # Unmount the archive
        if self._folder is not None:
            g_mount = self._folder.find_enclosing_mount()
            g_mount.unmount(self._unmount_end)

        # Wait
        self._loop.run()
示例#7
0
    def wait(self, stage=STATE_CHANNELS_LOADED):
        loop = MainLoop(None, True)

        def data_ready_cb(new_stage):
            if new_stage >= stage:
                loop.quit()

        self.__service.connect_to_signal('DataReady', data_ready_cb)

        if self.__service.GetDataStage() >= stage:
            loop.quit()

        progress_id = 0

        if loop.is_running():
            if sys.stdout.isatty():
                progress = ['-\r', '\\\r', '|\r', '/\r']

                def progress_cb():
                    c = progress.pop(0)
                    sys.stdout.write(c)
                    sys.stdout.flush()
                    progress.append(c)
                    return True

                progress_id = timeout_add(250, progress_cb)
                sys.stdout.write('  loading...\r')

            loop.run()

        if progress_id:
            source_remove(progress_id)
            sys.stdout.write('\r\033[K')
            sys.stdout.flush()
示例#8
0
    def __init__(self, g_file):
        self._g_file = None
        self._loop = MainLoop()

        # Already mounted ?
        if g_file.query_exists():
            self._g_file = g_file
        else:
            mount_operation = MountOperation()
            mount_operation.connect('ask-password', self._ask_password)
            g_file.mount_enclosing_volume(mount_operation, self._mount_end)

            # Wait
            self._loop.run()
示例#9
0
    def __init__(self, bus):
        def player_message_cb(bus, message):
            if gst.MESSAGE_EOS == message.type:
                self.__player.set_state(gst.STATE_NULL)
                return True

            if gst.MESSAGE_ERROR == message.type:
                print message.structure and message.structure.to_string() or ''
                self.__player.set_state(gst.STATE_NULL)
                return True

            if gst.MESSAGE_STATE_CHANGED == message.type:
                if message.src == self.__player:
                    self.StateChanged(*self.GetState())
                    self.__favorites.set_state(*self.GetState())

                return True

            if gst.MESSAGE_TAG == message.type:
                valid_types = float, int, str, unicode

                tags = [(k, v) for k, v
                        in dict(message.structure).items()
                        if isinstance(v, valid_types)]

                self.StreamTagsChanged(dict(tags))

                return True

            return True

        self.__data_stage = 0
        self.__player = Player()
        self.__player.get_bus().add_watch(player_message_cb)
        self.__httplib = Http(cache=get_cache_filename())
        self.__favorites = Favorites()
        self.__stations = list()
        self.__stream_tags = dict()

        proxy = SessionBus().get_object('org.freedesktop.Notifications', '/org/freedesktop/Notifications')
        self.__notifications = Interface(proxy, 'org.freedesktop.Notifications')
        self.__notify_id = 0

        name = BusName(Service.name, bus)
        super(Service, self).__init__(bus, '/', name)
        self.__loop = MainLoop(None, True)

        Thread(target=self.__load).start()
示例#10
0
文件: vfs.py 项目: Nabellaleen/itools
    def __init__(self, g_file):
        self._folder = None
        self._loop = MainLoop()

        # Make the archive uri
        uri = g_file.get_uri()
        uri = 'archive://' + quote(uri, '')

        # Mount the archive if needed
        g_file = File(uri)
        # Already mounted ?
        if g_file.query_exists():
            self._folder = g_file
        else:
            mount_operation = MountOperation()
            mount_operation.set_anonymous(True)
            g_file.mount_enclosing_volume(mount_operation, self._mount_end)

            # Wait
            self._loop.run()
示例#11
0
class Service(Object):
    name = 'de.taschenorakel.webradio'
    interface = '%s.Service' % name

    def __init__(self, bus):
        def player_message_cb(bus, message):
            if gst.MESSAGE_EOS == message.type:
                self.__player.set_state(gst.STATE_NULL)
                return True

            if gst.MESSAGE_ERROR == message.type:
                print message.structure and message.structure.to_string() or ''
                self.__player.set_state(gst.STATE_NULL)
                return True

            if gst.MESSAGE_STATE_CHANGED == message.type:
                if message.src == self.__player:
                    self.StateChanged(*self.GetState())
                    self.__favorites.set_state(*self.GetState())

                return True

            if gst.MESSAGE_TAG == message.type:
                valid_types = float, int, str, unicode

                tags = [(k, v) for k, v
                        in dict(message.structure).items()
                        if isinstance(v, valid_types)]

                self.StreamTagsChanged(dict(tags))

                return True

            return True

        self.__data_stage = 0
        self.__player = Player()
        self.__player.get_bus().add_watch(player_message_cb)
        self.__httplib = Http(cache=get_cache_filename())
        self.__favorites = Favorites()
        self.__stations = list()
        self.__stream_tags = dict()

        proxy = SessionBus().get_object('org.freedesktop.Notifications', '/org/freedesktop/Notifications')
        self.__notifications = Interface(proxy, 'org.freedesktop.Notifications')
        self.__notify_id = 0

        name = BusName(Service.name, bus)
        super(Service, self).__init__(bus, '/', name)
        self.__loop = MainLoop(None, True)

        Thread(target=self.__load).start()

    def __fetch_from_cache(self, uri):
        print 'fetching from cache %s' % uri
        return self.__httplib.request(uri, headers={'cache-control': 'only-if-cached'})

    def __fetch_from_web(self, uri):
        print 'fetching from web %s' % uri
        return self.__httplib.request(uri)

    def __fetch(self, uri):
        response, content = self.__fetch_from_cache(uri)

        if 504 == response.status:
            response, content = self.__fetch_from_web(uri)

        return response, content

    def __notify(self, summary, body=None, id=0, icon='rhythmbox',
                 app_name='webradio', actions=None, hints=None, timeout=-1):
        return self.__notifications.Notify(
            app_name or '', int(id), icon or '', summary,
            body or '', actions or [], hints or {}, int(timeout))

    def __load(self):
        def load_station_details(station):
            response, content = self.__fetch(station.uri)

            if 200 == response.status:
                pattern = re.compile(r'href="([^"]+\.pls)"')

                for uri in pattern.findall(content):
                    if uri.startswith('/'):
                        uri = urljoin(station.uri, uri)
                    if station.accept_stream(uri):
                        pending_channels.append([station, uri])

                print '%d stations found...' % len(pending_channels)

            else:
                print 'Bad response: %s %s' % (response.reason,
                                               response.status)

        def find_config_file(basename):
            filename = get_config_filename(basename)

            if os.path.isfile(filename):
                return filename

            for libdir in sys.path:
                prefix = os.path.commonprefix([__file__, libdir])

                if not prefix or prefix != libdir:
                    continue

                libdir_parent, libdir_name = os.path.split(libdir)

                if 'site-packages' == libdir_name:
                    prefix = os.path.join(libdir_parent, '..', '..')
                    filename = os.path.join(prefix, 'share', 'webradio', basename)

                    if os.path.isfile(filename):
                        return filename

                for filename in [
                        os.path.join(libdir, 'data', basename),
                        os.path.join(libdir_parent, 'data', basename)]:
                    if os.path.isfile(filename):
                        return filename

            return None

        def load_station_list():
            filename = find_config_file('stations')

            if filename is None:
                raise RuntimeError, 'Cannot find station list'

            print 'reading stations from %r' % filename

            parser = SafeConfigParser()
            parser.read(filename)

            for station_id in parser.sections():
                uri = parser.get(station_id, 'uri')
                title = parser.get(station_id, 'title')
                stream_uri = parser.get(station_id, 'streams')
                station = Station(station_id, title, uri)

                if stream_uri:
                    station.stream_uri = stream_uri

                i = 1

                while True:
                    key = 'noise%d' % i
                    i += 1

                    if not parser.has_option(station_id, key):
                        break

                    noise = parser.get(station_id, key)
                    station.add_noise_filter(noise)
                for key in parser.options(station_id):
                    if key.startswith('alias.'):
                        name = key[len('alias.'):]
                        value = parser.get(station_id, key)
                        station.add_alias(name, value)
                        continue

                load_station_details(station)
                self.StationAdded(station)

        def load_streams(channel, content):
            parser = SafeConfigParser()
            parser.readfp(StringIO(content))

            playlist = dict(parser.items('playlist'))
            length = int(playlist['numberofentries'])

            for i in range(1, length + 1):
                uri = playlist['file%d' % i]
                title = playlist.get('title%d' % i)
                length = int(playlist.get('length%d' % i, -1))

                if title:
                    title = channel.station.filter_noise(title)

                stream = Stream(uri, title, length)
                channel.streams.append(stream)

        def load_pending_channels():
            for station, uri in pending_channels:
                response, content = self.__fetch(uri)

                if 200 == response.status:
                    channel = Channel(station, uri)
                    station.channels.append(channel)
                    load_streams(channel, content)
                    self.ChannelAdded(station.id, channel)

        pending_channels = []

        load_station_list()
        idle_add(self.DataReady, 1)

        load_pending_channels()
        idle_add(self.DataReady, 2)

    def run(self):
        try:
            self.__loop.run()

        except KeyboardInterrupt:
            self.__loop.quit()

    @method(dbus_interface=interface, utf8_strings=True,
            in_signature='', out_signature='a' + Station.dbus_signature)
    def GetStations(self):
        return self.__stations

    @method(dbus_interface=interface, utf8_strings=True,
            in_signature='', out_signature='a{sv}')
    def GetStreamTags(self):
        return self.__stream_tags

    @method(dbus_interface=interface, utf8_strings=True, in_signature='as',
            out_signature='a(s' + Channel.dbus_signature + ')')
    def Find(self, query):
        result = list()

        for station in self.__stations:
            for channel in station.channels:
                if channel.matches(query):
                    match = station.id, channel
                    result.append(match)

        return result

    @method(dbus_interface=interface, in_signature='s', out_signature='')
    def Play(self, uri):
        self.__player.set_state(gst.STATE_NULL)
        self.__player.uri = uri
        self.__player.set_state(gst.STATE_PLAYING)

    @method(dbus_interface=interface, in_signature='', out_signature='')
    def Pause(self):
        self.__player.set_state(gst.STATE_PAUSED)

    @method(dbus_interface=interface, in_signature='', out_signature='')
    def Resume(self):
        self.__player.set_state(gst.STATE_PLAYING)

    @method(dbus_interface=interface, in_signature='', out_signature='')
    def Quit(self):
        self.__loop.quit()

    @method(dbus_interface=interface, in_signature='', out_signature='i')
    def GetDataStage(self):
        return self.__data_stage

    @method(dbus_interface=interface, in_signature='', out_signature='bs')
    def GetState(self):
        playing = (gst.STATE_PLAYING == self.__player.get_state()[1])
        channel_uri = self.__player.uri or ''
        return playing, channel_uri

    @signal(dbus_interface=interface, signature='i')
    def DataReady(self, stage):
        self.__data_stage = stage

    @signal(dbus_interface=interface, signature=Station.dbus_signature)
    def StationAdded(self, station):
        self.__stations.append(station)

    @signal(dbus_interface=interface, signature='s' + Channel.dbus_signature)
    def ChannelAdded(self, station_id, channel):
        pass

    @signal(dbus_interface=interface, signature='bs')
    def StateChanged(self, playing, stream_uri):
        pass

    @signal(dbus_interface=interface, signature='a{sv}')
    def StreamTagsChanged(self, tags):
        self.__stream_tags.update(tags)

        summary = self.__stream_tags.get('title') or ''
        body = self.__stream_tags.get('organization') or ''
        self.__notify_id = self.__notify(summary, body, id=self.__notify_id)

    @method(dbus_interface=interface, in_signature='', out_signature='as')
    def GetTags(self):
        tags = dict()

        for s in self.__stations:
            for c in s.channels:
                for t in c.tags:
                    tags[t] = True

            tags[s.id] = True

        tags = list(tags)
        tags.sort()

        return tags

    @method(dbus_interface=interface, in_signature='', out_signature='as')
    def ListEqualizerProfiles(self):
        return self.__player.get_profile_names()

    @method(dbus_interface=interface, in_signature='', out_signature='s')
    def GetEqualizerProfile(self):
        return self.__player.profile

    @method(dbus_interface=interface, in_signature='s', out_signature='')
    def SetEqualizerProfile(self, profile_name):
        self.__player.profile = profile_name
示例#12
0
class Service(Object):
    name = 'de.taschenorakel.webradio'
    interface = '%s.Service' % name

    def __init__(self, bus):
        def player_message_cb(bus, message):
            if gst.MESSAGE_EOS == message.type:
                self.__player.set_state(gst.STATE_NULL)
                return True

            if gst.MESSAGE_ERROR == message.type:
                print message.structure and message.structure.to_string() or ''
                self.__player.set_state(gst.STATE_NULL)
                return True

            if gst.MESSAGE_STATE_CHANGED == message.type:
                if message.src == self.__player:
                    self.StateChanged(*self.GetState())
                    self.__favorites.set_state(*self.GetState())

                return True

            if gst.MESSAGE_TAG == message.type:
                valid_types = float, int, str, unicode

                tags = [(k, v) for k, v in dict(message.structure).items()
                        if isinstance(v, valid_types)]

                self.StreamTagsChanged(dict(tags))

                return True

            return True

        self.__data_stage = 0
        self.__player = Player()
        self.__player.get_bus().add_watch(player_message_cb)
        self.__httplib = Http(cache=get_cache_filename())
        self.__favorites = Favorites()
        self.__stations = list()
        self.__stream_tags = dict()

        proxy = SessionBus().get_object('org.freedesktop.Notifications',
                                        '/org/freedesktop/Notifications')
        self.__notifications = Interface(proxy,
                                         'org.freedesktop.Notifications')
        self.__notify_id = 0

        name = BusName(Service.name, bus)
        super(Service, self).__init__(bus, '/', name)
        self.__loop = MainLoop(None, True)

        Thread(target=self.__load).start()

    def __fetch_from_cache(self, uri):
        print 'fetching from cache %s' % uri
        return self.__httplib.request(
            uri, headers={'cache-control': 'only-if-cached'})

    def __fetch_from_web(self, uri):
        print 'fetching from web %s' % uri
        return self.__httplib.request(uri)

    def __fetch(self, uri):
        response, content = self.__fetch_from_cache(uri)

        if 504 == response.status:
            response, content = self.__fetch_from_web(uri)

        return response, content

    def __notify(self,
                 summary,
                 body=None,
                 id=0,
                 icon='rhythmbox',
                 app_name='webradio',
                 actions=None,
                 hints=None,
                 timeout=-1):
        return self.__notifications.Notify(app_name or '', int(id), icon or '',
                                           summary, body or '', actions or [],
                                           hints or {}, int(timeout))

    def __load(self):
        def load_station_details(station):
            response, content = self.__fetch(station.uri)

            if 200 == response.status:
                pattern = re.compile(r'href="([^"]+\.pls)"')

                for uri in pattern.findall(content):
                    if uri.startswith('/'):
                        uri = urljoin(station.uri, uri)
                    if station.accept_stream(uri):
                        pending_channels.append([station, uri])

                print '%d stations found...' % len(pending_channels)

            else:
                print 'Bad response: %s %s' % (response.reason,
                                               response.status)

        def find_config_file(basename):
            filename = get_config_filename(basename)

            if os.path.isfile(filename):
                return filename

            for libdir in sys.path:
                prefix = os.path.commonprefix([__file__, libdir])

                if not prefix or prefix != libdir:
                    continue

                libdir_parent, libdir_name = os.path.split(libdir)

                if 'site-packages' == libdir_name:
                    prefix = os.path.join(libdir_parent, '..', '..')
                    filename = os.path.join(prefix, 'share', 'webradio',
                                            basename)

                    if os.path.isfile(filename):
                        return filename

                for filename in [
                        os.path.join(libdir, 'data', basename),
                        os.path.join(libdir_parent, 'data', basename)
                ]:
                    if os.path.isfile(filename):
                        return filename

            return None

        def load_station_list():
            filename = find_config_file('stations')

            if filename is None:
                raise RuntimeError, 'Cannot find station list'

            print 'reading stations from %r' % filename

            parser = SafeConfigParser()
            parser.read(filename)

            for station_id in parser.sections():
                uri = parser.get(station_id, 'uri')
                title = parser.get(station_id, 'title')
                stream_uri = parser.get(station_id, 'streams')
                station = Station(station_id, title, uri)

                if stream_uri:
                    station.stream_uri = stream_uri

                i = 1

                while True:
                    key = 'noise%d' % i
                    i += 1

                    if not parser.has_option(station_id, key):
                        break

                    noise = parser.get(station_id, key)
                    station.add_noise_filter(noise)
                for key in parser.options(station_id):
                    if key.startswith('alias.'):
                        name = key[len('alias.'):]
                        value = parser.get(station_id, key)
                        station.add_alias(name, value)
                        continue

                load_station_details(station)
                self.StationAdded(station)

        def load_streams(channel, content):
            parser = SafeConfigParser()
            parser.readfp(StringIO(content))

            playlist = dict(parser.items('playlist'))
            length = int(playlist['numberofentries'])

            for i in range(1, length + 1):
                uri = playlist['file%d' % i]
                title = playlist.get('title%d' % i)
                length = int(playlist.get('length%d' % i, -1))

                if title:
                    title = channel.station.filter_noise(title)

                stream = Stream(uri, title, length)
                channel.streams.append(stream)

        def load_pending_channels():
            for station, uri in pending_channels:
                response, content = self.__fetch(uri)

                if 200 == response.status:
                    channel = Channel(station, uri)
                    station.channels.append(channel)
                    load_streams(channel, content)
                    self.ChannelAdded(station.id, channel)

        pending_channels = []

        load_station_list()
        idle_add(self.DataReady, 1)

        load_pending_channels()
        idle_add(self.DataReady, 2)

    def run(self):
        try:
            self.__loop.run()

        except KeyboardInterrupt:
            self.__loop.quit()

    @method(dbus_interface=interface,
            utf8_strings=True,
            in_signature='',
            out_signature='a' + Station.dbus_signature)
    def GetStations(self):
        return self.__stations

    @method(dbus_interface=interface,
            utf8_strings=True,
            in_signature='',
            out_signature='a{sv}')
    def GetStreamTags(self):
        return self.__stream_tags

    @method(dbus_interface=interface,
            utf8_strings=True,
            in_signature='as',
            out_signature='a(s' + Channel.dbus_signature + ')')
    def Find(self, query):
        result = list()

        for station in self.__stations:
            for channel in station.channels:
                if channel.matches(query):
                    match = station.id, channel
                    result.append(match)

        return result

    @method(dbus_interface=interface, in_signature='s', out_signature='')
    def Play(self, uri):
        self.__player.set_state(gst.STATE_NULL)
        self.__player.uri = uri
        self.__player.set_state(gst.STATE_PLAYING)

    @method(dbus_interface=interface, in_signature='', out_signature='')
    def Pause(self):
        self.__player.set_state(gst.STATE_PAUSED)

    @method(dbus_interface=interface, in_signature='', out_signature='')
    def Resume(self):
        self.__player.set_state(gst.STATE_PLAYING)

    @method(dbus_interface=interface, in_signature='', out_signature='')
    def Quit(self):
        self.__loop.quit()

    @method(dbus_interface=interface, in_signature='', out_signature='i')
    def GetDataStage(self):
        return self.__data_stage

    @method(dbus_interface=interface, in_signature='', out_signature='bs')
    def GetState(self):
        playing = (gst.STATE_PLAYING == self.__player.get_state()[1])
        channel_uri = self.__player.uri or ''
        return playing, channel_uri

    @signal(dbus_interface=interface, signature='i')
    def DataReady(self, stage):
        self.__data_stage = stage

    @signal(dbus_interface=interface, signature=Station.dbus_signature)
    def StationAdded(self, station):
        self.__stations.append(station)

    @signal(dbus_interface=interface, signature='s' + Channel.dbus_signature)
    def ChannelAdded(self, station_id, channel):
        pass

    @signal(dbus_interface=interface, signature='bs')
    def StateChanged(self, playing, stream_uri):
        pass

    @signal(dbus_interface=interface, signature='a{sv}')
    def StreamTagsChanged(self, tags):
        self.__stream_tags.update(tags)

        summary = self.__stream_tags.get('title') or ''
        body = self.__stream_tags.get('organization') or ''
        self.__notify_id = self.__notify(summary, body, id=self.__notify_id)

    @method(dbus_interface=interface, in_signature='', out_signature='as')
    def GetTags(self):
        tags = dict()

        for s in self.__stations:
            for c in s.channels:
                for t in c.tags:
                    tags[t] = True

            tags[s.id] = True

        tags = list(tags)
        tags.sort()

        return tags

    @method(dbus_interface=interface, in_signature='', out_signature='as')
    def ListEqualizerProfiles(self):
        return self.__player.get_profile_names()

    @method(dbus_interface=interface, in_signature='', out_signature='s')
    def GetEqualizerProfile(self):
        return self.__player.profile

    @method(dbus_interface=interface, in_signature='s', out_signature='')
    def SetEqualizerProfile(self, profile_name):
        self.__player.profile = profile_name