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('The Beatles')
127 >>> player.find_track('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 if artist.name != fuzz_art:
184 artist.add_alias(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)' %
194 #self.log.debug('FZZZ: "%s" does not match "%s"' %
198 self.log.debug('Found: {}'.format('/'.join(artist.names)))
202 def disconnect(self):
203 """Closing client connection with the Player
205 raise NotImplementedError
208 """Connect client to the Player
210 raise NotImplementedError
214 raise NotImplementedError
218 raise NotImplementedError
222 raise NotImplementedError
226 raise NotImplementedError
230 raise NotImplementedError
233 # vim: ai ts=4 sw=4 sts=4 expandtab