Exemplo n.º 1
0
 def search(self, query=None, uris=None, exact=False):
     tracklist = []
     albumlist = []
     for q in query:
         s = query[q][0]
         uri = 'yle:search:{0}:{1}'.format(q, s)
         if q == 'artist':
             return SearchResult(tracks=[], albums=[], uri=uri)
         albums, tracks = self.__yleapi.get_yle_item(offset=0,
                                                     query=s,
                                                     limit=100)
         if q != 'album':
             for item in tracks:
                 album = Album()
                 if tracks[item]['album']:
                     album_item = albums[tracks[item]['album']]
                     album = Album(name=album_item['name'],
                                   uri=album_item['uri'])
                 tracklist.append(
                     Track(name=tracks[item]['name'],
                           uri=tracks[item]['uri']))
         for item in albums:
             image = albums[item]['image']
             albumlist.append(
                 Album(name=albums[item]['name'],
                       uri=albums[item]['uri'],
                       images=[image]))
     return SearchResult(tracks=tracklist, albums=albumlist, uri=uri)
Exemplo n.º 2
0
    def search(self, query=None, uris=None, exact=False):
        # TODO Support exact search

        if not query:
            return

        if 'uri' in query:
            search_query = ''.join(query['uri'])
            url = urlparse(search_query)
            if 'youtube.com' in url.netloc:
                req = parse_qs(url.query)
                if 'list' in req:
                    return SearchResult(uri='youtube:search',
                                        tracks=resolve_playlist(
                                            req.get('list')[0]))
                else:
                    logger.info("Resolving YouTube for track '%s'",
                                search_query)
                    return SearchResult(
                        uri='youtube:search',
                        tracks=[t for t in [resolve_url(search_query)] if t])
        else:
            search_query = ' '.join(query.values()[0])
            logger.info("Searching YouTube for query '%s'", search_query)
            return SearchResult(uri='youtube:search',
                                tracks=search_youtube(search_query))
Exemplo n.º 3
0
 def __init__(self, *args, **kwargs):
     super().__init__(*args, **kwargs)
     self.dummy_library = []
     self.dummy_get_distinct_result = {}
     self.dummy_browse_result = {}
     self.dummy_find_exact_result = SearchResult()
     self.dummy_search_result = SearchResult()
Exemplo n.º 4
0
    def search(self, query=None, uris=None):
        logger.debug('Query "%s":' % query)
        if not self.remote.has_connection:
            return []

        if not query:
            # Fetch all data(browse library)
            return SearchResult(uri='plex:search', tracks=self.remote.get_tracks())

        self._validate_query(query)
        if 'any' in query:
            return SearchResult(uri='plex:search-any', tracks=self.remote.get_item_by(query['any'][0]) or [])
        else:
            search = []
            for (field, val) in query.iteritems():
                if field == "album":
                    search.append(val[0])
                if field == "artist":
                    search.append(val[0])
                if field == "track_name":
                    search.append(val[0])
                if field == "date":
                    search.append(val[0])
            logger.debug('Search query "%s":' % search)
            return SearchResult(uri='plex:search-' + '-'.join(search),
                                tracks=self.remote.get_item_by('/'.join(search)) or [])
Exemplo n.º 5
0
    def search(self, query=None, uris=None, exact=False):
        '''Search the library for tracks where field contains values.

        Parameters:
        query (dict) – one or more queries to search for - the dict's keys being:
              {
                  'any': *, # this is what we get without explicit modifiers
                  'albumartist': *,
                  'date': *,
                  'track_name': *,
                  'track_number': *,
              }


        uris (list of string or None) – zero or more URI roots to limit the search to
        exact (bool) – if the search should use exact matching

        Returns mopidy.models.SearchResult, which has these properties
            uri (string) – search result URI
            tracks (list of Track elements) – matching tracks
            artists (list of Artist elements) – matching artists
            albums (list of Album elements) – matching albums
        '''

        logger.info("Searching Plex for track '%s'", query)
        if query is None:
            logger.debug('Ignored search without query')
            return SearchResult(uri='plex:search')


        if 'uri' in query and False: # TODO add uri limiting
            pass
        else:
            search_query = ' '.join(query.values()[0])

        search_uri = 'plex:search:%s' % urllib.quote(search_query.encode('utf-8'))
        logger.info("Searching Plex with query '%s'", search_query)

        artists = []
        tracks = []
        albums = []
        for hit in self.plex.searchAudio(search_query):
            logger.debug('Got plex hit from query "%s": %s', search_query, hit)
            if isinstance(hit, plexaudio.Artist):
                artists.append(wrap_artist(hit, self.backend.plex_uri))
            elif isinstance(hit, plexaudio.Track):
                tracks.append(wrap_track(hit, self.backend.plex_uri))
            elif isinstance(hit, plexaudio.Album):
                albums.append(wrap_album(hit, self.backend.plex_uri, self.backend.resolve_uri))


        logger.debug("Got results: %s, %s, %s", artists, tracks, albums)

        return SearchResult(
            uri=search_uri,
            tracks=tracks,
            artists=artists,
            albums=albums
        )
Exemplo n.º 6
0
    def search(self, query=None, uris=None, exact=False):
        # TODO: restrict the result to 'uris'
        logger.debug(
            'Beets Query (exact=%s) within "%s": %s', exact, uris, query
        )
        if not self.remote.has_connection:
            return SearchResult(uri="beets:search-disconnected", tracks=[])

        self._validate_query(query)
        search_list = []
        for (field, values) in query.items():
            for val in values:
                # missing / unsupported fields: uri, performer
                if field == "any":
                    search_list.append(val)
                elif field == "album":
                    search_list.append(("album", val))
                elif field == "artist":
                    search_list.append(("artist", val))
                elif field == "albumartist":
                    search_list.append(("albumartist", val))
                elif field == "track_name":
                    search_list.append(("title", val))
                elif field == "track_no":
                    search_list.append(("track", val))
                elif field == "composer":
                    search_list.append(("composer", val))
                elif field == "genre":
                    search_list.append(("genre", val))
                elif field == "comment":
                    search_list.append(("comments", val))
                elif field == "date":
                    # supported date formats: YYYY, YYYY-MM, YYYY-MM-DD
                    # Days and months may consist of one or two digits.
                    # A slash (instead of a dash) is acceptable as a separator.
                    match = DATE_REGEX.search(val)
                    if match:
                        # remove None values
                        for key, value in match.groupdict().items():
                            if value:
                                search_list.append((key, int(value)))
                    else:
                        logger.info(
                            "Beets search: ignoring unknown date format (%s). "
                            'It should be "YYYY", "YYYY-MM" or "YYYY-MM-DD".',
                            val,
                        )
                else:
                    logger.info("Beets: ignoring unknown query key: %s", field)
                    break
        logger.debug("Beets search query: %s", search_list)
        tracks = self.remote.get_tracks_by(search_list, exact, [])
        uri = "-".join(
            [
                item if isinstance(item, str) else "=".join(item)
                for item in search_list
            ]
        )
        return SearchResult(uri="beets:search-" + uri, tracks=tracks)
Exemplo n.º 7
0
    def search(self, query=None, uris=None):
        # TODO Only return results within URI roots given by ``uris``

        if not query:
            return self._get_all_tracks()

        uris = query.get('uri', [])
        if uris:
            tracks = []
            for uri in uris:
                tracks += self.lookup(uri)
            if len(uris) == 1:
                uri = uris[0]
            else:
                uri = 'spotify:search'
            return SearchResult(uri=uri, tracks=tracks)

        spotify_query = self._translate_search_query(query)

        if not spotify_query:
            logger.debug('Spotify search aborted due to empty query')
            return SearchResult(uri='spotify:search')

        logger.debug('Spotify search query: %s' % spotify_query)

        future = pykka.ThreadingFuture()

        def callback(results, userdata=None):
            search_result = SearchResult(
                uri='spotify:search:%s' %
                (urllib.quote(results.query().encode('utf-8'))),
                albums=[
                    translator.to_mopidy_album(a) for a in results.albums()
                ],
                artists=[
                    translator.to_mopidy_artist(a) for a in results.artists()
                ],
                tracks=[
                    translator.to_mopidy_track(t) for t in results.tracks()
                ])
            future.set(search_result)

        if not self.backend.spotify.connected.is_set():
            logger.debug('Not connected: Spotify search cancelled')
            return SearchResult(uri='spotify:search')

        self.backend.spotify.session.search(spotify_query,
                                            callback,
                                            album_count=200,
                                            artist_count=200,
                                            track_count=200)

        try:
            return future.get(timeout=self._timeout)
        except pykka.Timeout:
            logger.debug('Timeout: Spotify search did not return in %ds',
                         self._timeout)
            return SearchResult(uri='spotify:search')
Exemplo n.º 8
0
    def _find_exact(self, query=None, uris=None):
        if not query:
            # Fetch all artists(browse library)
            return SearchResult(uri='subsonic:search',
                                tracks=self.remote.get_artists())

        return SearchResult(uri='subsonic:tracks',
                            tracks=self.remote.get_tracks_by(
                                query.get('artist'), query.get('album')))
Exemplo n.º 9
0
 def search_uri(self, query):
     type = uri.get_type(lookup_uri)
     if type == uri.ARTIST:
         artist = self.lookup_artist(uri.get_artist_id(lookup_uri))
         if artist is not None:
             return SearchResult(artists=[artist])
     elif type == uri.ALBUM:
         album = self.lookup_album(uri.get_album_id(lookup_uri))
         if album is not None:
             return SearchResult(albums=[album])
     elif type == uri.SONG:
         song = self.lookup_song(uri.get_song_id(lookup_uri))
         if song is not None:
             return SearchResult(tracks=[song])
     return None
Exemplo n.º 10
0
def stations_to_search(stations):
    tracks = []
    for station in stations:
        logger.info("stations_to_search: processing {}".format(station))
        for s in station['streams']:
            tracks.append(Track(name=station['text'], uri=s['url']))
    return SearchResult(tracks=tracks)
Exemplo n.º 11
0
    def search(self, query=None, uris=None, exact=False):
        if not query.get('any'):
            return None

        categories = set()
        countries = []

        for uri in uris or []:
            variant, identifier = translator.parse_uri(uri)
            if variant == 'country':
                countries.append(identifier.lower())
            elif variant == 'continent':
                countries.extend(self.backend.dirble.countries(identifier))
            elif variant == 'category':
                pending = [self.backend.dirble.category(identifier)]
                while pending:
                    c = pending.pop(0)
                    categories.add(c['id'])
                    pending.extend(c['children'])

        tracks = []
        for station in self.backend.dirble.search(' '.join(query['any'])):
            if countries and station['country'].lower() not in countries:
                continue
            station_categories = {c['id'] for c in station['categories']}
            if categories and not station_categories.intersection(categories):
                continue
            tracks.append(translator.station_to_track(station))

        return SearchResult(tracks=tracks)
Exemplo n.º 12
0
 def search(self, query=None, uris=None, exact=False):
     if not query:
         return
     search_query = ' '.join(query.values()[0])
     logger.info("Searching ChiaSeNhac for query '%s'", search_query)
     tracks = chiasenhac_search(search_query)
     return SearchResult(uri="chiasenhac:search", tracks=tracks)
Exemplo n.º 13
0
    def search(self, query=None, uris=None, exact=False):
        if exact:
            return self._find_exact(query=query, uris=uris)

        lib_tracks, lib_artists, lib_albums = self._search_library(query, uris)

        if query:
            aa_tracks, aa_artists, aa_albums = self._search(query, uris)
            for aa_artist in aa_artists:
                lib_artists.add(aa_artist)

            for aa_album in aa_albums:
                lib_albums.add(aa_album)

            lib_tracks = set(lib_tracks)

            for aa_track in aa_tracks:
                lib_tracks.add(aa_track)

        return SearchResult(
            uri="gmusic:search",
            tracks=lib_tracks,
            artists=lib_artists,
            albums=lib_albums,
        )
Exemplo n.º 14
0
    def test_list_album_with_artist_name(self):
        self.backend.library.dummy_find_exact_result = SearchResult(
            tracks=[Track(album=Album(name='foo'))])

        self.sendRequest('list "album" "anartist"')
        self.assertInResponse('Album: foo')
        self.assertInResponse('OK')
Exemplo n.º 15
0
 def search(self, query=None, uris=None, exact=False):
     logger.debug(u'Search query: %s in uris: %s' % (query, uris))
     # import pdb; pdb.set_trace()
     query = self._sanitize_query(query)
     logger.debug(u'Search sanitized query: %s ' % query)
     if exact:
         return self._find_exact(query, uris)
     albums = []
     if not query:
         uri = 'beetslocal:search-all'
         tracks = self.lib.items()
         albums = self.lib.albums()
     else:
         uri = uricompose('beetslocal', None, 'search', query)
         track_query = self._build_beets_track_query(query)
         logger.debug(u'Build Query "%s":' % track_query)
         tracks = self.lib.items(track_query)
         if 'track_name' not in query:
             # when trackname queried dont search for albums
             album_query = self._build_beets_album_query(query)
             logger.debug('Build Query "%s":' % album_query)
             albums = self.lib.albums(album_query)
     logger.debug(u"Query found %s tracks and %s albums" %
                  (len(tracks), len(albums)))
     return SearchResult(
         uri=uri,
         tracks=[self._convert_item(track) for track in tracks],
         albums=[self._convert_album(album) for album in albums])
Exemplo n.º 16
0
    def test_list_date_should_not_return_blank_dates(self):
        self.backend.library.dummy_find_exact_result = SearchResult(
            tracks=[Track(date="")])

        self.send_request('list "date"')
        self.assertNotInResponse("Date: ")
        self.assertInResponse("OK")
Exemplo n.º 17
0
 def find_as_search_result(
     self,
     query,
     exclude_artists=False,
     exclude_albums=False,
     exclude_songs=False,
 ):
     result = self.find_raw(query)
     if result is None:
         return None
     return SearchResult(
         uri=uri.get_search_uri(query),
         artists=[
             self.raw_artist_to_artist(artist)
             for artist in result.get("artist") or []
         ],
         albums=[
             self.raw_album_to_album(album)
             for album in result.get("album") or []
         ],
         tracks=[
             self.raw_song_to_track(song)
             for song in result.get("song") or []
         ],
     )
Exemplo n.º 18
0
    def test_list_album_should_not_return_albums_without_names(self):
        self.backend.library.dummy_find_exact_result = SearchResult(
            tracks=[Track(album=Album(name=""))])

        self.send_request('list "album"')
        self.assertNotInResponse("Album: ")
        self.assertInResponse("OK")
Exemplo n.º 19
0
    def test_list_artist_should_not_return_artists_without_names(self):
        self.backend.library.dummy_find_exact_result = SearchResult(
            tracks=[Track(artists=[Artist(name="")])])

        self.send_request('list "artist"')
        self.assertNotInResponse("Artist: ")
        self.assertInResponse("OK")
Exemplo n.º 20
0
 def find_as_search_result(self, query, exclude_artists=False, exclude_albums=False, exclude_songs=False):
     result_folders = self.find_raw(query, exclude_artists=exclude_artists, exclude_albums=exclude_albums, exclude_songs=True)
     result_id3 = self.find_raw(query, exclude_artists=exclude_artists, exclude_albums=exclude_albums, exclude_songs=exclude_songs, id3=True)
     if result_folders is None:
         result_folders = dict()
     if result_id3 is None:
         return None
     artists_map = {}
     albums_map = {}
     for artist in result_folders.get('artist') or []:
         artists_map[artist.get('name')] = [artist, False]
     for artist in result_id3.get('artist') or []:
         artists_map[artist.get('name')] = [artist, True]
     for album in result_folders.get('album') or []:
         albums_map[(album.get('artist'), album.get('title'))] = [album, False]
     for album in result_id3.get('album') or []:
         albums_map[(album.get('artist'), album.get('name'))] = [album, True]
     out_artists = []
     out_albums = []
     for artist_obj in artists_map.viewvalues():
         out_artists.append(self.raw_artist_to_artist(artist_obj[0]) if artist_obj[1] else self.raw_directory_to_artist(artist_obj[0]))
     for album_obj in albums_map.viewvalues():
         out_albums.append(self.raw_album_to_album(album_obj[0]) if album_obj[1] else self.raw_directory_to_album(album_obj[0]))
     return SearchResult(
         uri=uri.get_search_uri(query), # FIXME: include api parameters in url somehow maybe?
         artists=out_artists,
         albums=out_albums,
         tracks=[self.raw_song_to_track(song) for song in (result_id3.get('song') or [])])
Exemplo n.º 21
0
 def _get_all_tracks(self):
     # Since we can't search for the entire Spotify library, we return
     # all tracks in the playlists when the query is empty.
     tracks = []
     for playlist in self.backend.playlists.playlists:
         tracks += playlist.tracks
     return SearchResult(uri='spotify:search', tracks=tracks)
Exemplo n.º 22
0
 def test_count_with_track_length_none(self):
     self.backend.library.dummy_find_exact_result = SearchResult(
         tracks=[Track(uri="dummy:b", date="2001", length=None)])
     self.send_request('count "date" "2001"')
     self.assertInResponse("songs: 1")
     self.assertInResponse("playtime: 0")
     self.assertInResponse("OK")
Exemplo n.º 23
0
 def search(self, query=None, uris=None, exact=False):
     logger.info("YTMusic searching for %s", query)
     tracks = []
     if "any" in query:
         try:
             res = API.search(" ".join(query["any"]), filter=None)
             tracks.extend(parseSearch(res))
             if (exact):
                 for track in tracks:
                     for q in query["any"]:
                         q = q.casefold()
                         if q != track.name.casefold():
                             tracks.remove(track)
                         if q == track.album.name.casefold():
                             tracks.remove(track)
                         for artist in track.artists:
                             if q == artist.name.casefold():
                                 tracks.remove(track)
         except Exception:
             logger.exception("YTMusic search failed for query \"%s\"", " ".join(query["any"]))
     elif "track_name" in query:
         try:
             res = API.search(" ".join(query["track_name"]), filter="songs")
             if exact:
                 tracks.extend(parseSearch(res, "track", query["track_name"]))
             else:
                 tracks.extend(parseSearch(res))
         except Exception:
             logger.exception("YTMusic search failed for query \"title\"=\"%s\"", " ".join(query["track_name"]))
     elif "albumartist" in query or "artist" in query:
         q1 = ("albumartist" in query and query["albumartist"]) or []
         q2 = ("artist" in query and query["artist"]) or []
         try:
             res = API.search(" ".join(q1 + q2), filter="artists")
             if exact:
                 tracks.extend(parseSearch(res, "artist", q1 + q2))
             else:
                 tracks.extend(parseSearch(res))
         except Exception:
             logger.exception("YTMusic search failed for query \"artist\"=\"%s\"", " ".join(q1 + q2))
     elif "album" in query:
         try:
             res = API.search(" ".join(query["album"]), filter="albums")
             if exact:
                 tracks.extend(parseSearch(res, "album", query["album"]))
             else:
                 tracks.extend(parseSearch(res))
         except Exception:
             logger.exception("YTMusic search failed for query \"album\"=\"%s\"", " ".join(query["album"]))
     else:
         logger.info("YTMusic skipping search, unsupported field types \"%s\"", " ".join(query.keys()))
         return None
     logger.info("YTMusic search returned %d results", len(tracks))
     return SearchResult(
         uri="",
         tracks=tracks,
         artists=None,
         albums=None,
     )
 def test_search_uri(self):
     empty = SearchResult(uri='local:search?')
     self.assertEqual(empty, self.library.search(uris=None))
     self.assertEqual(empty, self.library.search(uris=[]))
     self.assertEqual(empty, self.library.search(uris=['local:']))
     self.assertEqual(empty, self.library.search(uris=['local:directory']))
     self.assertEqual(empty, self.library.search(uris=['local:directory:']))
     self.assertEqual(empty, self.library.search(uris=['foobar:']))
Exemplo n.º 25
0
 def test_serialize_without_results(self):
     self.assertDictEqual(
         {
             "__model__": "SearchResult",
             "uri": "uri"
         },
         SearchResult(uri="uri").serialize(),
     )
Exemplo n.º 26
0
    def search(self, query=None, uris=None, exact=False):
        refresh_cache()
        if uris is not None:
            if next(
                (uri
                 for uri in uris if uri.startswith(uri_scheme)), None) is None:
                return None

        if query is None or len(query) == 0: return None

        query_type = u'cloudcast'
        query_val = u''

        # look for the first query key that we can deal with
        for k in query:
            q = query[k]
            if q[0].startswith('refresh:'):
                clear_caches(True)
                q[0] = q[0][len('refresh:'):]
            if k in ['artist', 'albumartist', 'composer', 'performer']:
                query_type = 'user'
                query_value = query[k][0]
                break
            if k in ['any', 'track_name']:
                if q[0].startswith('user:'******'user'
                    query_value = q[0][len('user:'******'': return None

        search_uri = uri_search.format(query_type, query_value)
        sr = searches_cache.get(search_uri)
        if sr is not None: return sr
        res = None
        if query_type == 'user':
            albums = list_albums(search_uri)
            res = SearchResult(uri=search_uri, albums=albums)
        else:
            tracks = get_tracks_for_uri(search_uri)
            res = SearchResult(uri=search_uri, tracks=tracks)
        searches_cache.add(search_uri, res)
        return res
Exemplo n.º 27
0
 def search(self, query=None, uris=None, exact=False):
     if query is None or not query:
         return
     tunein_query = translator.mopidy_to_tunein_query(query)
     tracks = []
     for station in self.backend.tunein.search(tunein_query):
         track = translator.station_to_track(station)
         tracks.append(track)
     return SearchResult(uri='tunein:search', tracks=tracks)
Exemplo n.º 28
0
    def _find_exact(self, query=None, uris=None):
        # Find exact can only be done on gmusic library,
        # since one can't filter all access searches
        lib_tracks, lib_artists, lib_albums = self._search_library(query, uris)

        return SearchResult(uri='gmusic:search',
                            tracks=lib_tracks,
                            artists=lib_artists,
                            albums=lib_albums)
Exemplo n.º 29
0
 def search_by_artist_and_album(self, artist_name, album_name):
     artists = self.subsonic_api.get_raw_artists()
     artist = next(item for item in artists
                   if artist_name in item.get('name'))
     albums = self.subsonic_api.get_raw_albums(artist.get('id'))
     album = next(item for item in albums
                  if album_name in item.get('title'))
     return SearchResult(
         tracks=self.subsonic_api.get_songs_as_tracks(album.get('id')))
Exemplo n.º 30
0
 def search(self, query=None, limit=100, offset=0, uris=None, exact=False):
     q = []
     for field, values in query.items() if query else []:
         q.extend((field, value) for value in values)
     filters = [f for uri in uris or [] for f in self._filters(uri) if f]
     with self._connect() as c:
         tracks = schema.search_tracks(c, q, limit, offset, exact, filters)
     uri = uritools.uricompose("local", path="search", query=q)
     return SearchResult(uri=uri, tracks=tracks)
Exemplo n.º 31
0
 def test_artists(self):
     artists = [Artist(), Artist(), Artist()]
     result = SearchResult(artists=artists)
     self.assertEqual(list(result.artists), artists)
     with self.assertRaises(AttributeError):
         result.artists = None
Exemplo n.º 32
0
 def test_tracks(self):
     tracks = [Track(), Track(), Track()]
     result = SearchResult(tracks=tracks)
     self.assertEqual(list(result.tracks), tracks)
     with self.assertRaises(AttributeError):
         result.tracks = None
Exemplo n.º 33
0
 def test_uri(self):
     uri = 'an_uri'
     result = SearchResult(uri=uri)
     self.assertEqual(result.uri, uri)
     with self.assertRaises(AttributeError):
         result.uri = None
Exemplo n.º 34
0
 def test_albums(self):
     albums = [Album(), Album(), Album()]
     result = SearchResult(albums=albums)
     self.assertEqual(list(result.albums), albums)
     with self.assertRaises(AttributeError):
         result.albums = None