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)
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))
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()
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 [])
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 )
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)
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')
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')))
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
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)
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)
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)
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, )
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')
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])
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")
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 [] ], )
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")
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")
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 [])])
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)
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")
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:']))
def test_serialize_without_results(self): self.assertDictEqual( { "__model__": "SearchResult", "uri": "uri" }, SearchResult(uri="uri").serialize(), )
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
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)
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)
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')))
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)
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
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
def test_uri(self): uri = 'an_uri' result = SearchResult(uri=uri) self.assertEqual(result.uri, uri) with self.assertRaises(AttributeError): result.uri = None
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