coherence.backends.tracker_storage — Cohen3 0.8.0 documentation

# Source code for coherence.backends.tracker_storage

    # Licensed under the MIT license
    # http://opensource.org/licenses/mit-license.php

    # Copyright 2008, Frank Scholz <coherence@beebits.net>

    import os.path

    import dbus
    import dbus.service
    from twisted.internet import defer
    from twisted.python import util

    from coherence.backend import BackendItem, BackendStore
    from coherence.upnp.core import DIDLLite

    ROOT_CONTAINER_ID = 0
    AUDIO_CONTAINER_ID = 100
    AUDIO_ALL_CONTAINER_ID = 101
    AUDIO_ARTIST_CONTAINER_ID = 102
    AUDIO_ALBUM_CONTAINER_ID = 103
    AUDIO_PLAYLIST_CONTAINER_ID = 104
    AUDIO_GENRE_CONTAINER_ID = 105

    VIDEO_CONTAINER_ID = 200
    VIDEO_ALL_CONTAINER_ID = 201

    IMAGE_CONTAINER_ID = 300
    IMAGE_ALL_CONTAINER_ID = 301

    BUS_NAME = 'org.freedesktop.Tracker'
    OBJECT_PATH = '/org/freedesktop/tracker'

    tracks_query = '''
    <rdfq:Condition>\
    <rdfq:equals>\
    <rdfq:Property name="Audio:Title" />\
    <rdf:String>*</rdf:String>\
    </rdfq:equals>\
    </rdfq:Condition>\
    '''

    video_query = '''
    <rdfq:Condition>\
    <rdfq:equals>\
    <rdfq:Property name="File:Name" />\
    <rdf:String>*</rdf:String>\
    </rdfq:equals>\
    </rdfq:Condition>\
    '''

    image_query = '''
    <rdfq:Condition>\
    <rdfq:equals>\
    <rdfq:Property name="File:Name" />\
    <rdf:String>*</rdf:String>\
    </rdfq:equals>\
    </rdfq:Condition>\
    '''

    class Container(BackendItem):
        logCategory = 'tracker_store'

        def __init__(self, id, parent_id, name, store=None, children_callback=None,
                     container_class=DIDLLite.Container):
            BackendItem.__init__(self)
            self.id = id
            self.parent_id = parent_id
            self.name = name
            self.mimetype = 'directory'
            self.item = container_class(id, parent_id, self.name)
            self.item.childCount = 0
            self.update_id = 0
            if children_callback is not None:
                self.children = children_callback
            else:
                self.children = util.OrderedDict()
            self.item.childCount = None  # self.get_child_count()

            if store is not None:
                self.get_url = lambda: store.urlbase + str(self.id)

        def add_child(self, child):
            id = child.id
            if isinstance(child.id, str):
                _, id = child.id.split('.')
            self.children[id] = child
            if self.item.childCount is not None:
                self.item.childCount += 1

        def get_children(self, start=0, end=0):
            self.info(f'container.get_children {start} {end}')

            if callable(self.children):
                return self.children(start, end - start)
            else:
                children = list(self.children.values())
            if end == 0:
                return children[start:]
            else:
                return children[start:end]

        def get_child_count(self):
            if self.item.childCount is not None:
                return self.item.childCount

            if callable(self.children):
                return len(self.children())
            else:
                return len(self.children)

        def get_item(self):
            return self.item

        def get_name(self):
            return self.name

        def get_id(self):
            return self.id

    class Artist(BackendItem):
        logCategory = 'tracker_store'

        def __init__(self, store, id, name):
            BackendItem.__init__(self)
            self.store = store
            self.id = f'artist.{int(id):d}'
            self.name = name
            self.children = {}
            self.sorted_children = None

        def add_child(self, child):
            _, id = child.id.split('.')
            self.children[id] = child

        def sort_children(self):
            if self.sorted_children is None:
                def childs_key_sort(x):
                    return self.children[x].name

                self.sorted_children = list(self.children.keys())
                self.sorted_children = sorted(self.sorted_children,
                                              key=childs_key_sort)
            return self.sorted_children

        def get_artist_all_tracks(self, start=0, request_count=0):
            children = []
            for album in self.sort_children():
                children += album.get_children()
            if request_count == 0:
                return children[start:]
            else:
                return children[start:request_count]

        def get_children(self, start=0, end=0):
            children = []
            for key in self.sort_children():
                children.append(self.children[key])

            if end == 0:
                return children[start:]
            else:
                return children[start:end]

        def get_child_count(self):
            return len(self.children)

        def get_item(self, parent_id=AUDIO_ARTIST_CONTAINER_ID):
            item = DIDLLite.MusicArtist(self.id, parent_id, self.name)
            return item

        def get_id(self):
            return self.id

        def get_name(self):
            return self.name

    class Album(BackendItem):
        logCategory = 'tracker_store'

        def __init__(self, store, id, title, artist):
            BackendItem.__init__(self)
            self.store = store
            self.id = f'album.{int(id):d}'
            self.name = str(title)
            self.artist = str(artist)
            self.cover = None
            self.children = {}
            self.sorted_children = None

        def add_child(self, child):
            _, id = child.id.split('.')
            self.children[id] = child

        def get_children(self, start=0, end=0):
            children = []
            if self.sorted_children is not None:
                for key in self.sorted_children:
                    children.append(self.children[key])
            else:
                def childs_key_sort(x):
                    return self.children[x].track_nr

                self.sorted_children = list(self.children.keys())
                self.sorted_children = \
                    sorted(self.sorted_children, key=childs_key_sort)
                for key in self.sorted_children:
                    children.append(self.children[key])

            if end == 0:
                return children[start:]
            else:
                return children[start:end]

        def get_child_count(self):
            return len(self.children)

        def get_item(self, parent_id=AUDIO_ALBUM_CONTAINER_ID):
            item = DIDLLite.MusicAlbum(self.id, parent_id, self.name)
            item.childCount = self.get_child_count()
            item.artist = self.artist
            item.albumArtURI = self.cover
            return item

        def get_id(self):
            return self.id

        def get_name(self):
            return self.name

        def get_cover(self):
            return self.cover

    class Track(BackendItem):
        logCategory = 'tracker_store'

        def __init__(self, store,
                     id, parent_id,
                     file, title,
                     artist, album, genre,
                     duration,
                     track_number,
                     size, mimetype):
            BackendItem.__init__(self)
            self.store = store
            self.id = 'song.%d' % int(id)
            self.parent_id = parent_id

            self.path = str(file)

            duration = str(duration).strip()
            duration = duration.split('.')[0]
            if len(duration) == 0:
                duration = 0
            seconds = int(duration)
            hours = seconds / 3600
            seconds = seconds - hours * 3600
            minutes = seconds / 60
            seconds = seconds - minutes * 60
            self.duration = f'{hours:d}:{minutes:02d}:{seconds:02d}'

            self.bitrate = 0

            self.title = str(title)
            self.artist = str(artist)
            self.album = str(album)
            self.genre = str(genre)
            track_number = str(track_number).strip()
            if len(track_number) == 0:
                track_number = 1
            self.track_nr = int(track_number)

            self.cover = None
            self.mimetype = str(mimetype)
            self.size = int(size)

            self.url = self.store.urlbase + str(self.id)

        def get_children(self, start=0, end=0):
            return []

        def get_child_count(self):
            return 0

        def get_item(self, parent_id=None):

            self.debug(f'Track get_item {self.id} @ {self.parent_id}')

            # create item
            item = DIDLLite.MusicTrack(self.id, self.parent_id)
            item.album = self.album

            item.artist = self.artist
            # item.date =
            item.genre = self.genre
            item.originalTrackNumber = self.track_nr
            item.title = self.title

            item.albumArtURI = self.cover

            # add http resource
            res = DIDLLite.Resource(self.url, f'http-get:*:{self.mimetype}:*')
            if self.size > 0:
                res.size = self.size
            if self.duration > 0:
                res.duration = str(self.duration)
            if self.bitrate > 0:
                res.bitrate = str(self.bitrate)
            item.res.append(res)
            # if self.store.server.coherence.config.get(
            #        'transcoding', 'no') == 'yes':
            #     if self.mimetype in ('audio/mpeg',
            #                          'application/ogg','audio/ogg',
            #                          'audio/x-m4a',
            #                          'application/x-flac'):
            #         dlna_pn = 'DLNA.ORG_PN=LPCM'
            #         dlna_tags = DIDLLite.simple_dlna_tags[:]
            #         dlna_tags[1] = 'DLNA.ORG_CI=1'
            #         #dlna_tags[2] = 'DLNA.ORG_OP=00'
            #         new_res = DIDLLite.Resource(self.url+'?transcoded=lpcm',
            #             'http-get:*:%s:%s' % (
            #                'audio/L16;rate=44100;channels=2', ';'.join(
            #                    [dlna_pn]+dlna_tags)))
            #         new_res.size = None
            #         if self.duration > 0:
            #             new_res.duration = str(self.duration)
            #         item.res.append(new_res)
            #
            #         if self.mimetype != 'audio/mpeg':
            #             new_res = DIDLLite.Resource(
            #                 self.url+'?transcoded=mp3',
            #                 f'http-get:*:{"audio/mpeg"}:*')
            #             new_res.size = None
            #             if self.duration > 0:
            #                 new_res.duration = str(self.duration)
            #             item.res.append(new_res)

            return item

        def get_id(self):
            return self.id

        def get_name(self):
            return self.title

        def get_url(self):
            return self.url

        def get_path(self):
            return self.path

    class Video(BackendItem):
        logCategory = 'tracker_store'

        def __init__(self, store,
                     id, parent_id,
                     file, title,
                     duration,
                     size, mimetype):
            BackendItem.__init__(self)
            self.store = store
            self.id = f'video.{int(id):d}'
            self.parent_id = parent_id

            self.path = str(file)

            duration = str(duration).strip()
            duration = duration.split('.')[0]
            if len(duration) == 0:
                duration = 0
            seconds = int(duration)
            hours = seconds / 3600
            seconds = seconds - hours * 3600
            minutes = seconds / 60
            seconds = seconds - minutes * 60
            self.duration = f'{hours:d}:{minutes:02d}:{seconds:02d}'

            self.title = str(title)

            self.cover = None
            self.mimetype = str(mimetype)
            self.size = int(size)

            self.url = self.store.urlbase + str(self.id)

        def get_children(self, start=0, end=0):
            return []

        def get_child_count(self):
            return 0

        def get_item(self, parent_id=None):

            self.debug(f'Video get_item {self.id} @ {self.parent_id}')

            # create item
            item = DIDLLite.VideoItem(self.id, self.parent_id)
            # item.date =
            item.title = self.title

            item.albumArtURI = self.cover

            # add http resource
            res = DIDLLite.Resource(self.url, f'http-get:*:{self.mimetype}:*')
            if self.size > 0:
                res.size = self.size
            if self.duration > 0:
                res.duration = str(self.duration)
            item.res.append(res)

            return item

        def get_id(self):
            return self.id

        def get_name(self):
            return self.title

        def get_url(self):
            return self.url

        def get_path(self):
            return self.path

    class Image(BackendItem):
        logCategory = 'tracker_store'

        def __init__(self, store,
                     id, parent_id,
                     file, title, album,
                     date, width, height,
                     size, mimetype):
            BackendItem.__init__(self)
            self.store = store
            self.id = f'image.{int(id):d}'
            self.parent_id = parent_id

            self.path = str(file)

            self.title = str(title)

            self.album = str(album.strip())
            self.mimetype = str(mimetype)
            self.size = int(size)

            self.url = self.store.urlbase + str(self.id)

        def get_children(self, start=0, end=0):
            return []

        def get_child_count(self):
            return 0

        def get_item(self, parent_id=None):
            self.debug(f'Image get_item {self.id} @ {self.parent_id}')

            # create item
            item = DIDLLite.ImageItem(self.id, self.parent_id)
            # item.date =
            item.title = self.title

            # add http resource
            res = DIDLLite.Resource(self.url, f'http-get:*:{self.mimetype}:*')
            if self.size > 0:
                res.size = self.size
            item.res.append(res)

            return item

        def get_id(self):
            return self.id

        def get_name(self):
            return self.title

        def get_url(self):
            return self.url

        def get_path(self):
            return self.path

    class TrackerStore(BackendStore):
        '''
        This is a backend to Meta Tracker:
            http://www.gnome.org/projects/tracker/index.html

        .. versionchanged:: 0.9.0
            Migrated from louie/dispatcher to EventDispatcher
        '''

        implements = ['MediaServer']
        logCategory = 'tracker_store'

        def __init__(self, server, **kwargs):

            if server.coherence.config.get('use_dbus', 'no') != 'yes':
                raise Exception(
                    'this backend needs use_dbus enabled in the configuration')
            BackendStore.__init__(self, server, **kwargs)

            self.config = kwargs
            self.name = kwargs.get('name', 'Tracker')

            self.update_id = 0
            self.token = None

            self.songs = 0
            self.albums = 0
            self.artists = 0
            self.playlists = 0
            self.genres = 0

            self.videos = 0
            self.images = 0

            self.bus = dbus.SessionBus()
            tracker_object = self.bus.get_object(BUS_NAME, OBJECT_PATH)
            self.tracker_interface = dbus.Interface(
                tracker_object, 'org.freedesktop.Tracker')
            self.search_interface = dbus.Interface(
                tracker_object, 'org.freedesktop.Tracker.Search')
            self.keywords_interface = dbus.Interface(
                tracker_object, 'org.freedesktop.Tracker.Keywords')
            self.metadata_interface = dbus.Interface(
                tracker_object, 'org.freedesktop.Tracker.Metadata')
            self.query_id = -1

            self.containers = {}
            self.tracks = {}
            self.containers[ROOT_CONTAINER_ID] = \
                Container(ROOT_CONTAINER_ID, -1, self.name, store=self)

            def queries_finished(r):
                self.init_completed = True

            def queries_failed(r):
                error = 'Query failed'
                self.on_init_failed(msg=error)

            services = kwargs.get('service', 'Music,Videos,Images')
            services = [x.strip().lower() for x in services.split(',')]

            ml = []
            mapping = {'music': self.get_tracks,
                       'videos': self.get_videos,
                       'images': self.get_images}
            for service in services:
                try:
                    ml.append(mapping[service]())
                except KeyError:
                    self.warning(f'Wrong Tracker service definition - {service}')
            if len(ml) > 0:
                dl = defer.DeferredList(ml)
                dl.addCallback(queries_finished)
                dl.addErrback(
                    lambda x: self.on_init_failed(
                        msg='Connection to Tracker service(s) failed!'))
            else:
                self.on_init_failed(msg='No Tracker service defined!')

        def __repr__(self):
            return 'TrackerStore'

        def get_by_id(self, id):
            self.info(f'looking for id {id}')
            if isinstance(id, str):
                id = id.split('@', 1)[0]
            elif isinstance(id, bytes):
                id = id.decode('utf-8').split('@', 1)[0]
            if isinstance(id, str) and id.startswith('artist_all_tracks_'):
                try:
                    return self.containers[id]
                except KeyError:
                    return None
            item = None
            try:
                id = int(id)
                item = self.containers[id]
            except (ValueError, KeyError):
                try:
                    type, id = id.split('.')
                    if type == 'song':
                        return self.containers[AUDIO_ALL_CONTAINER_ID].children[id]
                    if type == 'album':
                        return self.containers[AUDIO_ALBUM_CONTAINER_ID].children[
                            id]
                    if type == 'artist':
                        return self.containers[AUDIO_ARTIST_CONTAINER_ID].children[
                            id]
                    if type == 'video':
                        return self.containers[VIDEO_ALL_CONTAINER_ID].children[id]
                    if type == 'image':
                        return self.containers[IMAGE_ALL_CONTAINER_ID].children[id]
                except (ValueError, KeyError):
                    return None
            return item

        def get_videos(self):

            def handle_error(error):
                print(error)
                return error

            def parse_videos_query_result(resultlist):
                videos = []
                for video in resultlist:
                    file, _, title, duration, size, mimetype = video
                    title = title.strip()
                    if len(title) == 0:
                        title = os.path.basename(file)
                    if mimetype == 'video/x-theora+ogg':
                        mimetype = 'video/ogg'
                    video_item = Video(self,
                                       self.videos, VIDEO_ALL_CONTAINER_ID,
                                       file, title,
                                       duration,
                                       size, mimetype)
                    self.videos += 1
                    videos.append(video_item)

                videos = sorted(
                    videos,
                    key=lambda x, y:
                    (x.get_name().lower() > y.get_name().lower()) -
                    (x.get_name().lower() < y.get_name().lower()))
                for video_item in videos:
                    self.containers[VIDEO_ALL_CONTAINER_ID].add_child(video_item)

            self.containers[VIDEO_CONTAINER_ID] = \
                Container(VIDEO_CONTAINER_ID, ROOT_CONTAINER_ID, 'Video',
                          store=self)
            self.containers[ROOT_CONTAINER_ID].add_child(
                self.containers[VIDEO_CONTAINER_ID])

            self.containers[VIDEO_ALL_CONTAINER_ID] = \
                Container(VIDEO_ALL_CONTAINER_ID, VIDEO_CONTAINER_ID, 'All Videos',
                          store=self,
                          children_callback=None)
            self.containers[VIDEO_CONTAINER_ID].add_child(
                self.containers[VIDEO_ALL_CONTAINER_ID])

            fields = ['Video:Title', 'Video:Duration',
                      'File:Size', 'File:Mime']

            d = defer.Deferred()
            d.addCallback(parse_videos_query_result)
            d.addErrback(handle_error)
            self.search_interface.Query(
                self.query_id, 'Videos', fields, '', '',
                video_query, False, 0, -1,
                reply_handler=lambda x: d.callback(x),
                error_handler=lambda x: d.errback(x))
            return d

        def get_images(self):

            def handle_error(error):
                return error

            def parse_images_query_result(resultlist):
                print('images', resultlist)
                images = []
                for image in resultlist:
                    file, _, title, album, \
                        date, width, height, \
                        size, mimetype = image
                    title = title.strip()
                    if len(title) == 0:
                        title = os.path.basename(file)
                    image_item = Image(
                        self,
                        self.images, IMAGE_ALL_CONTAINER_ID,
                        file, title, album,
                        date, width, height,
                        size, mimetype)
                    self.images += 1
                    images.append(image_item)

                images = sorted(
                    images,
                    key=lambda x, y:
                    (x.get_name().lower() > y.get_name().lower()) -
                    (x.get_name().lower() < y.get_name().lower()))
                for image_item in images:
                    self.containers[IMAGE_ALL_CONTAINER_ID].add_child(image_item)

            self.containers[IMAGE_CONTAINER_ID] = \
                Container(IMAGE_CONTAINER_ID, ROOT_CONTAINER_ID, 'Images',
                          store=self)
            self.containers[ROOT_CONTAINER_ID].add_child(
                self.containers[IMAGE_CONTAINER_ID])

            self.containers[IMAGE_ALL_CONTAINER_ID] = \
                Container(IMAGE_ALL_CONTAINER_ID, IMAGE_CONTAINER_ID, 'All Images',
                          store=self,
                          children_callback=None)
            self.containers[IMAGE_CONTAINER_ID].add_child(
                self.containers[IMAGE_ALL_CONTAINER_ID])

            fields = ['Image:Title', 'Image:Album',
                      'Image:Date', 'Image:Width', 'Image:Height',
                      'File:Size', 'File:Mime']

            d = defer.Deferred()
            d.addCallback(parse_images_query_result)
            d.addErrback(handle_error)
            self.search_interface.Query(
                self.query_id, 'Images', fields, '', '',
                image_query, False, 0, -1,
                reply_handler=lambda x: d.callback(x),
                error_handler=lambda x: d.errback(x))
            return d

        def get_tracks(self):

            def handle_error(error):
                return error

            def parse_tracks_query_result(resultlist):
                albums = {}
                artists = {}
                tracks = []
                for track in resultlist:
                    file, service, title, artist, album, genre, \
                        duration, album_track_count, \
                        track_number, codec, \
                        size, mimetype = track
                    if mimetype == 'video/x-vorbis+ogg':
                        mimetype = 'audio/ogg'
                    track_item = Track(
                        self,
                        self.songs, AUDIO_ALL_CONTAINER_ID,
                        file, title, artist, album, genre,
                        duration,
                        track_number,
                        size, mimetype)
                    self.songs += 1
                    tracks.append(track_item)

                tracks = sorted(
                    tracks, key=lambda x, y: (x.get_name() > y.get_name()) -
                                             (x.get_name() < y.get_name()))
                for track_item in tracks:
                    self.containers[AUDIO_ALL_CONTAINER_ID].add_child(track_item)

                    try:
                        album_item = albums[track_item.album]
                        album_item.add_child(track_item)
                    except KeyError:
                        album_item = Album(self, self.albums, track_item.album,
                                           track_item.artist)
                        albums[str(track_item.album)] = album_item
                        self.albums += 1
                        album_item.add_child(track_item)

                        try:
                            artist_item = artists[track_item.artist]
                            artist_item.add_child(album_item)
                        except KeyError:
                            artist_item = Artist(self, self.artists,
                                                 track_item.artist)
                            artists[str(track_item.artist)] = artist_item
                            self.artists += 1
                            artist_item.add_child(album_item)

                sorted_keys = list(albums.keys())
                sorted_keys.sort()
                for key in sorted_keys:
                    self.containers[AUDIO_ALBUM_CONTAINER_ID].add_child(
                        albums[key])
                sorted_keys = list(artists.keys())
                sorted_keys.sort()
                for key in sorted_keys:
                    self.containers[AUDIO_ARTIST_CONTAINER_ID].add_child(
                        artists[key])

            self.containers[AUDIO_CONTAINER_ID] = \
                Container(AUDIO_CONTAINER_ID, ROOT_CONTAINER_ID, 'Audio',
                          store=self)
            self.containers[ROOT_CONTAINER_ID].add_child(
                self.containers[AUDIO_CONTAINER_ID])

            self.containers[AUDIO_ALL_CONTAINER_ID] = \
                Container(AUDIO_ALL_CONTAINER_ID, AUDIO_CONTAINER_ID, 'All Tracks',
                          store=self,
                          children_callback=None)
            self.containers[AUDIO_CONTAINER_ID].add_child(
                self.containers[AUDIO_ALL_CONTAINER_ID])

            self.containers[AUDIO_ALBUM_CONTAINER_ID] = \
                Container(AUDIO_ALBUM_CONTAINER_ID, AUDIO_CONTAINER_ID, 'Albums',
                          store=self,
                          children_callback=None)
            self.containers[AUDIO_CONTAINER_ID].add_child(
                self.containers[AUDIO_ALBUM_CONTAINER_ID])

            self.containers[AUDIO_ARTIST_CONTAINER_ID] = \
                Container(AUDIO_ARTIST_CONTAINER_ID, AUDIO_CONTAINER_ID, 'Artists',
                          store=self,
                          children_callback=None)
            self.containers[AUDIO_CONTAINER_ID].add_child(
                self.containers[AUDIO_ARTIST_CONTAINER_ID])

            self.containers[AUDIO_PLAYLIST_CONTAINER_ID] = \
                Container(AUDIO_PLAYLIST_CONTAINER_ID, AUDIO_CONTAINER_ID,
                          'Playlists',
                          store=self,
                          children_callback=None,
                          container_class=DIDLLite.PlaylistContainer)
            self.containers[AUDIO_CONTAINER_ID].add_child(
                self.containers[AUDIO_PLAYLIST_CONTAINER_ID])

            self.containers[AUDIO_GENRE_CONTAINER_ID] = \
                Container(AUDIO_GENRE_CONTAINER_ID, AUDIO_CONTAINER_ID, 'Genres',
                          store=self,
                          children_callback=None)
            self.containers[AUDIO_CONTAINER_ID].add_child(
                self.containers[AUDIO_GENRE_CONTAINER_ID])

            self.wmc_mapping.update(
                {'4': lambda: self.get_by_id(AUDIO_ALL_CONTAINER_ID),  # all tracks
                 '5': lambda: self.get_by_id(AUDIO_GENRE_CONTAINER_ID),
                 # all genres
                 '6': lambda: self.get_by_id(AUDIO_ARTIST_CONTAINER_ID),
                 # all artists
                 '7': lambda: self.get_by_id(AUDIO_ALBUM_CONTAINER_ID),
                 # all albums
                 '13': lambda: self.get_by_id(AUDIO_PLAYLIST_CONTAINER_ID),
                 # all playlists
                 })

            fields = ['Audio:Title', 'Audio:Artist',
                      'Audio:Album', 'Audio:Genre',
                      'Audio:Duration', 'Audio:AlbumTrackCount',
                      'Audio:TrackNo', 'Audio:Codec',
                      'File:Size', 'File:Mime']

            d = defer.Deferred()
            d.addCallback(parse_tracks_query_result)
            d.addErrback(handle_error)
            self.search_interface.Query(
                self.query_id, 'Music', fields, '', '',
                tracks_query, False, 0, -1,
                reply_handler=lambda x: d.callback(x),
                error_handler=lambda x: d.errback(x))
            return d

        def upnp_init(self):
            if self.server:
                self.server.connection_manager_server.set_variable(
                    0,
                    'SourceProtocolInfo',
                    ['http-get:*:audio/mpeg:*',
                     f'internal:{self.server.coherence.hostname}:audio/mpeg:*',
                     'http-get:*:application/ogg:*',
                     'internal:%s:application/ogg:*' %
                     self.server.coherence.hostname,
                     'http-get:*:audio/ogg:*',
                     f'internal:{self.server.coherence.hostname}:audio/ogg:*',
                     'http-get:*:video/ogg:*',
                     f'internal:{self.server.coherence.hostname}:video/ogg:*',
                     'http-get:*:video/mpeg:*',
                     f'internal:{self.server.coherence.hostname}:video/mpeg:*',
                     'http-get:*:video/x-msvideo:*',
                     f'internal:{self.server.coherence.hostname}:video/x-msvideo:*',  # noqa
                     'http-get:*:video/avi:*',
                     f'internal:{self.server.coherence.hostname}:video/avi:*',
                     'http-get:*:video/mp4:*',
                     f'internal:{self.server.coherence.hostname}:video/mp4:*',
                     'http-get:*:video/quicktime:*',
                     f'internal:{self.server.coherence.hostname}:video/quicktime:*',  # noqa
                     'http-get:*:image/jpg:*',
                     f'internal:{self.server.coherence.hostname}:image/jpg:*',
                     'http-get:*:image/png:*',
                     f'internal:{self.server.coherence.hostname}:image/png:*',
                     'http-get:*:image/gif:*',
                     f'internal:{self.server.coherence.hostname}:image/gif:*',
                     ])