Merge lp:~thisfred/autoqueue/lp-777354 into lp:autoqueue
- lp-777354
- Merge into trunk
Proposed by
Eric Casteleijn
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Eric Casteleijn | ||||
Approved revision: | 323 | ||||
Merged at revision: | 322 | ||||
Proposed branch: | lp:~thisfred/autoqueue/lp-777354 | ||||
Merge into: | lp:autoqueue | ||||
Diff against target: |
647 lines (+152/-158) 2 files modified
autoqueue/__init__.py (+3/-7) mpd_autoqueue.py (+149/-151) |
||||
To merge this branch: | bzr merge lp:~thisfred/autoqueue/lp-777354 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Eric Casteleijn | Pending | ||
Review via email: mp+60223@code.launchpad.net |
Commit message
Should fix mpd support
Description of the change
Should fix mpd support
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'autoqueue/__init__.py' |
2 | --- autoqueue/__init__.py 2011-05-06 15:30:42 +0000 |
3 | +++ autoqueue/__init__.py 2011-05-06 17:48:32 +0000 |
4 | @@ -75,10 +75,6 @@ |
5 | """Return filename for the song.""" |
6 | |
7 | @abstractmethod |
8 | - def get_length(self): |
9 | - """Return length in seconds.""" |
10 | - |
11 | - @abstractmethod |
12 | def get_last_started(self): |
13 | """Return the datetime the song was last played.""" |
14 | |
15 | @@ -162,7 +158,7 @@ |
16 | self.running = False |
17 | self.log('Error handler received: %r, %r' % (args, kwargs)) |
18 | |
19 | - def player_get_cache_dir(self): |
20 | + def get_cache_dir(self): |
21 | """Get the directory to store temporary data. |
22 | |
23 | Defaults to $XDG_CACHE_HOME/autoqueue on Gnome. |
24 | @@ -180,7 +176,7 @@ |
25 | def get_blocked_artists_pickle(self): |
26 | """Read the list of blocked artists from disk.""" |
27 | dump = os.path.join( |
28 | - self.player_get_cache_dir(), "autoqueue_block_cache") |
29 | + self.get_cache_dir(), "autoqueue_block_cache") |
30 | try: |
31 | pickle = open(dump, 'r') |
32 | try: |
33 | @@ -206,7 +202,7 @@ |
34 | artist_name, |
35 | len(self._blocked_artists))) |
36 | dump = os.path.join( |
37 | - self.player_get_cache_dir(), "autoqueue_block_cache") |
38 | + self.get_cache_dir(), "autoqueue_block_cache") |
39 | try: |
40 | os.remove(dump) |
41 | except OSError: |
42 | |
43 | === modified file 'mpd_autoqueue.py' |
44 | --- mpd_autoqueue.py 2010-02-28 02:04:38 +0000 |
45 | +++ mpd_autoqueue.py 2011-05-06 17:48:32 +0000 |
46 | @@ -1,161 +1,136 @@ |
47 | #!/usr/bin/env python |
48 | -''' |
49 | -Copyright (c) 2008, Rick van Hattem |
50 | -All rights reserved. |
51 | - |
52 | -Redistribution and use in source and binary forms, with or without |
53 | -modification, are permitted provided that the following conditions are |
54 | -met: |
55 | - |
56 | - * Redistributions of source code must retain the above copyright |
57 | - notice, this list of conditions and the following disclaimer. |
58 | - |
59 | - * Redistributions in binary form must reproduce the above |
60 | - copyright notice, this list of conditions and the following |
61 | - disclaimer in the documentation and/or other materials provided |
62 | - with the distribution. |
63 | - |
64 | - * The names of the contributors may not be used to endorse or |
65 | - promote products derived from this software without specific |
66 | - prior written permission. |
67 | - |
68 | -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
69 | -'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
70 | -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
71 | -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
72 | -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
73 | -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
74 | -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
75 | -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
76 | -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
77 | -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
78 | -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
79 | -''' |
80 | +"""mpd implementation of autoqueue. |
81 | + |
82 | +Copyright (c) 2008-2011 Rick van Hattem, Eric Casteleijn |
83 | +""" |
84 | |
85 | import os |
86 | import sys |
87 | import mpd |
88 | import time |
89 | import errno |
90 | -import atexit |
91 | import socket |
92 | import signal |
93 | import optparse |
94 | import autoqueue |
95 | |
96 | + |
97 | def expand_path(path): |
98 | path = os.path.expandvars(os.path.expanduser(path)) |
99 | if not os.path.isdir(path): |
100 | os.mkdir(path) |
101 | return path |
102 | |
103 | + |
104 | def expand_file(path): |
105 | path, file_ = os.path.split(path) |
106 | return os.path.join(expand_path(path), file_) |
107 | |
108 | -'''The settings path automatically expands ~ to the user home directory |
109 | -and $VAR to environment variables |
110 | -On most *n?x systems ~/.autoqueue will be expanded to /home/username/.autoqueue/ |
111 | -Setting the path to $HOME/.autoqueue/ will usually yield the same result |
112 | -''' |
113 | +# The settings path automatically expands ~ to the user home directory |
114 | +# and $VAR to environment variables On most *n?x systems ~/.autoqueue |
115 | +# will be expanded to /home/username/.autoqueue/ Setting the path to |
116 | +# $HOME/.autoqueue/ will usually yield the same result |
117 | + |
118 | SETTINGS_PATH = '~/.autoqueue/' |
119 | |
120 | -'''These settings will be overwritten by the MPD_HOST/MPD_PORT environment |
121 | -variables and/or by the commandline arguments''' |
122 | +# These settings will be overwritten by the MPD_HOST/MPD_PORT |
123 | +# environment variables and/or by the commandline arguments |
124 | + |
125 | MPD_PORT = 6600 |
126 | MPD_HOST = 'localhost' |
127 | |
128 | # The PID file to see if there are no other instances running |
129 | + |
130 | PID_FILE = os.path.join(expand_path(SETTINGS_PATH), 'mpd.pid') |
131 | |
132 | # Send a KILL signal if the process didn't end KILL_TIMEOUT seconds |
133 | # after the TERM signal, check every KILL_CHECK_DELAY seconds if it's |
134 | # still alive after sending the TERM signal |
135 | + |
136 | KILL_TIMEOUT = 10 |
137 | KILL_CHECK_DELAY = 0.1 |
138 | |
139 | -'''The targets for stdin, stdout and stderr when daemonizing''' |
140 | +# The targets for stdin, stdout and stderr when daemonizing |
141 | + |
142 | STDIN = '/dev/null' |
143 | STDOUT = SETTINGS_PATH + 'stdout' |
144 | STDERR = SETTINGS_PATH + 'stderr' |
145 | |
146 | -'''The interval to refresh the MPD status, the faster this is set, the faster |
147 | -the MPD server will be polled to see if the queue is empty''' |
148 | +# The interval to refresh the MPD status, the faster this is set, the |
149 | +# faster the MPD server will be polled to see if the queue is empty |
150 | + |
151 | REFRESH_INTERVAL = 10 |
152 | |
153 | -'''The desired length for the queue, set this to 0 to add a song for every |
154 | -finished song or to any other number for the number of seconds to keep the |
155 | -queue filled''' |
156 | +# The desired length for the queue, set this to 0 to add a song for |
157 | +# every finished song or to any other number for the number of seconds |
158 | +# to keep the queue filled |
159 | + |
160 | DESIRED_QUEUE_LENGTH = 0 |
161 | |
162 | -'''Make sure we have a little margin before changing the song so the queue |
163 | -won't run empty, keeping this at 15 seconds should be safe |
164 | -Do note that when DESIRED_QUEUE_LENGTH = 0 than this would probably work |
165 | -better with a value of 0''' |
166 | +# Make sure we have a little margin before changing the song so the |
167 | +# queue won't run empty, keeping this at 15 seconds should be safe Do |
168 | +# note that when DESIRED_QUEUE_LENGTH = 0 than this would probably |
169 | +# work better with a value of 0 |
170 | + |
171 | QUEUE_MARGIN = 0 |
172 | |
173 | -'''When MPD is not running, should we launch? |
174 | -And if MPD exits, should we exit?''' |
175 | +# When MPD is not running, should we launch? And if MPD exits, should |
176 | +# we exit? |
177 | + |
178 | EXIT_WITH_MPD = False |
179 | |
180 | + |
181 | class Song(autoqueue.SongBase): |
182 | - '''A MPD song object''' |
183 | - def __init__(self, file=None, time=0, **kwargs): |
184 | + """An MPD song object.""" |
185 | + |
186 | + # pylint: disable=W0231 |
187 | + def __init__(self, song_file=None, song_length=0, **kwargs): |
188 | self.title = self.artist = self.album = '' |
189 | - self._file = file |
190 | - self.time = time |
191 | + self.file = song_file |
192 | + self.time = song_length |
193 | self.__dict__.update(**kwargs) |
194 | + # pylint: enable=W0231 |
195 | |
196 | def get_artist(self): |
197 | - '''return lowercase UNICODE name of artist''' |
198 | + """Return lowercase UNICODE name of artist.""" |
199 | return unicode(self.artist.lower(), 'utf-8') |
200 | |
201 | + def get_artists(self): |
202 | + """Get list of artists and performers for the song.""" |
203 | + return [self.get_artist()] |
204 | + |
205 | def get_title(self): |
206 | - '''return lowercase UNICODE title of song''' |
207 | + """Return lowercase UNICODE title of song.""" |
208 | return unicode(self.title.lower(), 'utf-8') |
209 | |
210 | - def get_album(self): |
211 | - '''return lowercase UNICODE album of song''' |
212 | - return unicode(self.album.lower(), 'utf-8') |
213 | - |
214 | def get_tags(self): |
215 | - '''return a list of tags for the songs''' |
216 | + """Return a list of tags for the song.""" |
217 | return [] |
218 | |
219 | def get_filename(self): |
220 | + """Return filename for the song.""" |
221 | return '/var/lib/mpd/music/' + self.file |
222 | |
223 | - @property |
224 | - def file(self): |
225 | - '''file is an immutable attribute that's used for the hash method''' |
226 | - return self._file |
227 | + def get_last_started(self): |
228 | + """Return the datetime the song was last played.""" |
229 | + return 0 |
230 | + |
231 | + def get_rating(self): |
232 | + """Return the rating of the song.""" |
233 | + return 0 |
234 | + |
235 | + def get_playcount(self): |
236 | + """Return the playcount of the song.""" |
237 | + return 0 |
238 | |
239 | def __repr__(self): |
240 | - return '<%s: %s - %s - %s (%s)>' % ( |
241 | + return '<%s: %s - %s - %s>' % ( |
242 | self.__class__.__name__, |
243 | self.album, |
244 | self.artist, |
245 | self.title, |
246 | - self.duration, |
247 | ) |
248 | |
249 | - def get_time(self): |
250 | - return self._time |
251 | - |
252 | - def set_time(self, value): |
253 | - self._time = int(value) |
254 | - |
255 | - time = property(get_time, set_time, doc='song duration in seconds') |
256 | - |
257 | - @property |
258 | - def duration(self): |
259 | - t = time.gmtime(int(self.time)) |
260 | - if t.tm_hour: |
261 | - fmt = '%H:%M:%S' |
262 | - else: |
263 | - fmt = '%M:%S' |
264 | - return time.strftime(fmt, t) |
265 | - |
266 | def __int__(self): |
267 | return self.time |
268 | |
269 | @@ -177,9 +152,10 @@ |
270 | def __ne__(self, other): |
271 | return hash(self) != hash(other) |
272 | |
273 | + |
274 | class Search(object): |
275 | - ''' |
276 | - Search object which keeps track of all search parameters |
277 | + """ |
278 | + Search object which keeps track of all search parameters. |
279 | |
280 | ALLOWED_FIELDS - list of allowed search fields |
281 | |
282 | @@ -192,13 +168,13 @@ |
283 | >>> search.add_parameters(any='test3', album='test4') |
284 | >>> search.get_parameters() |
285 | ['album', 'test4', 'title', 'test2', 'any', 'test3', 'artist', 'test'] |
286 | - ''' |
287 | + """ |
288 | ALLOWED_FIELDS = ('artist', 'album', 'title', 'track', 'name', 'genre', |
289 | 'date', 'composer', 'performer', 'comment', 'disc', 'filename', 'any') |
290 | |
291 | def __init__(self, field=None, value=None, **parameters): |
292 | - ''' |
293 | - Create a search object |
294 | + """ |
295 | + Create a search object. |
296 | |
297 | >>> search = Search(artist='test') |
298 | >>> search.parameters |
299 | @@ -211,15 +187,15 @@ |
300 | >>> search = Search('artist', 'test') |
301 | >>> search.parameters |
302 | {'artist': set(['test'])} |
303 | - ''' |
304 | + """ |
305 | self.parameters = {} |
306 | if field and value: |
307 | self.add_parameter(field, value) |
308 | self.add_parameters(**parameters) |
309 | |
310 | def add_parameters(self, **parameters): |
311 | - ''' |
312 | - Add one or more parameters to the search query |
313 | + """ |
314 | + Add one or more parameters to the search query. |
315 | |
316 | Use with named arguments, the key must be in ALLOWED_FIELDS |
317 | |
318 | @@ -227,12 +203,12 @@ |
319 | >>> search.add_parameters(artist='test1', title='test2') |
320 | >>> search.parameters |
321 | {'artist': set(['test1']), 'title': set(['test2'])} |
322 | - ''' |
323 | + """ |
324 | [self.add_parameter(k, v) for k, v in parameters.iteritems()] |
325 | |
326 | def add_parameter(self, field, value): |
327 | - ''' |
328 | - Add a parameter to the search query |
329 | + """ |
330 | + Add a parameter to the search query. |
331 | |
332 | field - must be in ALLOWED_FIELDS |
333 | value - a literal string to be searched for |
334 | @@ -247,7 +223,7 @@ |
335 | Traceback (most recent call last): |
336 | ... |
337 | TypeError: "spam" is not a valid field, please choose on from ALLOWED_FIELDS |
338 | - ''' |
339 | + """ |
340 | if field in self.ALLOWED_FIELDS: |
341 | self.parameters.setdefault(field, set()).add(value.lower().strip()) |
342 | else: |
343 | @@ -255,8 +231,8 @@ |
344 | 'on from ALLOWED_FIELDS' % field |
345 | |
346 | def get_parameters(self): |
347 | - ''' |
348 | - Return a list of parameters for the MPDClient.search method |
349 | + """ |
350 | + Return a list of parameters for the MPDClient.search method. |
351 | |
352 | >>> search = Search(artist='test1', title='test2') |
353 | >>> search.get_parameters() |
354 | @@ -268,7 +244,7 @@ |
355 | Traceback (most recent call last): |
356 | ... |
357 | ValueError: Empty search queries are not allowed |
358 | - ''' |
359 | + """ |
360 | ret = [] |
361 | for k, vs in self.parameters.iteritems(): |
362 | ret += [[k, v.encode('utf-8')] for v in vs] |
363 | @@ -277,47 +253,56 @@ |
364 | raise ValueError, 'Empty search queries are not allowed' |
365 | return sum(ret, []) |
366 | |
367 | + |
368 | class Daemon(object): |
369 | - '''Class to easily create a daemon which transparently handles the saving |
370 | - and removing of the PID file''' |
371 | + """ |
372 | + Class to easily create a daemon which transparently handles the |
373 | + saving and removing of the PID file. |
374 | + |
375 | + """ |
376 | |
377 | def __init__(self, pid_file): |
378 | - '''Create a new Daemon |
379 | + """Create a new Daemon. |
380 | |
381 | pid_file -- The file to save the PID in |
382 | - ''' |
383 | + """ |
384 | + self._pid_file = None |
385 | self.pid_file = pid_file |
386 | signal.signal(signal.SIGTERM, lambda *args: self.exit()) |
387 | signal.signal(signal.SIGINT, lambda *args: self.exit()) |
388 | signal.signal(signal.SIGQUIT, lambda *args: self.exit()) |
389 | |
390 | def set_pid_file(self, pid_file): |
391 | + """Set pid file.""" |
392 | self._pid_file = pid_file |
393 | open(pid_file, 'w').write('%d' % os.getpid()) |
394 | |
395 | def get_pid_file(self): |
396 | + """Get pid file.""" |
397 | return self._pid_file |
398 | |
399 | def del_pid_file(self): |
400 | + """Delete pid file.""" |
401 | try: |
402 | os.unlink(self._pid_file) |
403 | - print >>sys.stderr, 'Removed PID file' |
404 | + print >> sys.stderr, 'Removed PID file' |
405 | except OSError: |
406 | - print >>sys.stderr, 'Trying to remove non-existing PID file' |
407 | + print >> sys.stderr, 'Trying to remove non-existing PID file' |
408 | |
409 | pid_file = property(get_pid_file, set_pid_file, del_pid_file, |
410 | 'The PID file will be written when set and deleted when unset') |
411 | |
412 | def exit(self): |
413 | - '''Kill the daemon and remove the PID file |
414 | + """Kill the daemon and remove the PID file |
415 | |
416 | This method will be called automatically when the process is |
417 | - terminated''' |
418 | + terminated""" |
419 | del self.pid_file |
420 | raise SystemExit |
421 | |
422 | @classmethod |
423 | def is_running(cls, pid): |
424 | + """Check whether we're already running.'""" |
425 | if not pid: |
426 | return False |
427 | try: |
428 | @@ -328,13 +313,14 @@ |
429 | |
430 | @classmethod |
431 | def kill(cls, pid, pid_file=None): |
432 | + """Kill process.""" |
433 | try: |
434 | pid = int(pid) |
435 | except (TypeError, ValueError): |
436 | pid = None |
437 | |
438 | if cls.is_running(pid): |
439 | - print >>sys.stderr, 'Sending TERM signal to process %d' % pid |
440 | + print >> sys.stderr, 'Sending TERM signal to process %d' % pid |
441 | os.kill(pid, signal.SIGTERM) |
442 | i = KILL_TIMEOUT |
443 | while i > 0 and cls.is_running(pid): |
444 | @@ -342,23 +328,26 @@ |
445 | time.sleep(KILL_CHECK_DELAY) |
446 | |
447 | if cls.is_running(pid): |
448 | - print >>sys.stderr, 'Sending KILL signal to process %d' % pid |
449 | + print >> sys.stderr, 'Sending KILL signal to process %d' % pid |
450 | os.kill(pid, signal.SIGKILL) |
451 | |
452 | time.sleep(1) |
453 | if cls.is_running(pid): |
454 | - print >>sys.stderr, 'Unable to kill process %d, still running' |
455 | + print >> sys.stderr, 'Unable to kill process %d, still running' |
456 | else: |
457 | if isinstance(pid, int): |
458 | - print >>sys.stderr, 'Process %d not running' % pid |
459 | + print >> sys.stderr, 'Process %d not running' % pid |
460 | if pid_file: |
461 | - print >>sys.stderr, 'Removing stale PID file' |
462 | + print >> sys.stderr, 'Removing stale PID file' |
463 | os.unlink(pid_file) |
464 | |
465 | @classmethod |
466 | def daemonize(cls): |
467 | - '''Daemonize using the double fork method so the process keeps running |
468 | - Even after the original shell exits''' |
469 | + """ |
470 | + Daemonize using the double fork method so the process keeps |
471 | + running Even after the original shell exits. |
472 | + |
473 | + """ |
474 | stdin_file = expand_file(STDIN) |
475 | stdout_file = expand_file(STDOUT) |
476 | stderr_file = expand_file(STDERR) |
477 | @@ -367,7 +356,7 @@ |
478 | if pid > 0: |
479 | sys.exit(0) |
480 | except OSError, e: |
481 | - print >>sys.stderr, 'Unable to fork: %s' % e |
482 | + print >> sys.stderr, 'Unable to fork: %s' % e |
483 | sys.exit(1) |
484 | |
485 | os.chdir('/') |
486 | @@ -379,10 +368,10 @@ |
487 | if pid > 0: |
488 | sys.exit(0) |
489 | except OSError, e: |
490 | - print >>sys.stderr, 'Unable to fork: %s' % e |
491 | + print >> sys.stderr, 'Unable to fork: %s' % e |
492 | sys.exit(1) |
493 | |
494 | - '''Redirect stdout, stderr and stdin''' |
495 | + # Redirect stdout, stderr and stdin |
496 | stdin = file(stdin_file, 'r') |
497 | stdout = file(stdout_file, 'a+') |
498 | stderr = file(stderr_file, 'a+', 0) |
499 | @@ -390,20 +379,23 @@ |
500 | os.dup2(stdout.fileno(), sys.stdout.fileno()) |
501 | os.dup2(stderr.fileno(), sys.stderr.fileno()) |
502 | |
503 | + |
504 | class AutoQueuePlugin(autoqueue.AutoQueueBase, Daemon): |
505 | + """Mpd implementation of autoqueue.""" |
506 | + |
507 | def __init__(self, host, port, pid_file): |
508 | self.host, self.port = host, port |
509 | self.client = mpd.MPDClient() |
510 | self.connect() |
511 | autoqueue.AutoQueueBase.__init__(self) |
512 | - self.random = True |
513 | self.verbose = True |
514 | - self.by_mirage = True |
515 | self.desired_queue_length = DESIRED_QUEUE_LENGTH |
516 | Daemon.__init__(self, pid_file) |
517 | self._current_song = None |
518 | + self._host = None |
519 | |
520 | def run(self): |
521 | + """Run.""" |
522 | running = True |
523 | while running or not EXIT_WITH_MPD: |
524 | interval = REFRESH_INTERVAL |
525 | @@ -416,8 +408,7 @@ |
526 | |
527 | interval = min( |
528 | REFRESH_INTERVAL, |
529 | - int(self.player_get_queue_length()) - QUEUE_MARGIN |
530 | - ) |
531 | + int(self.player_get_queue_length()) - QUEUE_MARGIN) |
532 | running = True |
533 | except mpd.ConnectionError: |
534 | print "disconnecting" |
535 | @@ -432,6 +423,7 @@ |
536 | self.exit() |
537 | |
538 | def connect(self): |
539 | + """Connect.""" |
540 | try: |
541 | self.client.connect(self.host, self.port) |
542 | return True |
543 | @@ -439,6 +431,7 @@ |
544 | return False |
545 | |
546 | def disconnect(self): |
547 | + """Disconnect.""" |
548 | try: |
549 | self.client.disconnect() |
550 | return True |
551 | @@ -446,56 +439,61 @@ |
552 | return False |
553 | |
554 | def get_host(self): |
555 | + """Get host.""" |
556 | return self._host |
557 | |
558 | def set_host(self, host): |
559 | + """Set host.""" |
560 | self._host = socket.gethostbyname(host) |
561 | |
562 | - host = property(get_host, set_host, doc='MPD ip (can be set by ip and hostname)') |
563 | + host = property( |
564 | + get_host, set_host, doc='MPD ip (can be set by ip and hostname)') |
565 | |
566 | - def player_construct_track_search(self, artist, title, restrictions): |
567 | - '''construct a search that looks for songs with this artist |
568 | - and title''' |
569 | + def player_construct_track_search(self, artist, title, restrictions=None): |
570 | + """Construct a search that looks for songs with this artist |
571 | + and title.""" |
572 | return Search(artist=artist, title=title) |
573 | |
574 | - def player_construct_tag_search(self, tags, exclude_artists, restrictions): |
575 | - '''construct a search that looks for songs with these tags''' |
576 | + def player_construct_tag_search(self, tags, exclude_artists, |
577 | + restrictions=None): |
578 | + """Construct a search that looks for songs with these tags.""" |
579 | return None |
580 | |
581 | - def player_construct_artist_search(self, artist, restrictions): |
582 | - '''construct a search that looks for songs with this artist''' |
583 | + def player_construct_artist_search(self, artist, restrictions=None): |
584 | + """Construct a search that looks for songs with this artist.""" |
585 | return Search(artist=artist) |
586 | |
587 | - def player_construct_restrictions( |
588 | - self, track_block_time, relaxors, restrictors): |
589 | - '''construct a search to further modify the searches''' |
590 | - return None |
591 | + def player_set_variables_from_config(self): |
592 | + """Initialize user settings from the configuration storage.""" |
593 | + pass |
594 | |
595 | def player_search(self, search): |
596 | - '''perform a player search''' |
597 | + """perform a player search""" |
598 | |
599 | results = self.client.search(*search.get_parameters()) |
600 | |
601 | - '''Make all search results lowercase and strip whitespace''' |
602 | - for r in results: |
603 | - for k, v in r.items(): |
604 | - if isinstance(v, basestring): |
605 | - r['%s_search' % k] = unicode(v, 'utf-8').strip().lower() |
606 | - |
607 | - '''Filter all non-exact matches''' |
608 | - for k, vs in search.parameters.iteritems(): |
609 | - for v in vs: |
610 | - results = [r for r in results if r.get('%s_search' % k) == v] |
611 | - |
612 | - '''Convert all rows to song objects''' |
613 | + # Make all search results lowercase and strip whitespace |
614 | + for result in results: |
615 | + for key, value in result.items(): |
616 | + if isinstance(value, basestring): |
617 | + result['%s_search' % key] = unicode( |
618 | + value, 'utf-8').strip().lower() |
619 | + |
620 | + # Filter all non-exact matches |
621 | + for key, values in search.parameters.iteritems(): |
622 | + for value in values: |
623 | + results = [ |
624 | + r for r in results if r.get('%s_search' % key) == value] |
625 | + |
626 | + # Convert all rows to song objects |
627 | return [Song(**x) for x in results] |
628 | |
629 | def player_enqueue(self, song): |
630 | - '''Put the song at the end of the queue''' |
631 | + """Put the song at the end of the queue""" |
632 | self.client.add(song.file) |
633 | |
634 | def player_get_userdir(self): |
635 | - '''get the application user directory to store files''' |
636 | + """get the application user directory to store files""" |
637 | return expand_path(SETTINGS_PATH) |
638 | |
639 | def player_current_song(self): |
640 | @@ -511,7 +509,7 @@ |
641 | return (Song(**x) for x in self.client.playlistid()) |
642 | |
643 | def player_get_songs_in_queue(self): |
644 | - '''return (wrapped) song objects for the songs in the queue''' |
645 | + """return (wrapped) song objects for the songs in the queue""" |
646 | id = self.player_current_song_id() |
647 | return [s for i, s in enumerate(self.player_playlist()) if i >= id] |
648 |