mirror of
https://github.com/beetbox/beets.git
synced 2026-02-22 07:14:24 +01:00
Deal with sorting
Try to follow any sort found & manage absence of sort. When there are multiple sort directives given, concatenate them. Tests not extended yet.
This commit is contained in:
parent
65b52b9c48
commit
45c0c9b3cb
3 changed files with 69 additions and 30 deletions
|
|
@ -730,3 +730,15 @@ class NullSort(Sort):
|
|||
"""No sorting. Leave results unsorted."""
|
||||
def sort(items):
|
||||
return items
|
||||
|
||||
def __nonzero__(self):
|
||||
return self.__bool__()
|
||||
|
||||
def __bool__(self):
|
||||
return False
|
||||
|
||||
def __eq__(self, other):
|
||||
return type(self) == type(other) or other is None
|
||||
|
||||
def __hash__(self):
|
||||
return 0
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ from beets import ui
|
|||
from beets.util import mkdirall, normpath, syspath
|
||||
from beets.library import Item, Album, parse_query_string
|
||||
from beets.dbcore import OrQuery
|
||||
from beets.dbcore.query import MultipleSort
|
||||
import os
|
||||
|
||||
|
||||
|
|
@ -77,9 +78,16 @@ class SmartPlaylistPlugin(BeetsPlugin):
|
|||
"""
|
||||
Instanciate queries for the playlists.
|
||||
|
||||
Each playlist has 2 queries: one or items one for albums. We must also
|
||||
remember its name. _unmatched_playlists is a set of tuples
|
||||
(name, q, album_q).
|
||||
Each playlist has 2 queries: one or items one for albums, each with a
|
||||
sort. We must also remember its name. _unmatched_playlists is a set of
|
||||
tuples (name, (q, q_sort), (album_q, album_q_sort)).
|
||||
|
||||
sort may be any sort, or NullSort, or None. None and NullSort are
|
||||
equivalent and both eval to False.
|
||||
More precisely
|
||||
- it will be NullSort when a playlist query ('query' or 'album_query')
|
||||
is a single item or a list with 1 element
|
||||
- it will be None when there are multiple items i a query
|
||||
"""
|
||||
self._unmatched_playlists = set()
|
||||
self._matched_playlists = set()
|
||||
|
|
@ -88,18 +96,28 @@ class SmartPlaylistPlugin(BeetsPlugin):
|
|||
playlist_data = (playlist['name'],)
|
||||
for key, Model in (('query', Item), ('album_query', Album)):
|
||||
qs = playlist.get(key)
|
||||
# FIXME sort mgmt
|
||||
if qs is None:
|
||||
query = None
|
||||
sort = None
|
||||
query_and_sort = None, None
|
||||
elif isinstance(qs, basestring):
|
||||
query, sort = parse_query_string(qs, Model)
|
||||
query_and_sort = parse_query_string(qs, Model)
|
||||
elif len(qs) == 1:
|
||||
query_and_sort = parse_query_string(qs[0], Model)
|
||||
else:
|
||||
query = OrQuery([parse_query_string(q, Model)[0]
|
||||
for q in qs])
|
||||
sort = None
|
||||
del sort # FIXME
|
||||
playlist_data += (query,)
|
||||
# multiple queries and sorts
|
||||
queries, sorts = zip(*(parse_query_string(q, Model)
|
||||
for q in qs))
|
||||
query = OrQuery(queries)
|
||||
sort = MultipleSort()
|
||||
for s in sorts:
|
||||
if s:
|
||||
sort.add_sort(s)
|
||||
if not sort.sorts:
|
||||
sort = None
|
||||
elif len(sort.sorts) == 1:
|
||||
sort = sort.sorts[0]
|
||||
query_and_sort = query, sort
|
||||
|
||||
playlist_data += (query_and_sort,)
|
||||
|
||||
self._unmatched_playlists.add(playlist_data)
|
||||
|
||||
|
|
@ -108,7 +126,7 @@ class SmartPlaylistPlugin(BeetsPlugin):
|
|||
self.build_queries()
|
||||
|
||||
for playlist in self._unmatched_playlists:
|
||||
n, q, a_q = playlist
|
||||
n, (q, _), (a_q, _) = playlist
|
||||
if a_q and isinstance(model, Album):
|
||||
matches = a_q.match(model)
|
||||
elif q and isinstance(model, Item):
|
||||
|
|
@ -133,14 +151,14 @@ class SmartPlaylistPlugin(BeetsPlugin):
|
|||
relative_to = normpath(relative_to)
|
||||
|
||||
for playlist in self._matched_playlists:
|
||||
name, query, album_query = playlist
|
||||
name, (query, q_sort), (album_query, a_q_sort) = playlist
|
||||
self._log.debug(u"Creating playlist {0}", name)
|
||||
items = []
|
||||
|
||||
if query:
|
||||
items.extend(lib.items(query))
|
||||
items.extend(lib.items(query, q_sort))
|
||||
if album_query:
|
||||
for album in lib.albums(album_query):
|
||||
for album in lib.albums(album_query, a_q_sort):
|
||||
items.extend(album.items())
|
||||
|
||||
m3us = {}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ from mock import Mock, MagicMock
|
|||
from beetsplug.smartplaylist import SmartPlaylistPlugin
|
||||
from beets.library import Item, Album, parse_query_string
|
||||
from beets.dbcore import OrQuery
|
||||
from beets.dbcore.query import NullSort
|
||||
from beets.util import syspath
|
||||
from beets.ui import UserError
|
||||
from beets import config
|
||||
|
|
@ -54,15 +55,15 @@ class SmartPlaylistTest(unittest.TestCase):
|
|||
])
|
||||
spl.build_queries()
|
||||
self.assertEqual(spl._matched_playlists, set())
|
||||
foo_foo, _ = parse_query_string('FOO foo', Item)
|
||||
bar_bar = OrQuery([parse_query_string('BAR bar1', Album)[0],
|
||||
parse_query_string('BAR bar2', Album)[0]])
|
||||
baz_baz, _ = parse_query_string('BAZ baz', Item)
|
||||
baz_baz2, _ = parse_query_string('BAZ baz', Album)
|
||||
foo_foo = parse_query_string('FOO foo', Item)
|
||||
baz_baz = parse_query_string('BAZ baz', Item)
|
||||
baz_baz2 = parse_query_string('BAZ baz', Album)
|
||||
bar_bar = OrQuery((parse_query_string('BAR bar1', Album)[0],
|
||||
parse_query_string('BAR bar2', Album)[0]))
|
||||
self.assertEqual(spl._unmatched_playlists, set([
|
||||
('foo', foo_foo, None),
|
||||
('bar', None, bar_bar),
|
||||
('baz', baz_baz, baz_baz2)
|
||||
('foo', foo_foo, (None, None)),
|
||||
('baz', baz_baz, baz_baz2),
|
||||
('bar', (None, None), (bar_bar, None)),
|
||||
]))
|
||||
|
||||
def test_db_changes(self):
|
||||
|
|
@ -80,9 +81,9 @@ class SmartPlaylistTest(unittest.TestCase):
|
|||
q2 = Mock()
|
||||
q2.matches.side_effect = {i1: False, i2: True}.__getitem__
|
||||
|
||||
pl1 = ('1', q1, a_q1)
|
||||
pl2 = ('2', None, a_q1)
|
||||
pl3 = ('3', q2, None)
|
||||
pl1 = '1', (q1, None), (a_q1, None)
|
||||
pl2 = '2', (None, None), (a_q1, None)
|
||||
pl3 = '3', (q2, None), (None, None)
|
||||
|
||||
spl._unmatched_playlists = set([pl1, pl2, pl3])
|
||||
spl._matched_playlists = set()
|
||||
|
|
@ -115,7 +116,7 @@ class SmartPlaylistTest(unittest.TestCase):
|
|||
lib = Mock()
|
||||
lib.items.return_value = [i]
|
||||
lib.albums.return_value = []
|
||||
pl = 'my_playlist.m3u', q, a_q
|
||||
pl = 'my_playlist.m3u', (q, None), (a_q, None)
|
||||
spl._matched_playlists = [pl]
|
||||
|
||||
dir = mkdtemp()
|
||||
|
|
@ -127,8 +128,8 @@ class SmartPlaylistTest(unittest.TestCase):
|
|||
rmtree(dir)
|
||||
raise
|
||||
|
||||
lib.items.assert_called_once_with(q)
|
||||
lib.albums.assert_called_once_with(a_q)
|
||||
lib.items.assert_called_once_with(q, None)
|
||||
lib.albums.assert_called_once_with(a_q, None)
|
||||
|
||||
m3u_filepath = path.join(dir, pl[0])
|
||||
self.assertTrue(path.exists(m3u_filepath))
|
||||
|
|
@ -177,3 +178,11 @@ class SmartPlaylistCLITest(unittest.TestCase, TestHelper):
|
|||
for name in ('my_playlist.m3u', 'all.m3u'):
|
||||
with open(path.join(self.temp_dir, name), 'r') as f:
|
||||
self.assertEqual(f.read(), self.item.path + b"\n")
|
||||
|
||||
|
||||
def suite():
|
||||
return unittest.TestLoader().loadTestsFromName(__name__)
|
||||
|
||||
|
||||
if __name__ == b'__main__':
|
||||
unittest.main(defaultTest='suite')
|
||||
|
|
|
|||
Loading…
Reference in a new issue