1 # -*- coding: utf-8 -*-
2 # Copyright (c) 2009-2014 Jack Kaliko <jack@azylum.org>
4 # This file is part of sima
6 # sima is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
11 # sima is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with sima. If not, see <http://www.gnu.org/licenses/>.
22 # Add decorator to filter through history?
24 # standard library import
26 from difflib import get_close_matches
27 from itertools import dropwhile
30 from .meta import Artist
31 from .simastr import SimaStr
32 from ..utils.leven import levenshtein_ratio
34 def blacklist(artist=False, album=False, track=False):
35 #pylint: disable=C0111,W0212
36 field = (album, track)
38 def wrapper(*args, **kwargs):
39 if not args[0].database:
40 return func(*args, **kwargs)
42 boolgen = (bl for bl in field)
43 bl_fun = (cls.database.get_bl_album,
44 cls.database.get_bl_track,)
45 #bl_getter = next(fn for fn, bl in zip(bl_fun, boolgen) if bl is True)
46 bl_getter = next(dropwhile(lambda _: not next(boolgen), bl_fun))
47 #cls.log.debug('using {0} as bl filter'.format(bl_getter.__name__))
49 for elem in func(*args, **kwargs):
50 if bl_getter(elem, add_not=True):
51 #cls.log.debug('Blacklisted "{0}"'.format(elem))
53 if track and cls.database.get_bl_album(elem, add_not=True):
54 # filter album as well in track mode
55 # (artist have already been)
56 cls.log.debug('Blacklisted alb. "{0.album}"'.format(elem))
64 def wrapper(*args, **kwargs):
66 if not args[0].database:
67 return func(*args, **kwargs)
68 result = func(*args, **kwargs)
72 for art in result.names:
73 if cls.database.get_bl_artist(art, add_not=True):
74 cls.log.debug('Blacklisted "{0}"'.format(art))
79 resp = Artist(name=names.pop(), mbid=result.mbid)
87 """Player interface to inherit from.
89 When querying player music library for tracks, Player instance *must* return
90 Track objects (usually a list of them)
92 Player instance should expose the following immutable attributes:
102 self.log = logging.getLogger('sima')
105 """Monitor player for change
107 * database player media library has changed
108 * playlist playlist modified
109 * options player options changed: repeat mode, etc…
110 * player player state changed: paused, stopped, skip track…
112 raise NotImplementedError
115 """Any cleanup necessary"""
118 def remove(self, position=0):
119 """Removes the oldest element of the playlist (index 0)
121 raise NotImplementedError
123 def find_track(self, artist, title=None):
125 Find tracks for a specific artist or filtering with a track title
126 >>> player.find_track(Artist('The Beatles'))
127 >>> player.find_track(Artist('Nirvana'), title='Smells Like Teen Spirit')
129 Returns a list of Track objects
131 raise NotImplementedError
133 def find_album(self, artist, album):
135 Find tracks by track's album name
136 >>> player.find_album('Nirvana', 'Nevermind')
138 Returns a list of Track objects
140 raise NotImplementedError
142 def search_albums(self, artist):
144 Find albums by artist's name
145 >>> art = Artist(name='Nirvana')
146 >>> player.search_albums(art)
148 Returns a list of string objects
150 raise NotImplementedError
153 def search_artist(self, artist):
155 Search artists based on a fuzzy search in the media library
156 >>> bea = player.search_artist('The beatles')
158 >>> ['The Beatles', 'Beatles', 'the beatles']
160 Returns a list of strings (artist names)
163 # Then proceed with fuzzy matching if got nothing
164 match = get_close_matches(artist.name, self.artists, 50, 0.73)
168 self.log.debug('found close match for "%s": %s' %
169 (artist, '/'.join(match)))
170 # Does not perform fuzzy matching on short and single word strings
171 # Only lowercased comparison
172 if ' ' not in artist.name and len(artist.name) < 8:
173 for fuzz_art in match:
174 # Regular lowered string comparison
175 if artist.name.lower() == fuzz_art.lower():
176 artist.add_alias(fuzz_art)
178 fzartist = SimaStr(artist.name)
179 for fuzz_art in match:
180 # Regular lowered string comparison
181 if artist.name.lower() == fuzz_art.lower():
183 artist.add_alias(fuzz_art)
184 if artist.name != fuzz_art:
185 self.log.debug('"%s" matches "%s".' % (fuzz_art, artist))
187 # SimaStr string __eq__ (not regular string comparison here)
188 if fzartist == fuzz_art:
190 artist.add_alias(fuzz_art)
191 self.log.info('"%s" quite probably matches "%s" (SimaStr)' %
195 self.log.debug('Found: {}'.format('/'.join(artist.names)))
199 def disconnect(self):
200 """Closing client connection with the Player
202 raise NotImplementedError
205 """Connect client to the Player
207 raise NotImplementedError
211 raise NotImplementedError
215 raise NotImplementedError
219 raise NotImplementedError
223 raise NotImplementedError
227 raise NotImplementedError
230 # vim: ai ts=4 sw=4 sts=4 expandtab