From 147bb5e983d059862dce55bd807c4662376fdac7 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Thu, 20 Dec 2012 21:09:11 +0100 Subject: [PATCH 1/4] local: Read track date from tag cache --- docs/changes.rst | 4 ++++ mopidy/backends/local/translator.py | 3 +++ tests/backends/local/translator_test.py | 12 ++++++------ 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/docs/changes.rst b/docs/changes.rst index 3caa02df..2a55e73e 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -24,6 +24,10 @@ v0.11.0 (in development) add "spotify:artist:5TgQ66WuWkoQ2xYxaSTnVP" add "spotify:user:p3.no:playlist:0XX6tamRiqEgh3t6FPFEkw" +**Local backend** + +- Load track dates from tag cache. + **MPD frontend** - Add :attr:`mopidy.settings.MPD_SERVER_CONNECTION_TIMEOUT` setting which diff --git a/mopidy/backends/local/translator.py b/mopidy/backends/local/translator.py index ff58a16e..390fd92a 100644 --- a/mopidy/backends/local/translator.py +++ b/mopidy/backends/local/translator.py @@ -123,6 +123,9 @@ def _convert_mpd_data(data, tracks, music_dir): if 'title' in data: track_kwargs['name'] = data['title'] + if 'date' in data: + track_kwargs['date'] = data['date'] + if 'musicbrainz_trackid' in data: track_kwargs['musicbrainz_id'] = data['musicbrainz_trackid'] diff --git a/tests/backends/local/translator_test.py b/tests/backends/local/translator_test.py index 90ee849d..61a86672 100644 --- a/tests/backends/local/translator_test.py +++ b/tests/backends/local/translator_test.py @@ -99,8 +99,8 @@ expected_tracks = [] def generate_track(path, ident): uri = path_to_uri(path_to_data_dir(path)) track = Track( - name='trackname', artists=expected_artists, track_no=1, - album=expected_albums[0], length=4000, uri=uri) + uri=uri, name='trackname', artists=expected_artists, + album=expected_albums[0], track_no=1, date='2006', length=4000) expected_tracks.append(track) @@ -126,8 +126,8 @@ class MPDTagCacheToTracksTest(unittest.TestCase): path_to_data_dir('simple_tag_cache'), path_to_data_dir('')) uri = path_to_uri(path_to_data_dir('song1.mp3')) track = Track( - name='trackname', artists=expected_artists, track_no=1, - album=expected_albums[0], length=4000, uri=uri) + uri=uri, name='trackname', artists=expected_artists, track_no=1, + album=expected_albums[0], date='2006', length=4000) self.assertEqual(set([track]), tracks) def test_advanced_cache(self): @@ -182,6 +182,6 @@ class MPDTagCacheToTracksTest(unittest.TestCase): artist = Artist(name='albumartistname') album = expected_albums[0].copy(artists=[artist]) track = Track( - name='trackname', artists=expected_artists, track_no=1, - album=album, length=4000, uri=uri) + uri=uri, name='trackname', artists=expected_artists, track_no=1, + album=album, date='2006', length=4000) self.assertEqual(track, list(tracks)[0]) From 53f3ef488c0176712670f92f61c0e9bb731a0e92 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Thu, 20 Dec 2012 21:26:55 +0100 Subject: [PATCH 2/4] local: Reorder search filters and tests --- mopidy/backends/local/library.py | 16 ++++++------ tests/backends/base/library.py | 44 ++++++++++++++++---------------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/mopidy/backends/local/library.py b/mopidy/backends/local/library.py index e0e6f423..65c45376 100644 --- a/mopidy/backends/local/library.py +++ b/mopidy/backends/local/library.py @@ -46,23 +46,23 @@ class LocalLibraryProvider(base.BaseLibraryProvider): for value in values: q = value.strip() + uri_filter = lambda t: q == t.uri track_filter = lambda t: q == t.name album_filter = lambda t: q == getattr(t, 'album', Album()).name artist_filter = lambda t: filter( lambda a: q == a.name, t.artists) - uri_filter = lambda t: q == t.uri any_filter = lambda t: ( track_filter(t) or album_filter(t) or artist_filter(t) or uri_filter(t)) - if field == 'track': + if field == 'uri': + result_tracks = filter(uri_filter, result_tracks) + elif field == 'track': result_tracks = filter(track_filter, result_tracks) elif field == 'album': result_tracks = filter(album_filter, result_tracks) elif field == 'artist': result_tracks = filter(artist_filter, result_tracks) - elif field == 'uri': - result_tracks = filter(uri_filter, result_tracks) elif field == 'any': result_tracks = filter(any_filter, result_tracks) else: @@ -80,23 +80,23 @@ class LocalLibraryProvider(base.BaseLibraryProvider): for value in values: q = value.strip().lower() + uri_filter = lambda t: q in t.uri.lower() track_filter = lambda t: q in t.name.lower() album_filter = lambda t: q in getattr( t, 'album', Album()).name.lower() artist_filter = lambda t: filter( lambda a: q in a.name.lower(), t.artists) - uri_filter = lambda t: q in t.uri.lower() any_filter = lambda t: track_filter(t) or album_filter(t) or \ artist_filter(t) or uri_filter(t) - if field == 'track': + if field == 'uri': + result_tracks = filter(uri_filter, result_tracks) + elif field == 'track': result_tracks = filter(track_filter, result_tracks) elif field == 'album': result_tracks = filter(album_filter, result_tracks) elif field == 'artist': result_tracks = filter(artist_filter, result_tracks) - elif field == 'uri': - result_tracks = filter(uri_filter, result_tracks) elif field == 'any': result_tracks = filter(any_filter, result_tracks) else: diff --git a/tests/backends/base/library.py b/tests/backends/base/library.py index 4e9232e5..c9db7767 100644 --- a/tests/backends/base/library.py +++ b/tests/backends/base/library.py @@ -60,11 +60,13 @@ class LibraryControllerTest(object): result = self.library.find_exact(album=['unknown artist']) self.assertEqual(result, []) - def test_find_exact_artist(self): - result = self.library.find_exact(artist=['artist1']) + def test_find_exact_uri(self): + track_1_uri = 'file://' + path_to_data_dir('uri1') + result = self.library.find_exact(uri=track_1_uri) self.assertEqual(result, self.tracks[:1]) - result = self.library.find_exact(artist=['artist2']) + track_2_uri = 'file://' + path_to_data_dir('uri2') + result = self.library.find_exact(uri=track_2_uri) self.assertEqual(result, self.tracks[1:2]) def test_find_exact_track(self): @@ -74,6 +76,13 @@ class LibraryControllerTest(object): result = self.library.find_exact(track=['track2']) self.assertEqual(result, self.tracks[1:2]) + def test_find_exact_artist(self): + result = self.library.find_exact(artist=['artist1']) + self.assertEqual(result, self.tracks[:1]) + + result = self.library.find_exact(artist=['artist2']) + self.assertEqual(result, self.tracks[1:2]) + def test_find_exact_album(self): result = self.library.find_exact(album=['album1']) self.assertEqual(result, self.tracks[:1]) @@ -81,15 +90,6 @@ class LibraryControllerTest(object): result = self.library.find_exact(album=['album2']) self.assertEqual(result, self.tracks[1:2]) - def test_find_exact_uri(self): - track_1_uri = 'file://' + path_to_data_dir('uri1') - result = self.library.find_exact(uri=track_1_uri) - self.assertEqual(result, self.tracks[:1]) - - track_2_uri = 'file://' + path_to_data_dir('uri2') - result = self.library.find_exact(uri=track_2_uri) - self.assertEqual(result, self.tracks[1:2]) - def test_find_exact_wrong_type(self): test = lambda: self.library.find_exact(wrong=['test']) self.assertRaises(LookupError, test) @@ -120,11 +120,11 @@ class LibraryControllerTest(object): result = self.library.search(any=['unknown']) self.assertEqual(result, []) - def test_search_artist(self): - result = self.library.search(artist=['Tist1']) + def test_search_uri(self): + result = self.library.search(uri=['RI1']) self.assertEqual(result, self.tracks[:1]) - result = self.library.search(artist=['Tist2']) + result = self.library.search(uri=['RI2']) self.assertEqual(result, self.tracks[1:2]) def test_search_track(self): @@ -134,6 +134,13 @@ class LibraryControllerTest(object): result = self.library.search(track=['Rack2']) self.assertEqual(result, self.tracks[1:2]) + def test_search_artist(self): + result = self.library.search(artist=['Tist1']) + self.assertEqual(result, self.tracks[:1]) + + result = self.library.search(artist=['Tist2']) + self.assertEqual(result, self.tracks[1:2]) + def test_search_album(self): result = self.library.search(album=['Bum1']) self.assertEqual(result, self.tracks[:1]) @@ -141,13 +148,6 @@ class LibraryControllerTest(object): result = self.library.search(album=['Bum2']) self.assertEqual(result, self.tracks[1:2]) - def test_search_uri(self): - result = self.library.search(uri=['RI1']) - self.assertEqual(result, self.tracks[:1]) - - result = self.library.search(uri=['RI2']) - self.assertEqual(result, self.tracks[1:2]) - def test_search_any(self): result = self.library.search(any=['Tist1']) self.assertEqual(result, self.tracks[:1]) From 02c8ea53d7eb54c25deec60425d1e4703b890c7e Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Thu, 20 Dec 2012 21:28:36 +0100 Subject: [PATCH 3/4] local: Add search-by-date support --- docs/changes.rst | 2 ++ mopidy/backends/local/library.py | 6 ++++++ tests/backends/base/library.py | 32 ++++++++++++++++++++++++++++---- tests/data/library_tag_cache | 2 ++ 4 files changed, 38 insertions(+), 4 deletions(-) diff --git a/docs/changes.rst b/docs/changes.rst index 2a55e73e..bc709be2 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -28,6 +28,8 @@ v0.11.0 (in development) - Load track dates from tag cache. +- Add support for searching by track date. + **MPD frontend** - Add :attr:`mopidy.settings.MPD_SERVER_CONNECTION_TIMEOUT` setting which diff --git a/mopidy/backends/local/library.py b/mopidy/backends/local/library.py index 65c45376..143c6d84 100644 --- a/mopidy/backends/local/library.py +++ b/mopidy/backends/local/library.py @@ -51,6 +51,7 @@ class LocalLibraryProvider(base.BaseLibraryProvider): album_filter = lambda t: q == getattr(t, 'album', Album()).name artist_filter = lambda t: filter( lambda a: q == a.name, t.artists) + date_filter = lambda t: q == t.date any_filter = lambda t: ( track_filter(t) or album_filter(t) or artist_filter(t) or uri_filter(t)) @@ -63,6 +64,8 @@ class LocalLibraryProvider(base.BaseLibraryProvider): result_tracks = filter(album_filter, result_tracks) elif field == 'artist': result_tracks = filter(artist_filter, result_tracks) + elif field == 'date': + result_tracks = filter(date_filter, result_tracks) elif field == 'any': result_tracks = filter(any_filter, result_tracks) else: @@ -86,6 +89,7 @@ class LocalLibraryProvider(base.BaseLibraryProvider): t, 'album', Album()).name.lower() artist_filter = lambda t: filter( lambda a: q in a.name.lower(), t.artists) + date_filter = lambda t: t.date and t.date.startswith(q) any_filter = lambda t: track_filter(t) or album_filter(t) or \ artist_filter(t) or uri_filter(t) @@ -97,6 +101,8 @@ class LocalLibraryProvider(base.BaseLibraryProvider): result_tracks = filter(album_filter, result_tracks) elif field == 'artist': result_tracks = filter(artist_filter, result_tracks) + elif field == 'date': + result_tracks = filter(date_filter, result_tracks) elif field == 'any': result_tracks = filter(any_filter, result_tracks) else: diff --git a/tests/backends/base/library.py b/tests/backends/base/library.py index c9db7767..57aec3c6 100644 --- a/tests/backends/base/library.py +++ b/tests/backends/base/library.py @@ -16,11 +16,12 @@ class LibraryControllerTest(object): Album()] tracks = [ Track( - name='track1', length=4000, artists=artists[:1], - album=albums[0], uri='file://' + path_to_data_dir('uri1')), + uri='file://' + path_to_data_dir('uri1'), name='track1', + artists=artists[:1], album=albums[0], date='2001-02-03', + length=4000), Track( - name='track2', length=4000, artists=artists[1:2], - album=albums[1], uri='file://' + path_to_data_dir('uri2')), + uri='file://' + path_to_data_dir('uri2'), name='track2', + artists=artists[1:2], album=albums[1], date='2002', length=4000), Track()] def setUp(self): @@ -90,6 +91,16 @@ class LibraryControllerTest(object): result = self.library.find_exact(album=['album2']) self.assertEqual(result, self.tracks[1:2]) + def test_find_exact_date(self): + result = self.library.find_exact(date=['2001']) + self.assertEqual(result, []) + + result = self.library.find_exact(date=['2001-02-03']) + self.assertEqual(result, self.tracks[:1]) + + result = self.library.find_exact(date=['2002']) + self.assertEqual(result, self.tracks[1:2]) + def test_find_exact_wrong_type(self): test = lambda: self.library.find_exact(wrong=['test']) self.assertRaises(LookupError, test) @@ -148,6 +159,19 @@ class LibraryControllerTest(object): result = self.library.search(album=['Bum2']) self.assertEqual(result, self.tracks[1:2]) + def test_search_date(self): + result = self.library.search(date=['2001']) + self.assertEqual(result, self.tracks[:1]) + + result = self.library.search(date=['2001-02-03']) + self.assertEqual(result, self.tracks[:1]) + + result = self.library.search(date=['2001-02-04']) + self.assertEqual(result, []) + + result = self.library.search(date=['2002']) + self.assertEqual(result, self.tracks[1:2]) + def test_search_any(self): result = self.library.search(any=['Tist1']) self.assertEqual(result, self.tracks[:1]) diff --git a/tests/data/library_tag_cache b/tests/data/library_tag_cache index e090fcbd..50771a0a 100644 --- a/tests/data/library_tag_cache +++ b/tests/data/library_tag_cache @@ -8,12 +8,14 @@ file: /uri1 Artist: artist1 Title: track1 Album: album1 +Date: 2001-02-03 Time: 4 key: uri2 file: /uri2 Artist: artist2 Title: track2 Album: album2 +Date: 2002 Time: 4 key: uri3 file: /uri3 From 242df281149c4743fd8d202649b3216d96b0e611 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Thu, 20 Dec 2012 21:34:06 +0100 Subject: [PATCH 4/4] mpd: Support search by date (#272) --- docs/changes.rst | 2 ++ mopidy/frontends/mpd/translator.py | 9 ++++++--- tests/frontends/mpd/translator_test.py | 14 ++++++++++++++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/docs/changes.rst b/docs/changes.rst index bc709be2..8c2087e5 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -53,6 +53,8 @@ v0.11.0 (in development) - Add empty stubs for channel commands for client to client communication. +- Add support for search by date. + **Internal changes** *Models:* diff --git a/mopidy/frontends/mpd/translator.py b/mopidy/frontends/mpd/translator.py index ef7c8a1c..e26d7dce 100644 --- a/mopidy/frontends/mpd/translator.py +++ b/mopidy/frontends/mpd/translator.py @@ -187,12 +187,15 @@ def query_from_mpd_search_format(mpd_query): :param mpd_query: the MPD search query :type mpd_query: string """ + # XXX The regexps below should be refactored to reuse common patterns here + # and in mopidy.frontends.mpd.protocol.music_db. query_pattern = ( - r'"?(?:[Aa]lbum|[Aa]rtist|[Ff]ile[name]*|[Tt]itle|[Aa]ny)"? "[^"]+"') + r'"?(?:[Aa]lbum|[Aa]rtist|[Dd]ate|[Ff]ile|[Ff]ilename|' + r'[Tt]itle|[Aa]ny)"? "[^"]+"') query_parts = re.findall(query_pattern, mpd_query) query_part_pattern = ( - r'"?(?P([Aa]lbum|[Aa]rtist|[Ff]ile[name]*|[Tt]itle|[Aa]ny))"? ' - r'"(?P[^"]+)"') + r'"?(?P([Aa]lbum|[Aa]rtist|[Dd]ate|[Ff]ile|[Ff]ilename|' + r'[Tt]itle|[Aa]ny))"? "(?P[^"]+)"') query = {} for query_part in query_parts: m = re.match(query_part_pattern, query_part) diff --git a/tests/frontends/mpd/translator_test.py b/tests/frontends/mpd/translator_test.py index aa3b77bb..088ae137 100644 --- a/tests/frontends/mpd/translator_test.py +++ b/tests/frontends/mpd/translator_test.py @@ -121,6 +121,20 @@ class PlaylistMpdFormatTest(unittest.TestCase): self.assertEqual(dict(result[0])['Track'], 2) +class QueryFromMpdSearchFormatTest(unittest.TestCase): + def test_dates_are_extracted(self): + result = translator.query_from_mpd_search_format( + 'Date "1974-01-02" Date "1975"') + self.assertEqual(result['date'][0], '1974-01-02') + self.assertEqual(result['date'][1], '1975') + + # TODO Test more mappings + + +class QueryFromMpdListFormatTest(unittest.TestCase): + pass # TODO + + class TracksToTagCacheFormatTest(unittest.TestCase): def setUp(self): settings.LOCAL_MUSIC_PATH = '/dir/subdir'