Merge lp:~thisfred/autoqueue/lp-777351 into lp:autoqueue
- lp-777351
- Merge into trunk
Proposed by
Eric Casteleijn
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Eric Casteleijn | ||||
Approved revision: | 322 | ||||
Merged at revision: | 321 | ||||
Proposed branch: | lp:~thisfred/autoqueue/lp-777351 | ||||
Merge into: | lp:autoqueue | ||||
Diff against target: |
305 lines (+62/-45) 3 files modified
autoqueue/__init__.py (+25/-34) autoqueue/tests/test_autoqueue.py (+4/-0) rhythmbox_autoqueue/__init__.py (+33/-11) |
||||
To merge this branch: | bzr merge lp:~thisfred/autoqueue/lp-777351 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Eric Casteleijn | Pending | ||
Review via email: mp+60205@code.launchpad.net |
Commit message
fixed rhythmbox support
Description of the change
fixed rhythmbox 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 00:03:33 +0000 | |||
3 | +++ autoqueue/__init__.py 2011-05-06 15:33:27 +0000 | |||
4 | @@ -24,6 +24,7 @@ | |||
5 | 24 | import os | 24 | import os |
6 | 25 | import random | 25 | import random |
7 | 26 | 26 | ||
8 | 27 | from abc import ABCMeta, abstractmethod | ||
9 | 27 | from dbus.mainloop.glib import DBusGMainLoop | 28 | from dbus.mainloop.glib import DBusGMainLoop |
10 | 28 | from collections import deque | 29 | from collections import deque |
11 | 29 | from datetime import datetime, timedelta | 30 | from datetime import datetime, timedelta |
12 | @@ -45,51 +46,49 @@ | |||
13 | 45 | class SongBase(object): | 46 | class SongBase(object): |
14 | 46 | """A wrapper object around player specific song objects.""" | 47 | """A wrapper object around player specific song objects.""" |
15 | 47 | 48 | ||
16 | 49 | __metaclass__ = ABCMeta | ||
17 | 50 | |||
18 | 48 | def __init__(self, song): | 51 | def __init__(self, song): |
19 | 49 | self.song = song | 52 | self.song = song |
20 | 50 | 53 | ||
21 | 51 | def __str__(self): | 54 | def __str__(self): |
22 | 52 | return "<Song: %s - %s>" % (self.get_artist(), self.get_title()) | 55 | return "<Song: %s - %s>" % (self.get_artist(), self.get_title()) |
23 | 53 | 56 | ||
24 | 57 | @abstractmethod | ||
25 | 54 | def get_artist(self): | 58 | def get_artist(self): |
26 | 55 | """Return lowercase UNICODE name of artist.""" | 59 | """Return lowercase UNICODE name of artist.""" |
27 | 56 | return NotImplemented | ||
28 | 57 | 60 | ||
29 | 61 | @abstractmethod | ||
30 | 58 | def get_artists(self): | 62 | def get_artists(self): |
31 | 59 | """Return lowercase UNICODE name of artists and performers.""" | 63 | """Return lowercase UNICODE name of artists and performers.""" |
32 | 60 | return NotImplemented | ||
33 | 61 | 64 | ||
34 | 65 | @abstractmethod | ||
35 | 62 | def get_title(self): | 66 | def get_title(self): |
36 | 63 | """Return lowercase UNICODE title of song.""" | 67 | """Return lowercase UNICODE title of song.""" |
37 | 64 | return NotImplemented | ||
38 | 65 | 68 | ||
39 | 69 | @abstractmethod | ||
40 | 66 | def get_tags(self): | 70 | def get_tags(self): |
41 | 67 | """Return a list of tags for the song.""" | 71 | """Return a list of tags for the song.""" |
42 | 68 | return [] | ||
43 | 69 | 72 | ||
44 | 73 | @abstractmethod | ||
45 | 70 | def get_filename(self): | 74 | def get_filename(self): |
46 | 71 | """Return filename for the song.""" | 75 | """Return filename for the song.""" |
47 | 72 | return NotImplemented | ||
48 | 73 | 76 | ||
49 | 77 | @abstractmethod | ||
50 | 74 | def get_length(self): | 78 | def get_length(self): |
51 | 75 | """Return length in seconds.""" | 79 | """Return length in seconds.""" |
52 | 76 | return NotImplemented | ||
53 | 77 | 80 | ||
54 | 81 | @abstractmethod | ||
55 | 78 | def get_last_started(self): | 82 | def get_last_started(self): |
56 | 79 | """Return the datetime the song was last played.""" | 83 | """Return the datetime the song was last played.""" |
57 | 80 | return NotImplemented | ||
58 | 81 | 84 | ||
59 | 85 | @abstractmethod | ||
60 | 82 | def get_rating(self): | 86 | def get_rating(self): |
61 | 83 | """Return the rating of the song.""" | 87 | """Return the rating of the song.""" |
62 | 84 | return NotImplemented | ||
63 | 85 | 88 | ||
64 | 89 | @abstractmethod | ||
65 | 86 | def get_playcount(self): | 90 | def get_playcount(self): |
66 | 87 | """Return the playcount of the song.""" | 91 | """Return the playcount of the song.""" |
67 | 88 | return NotImplemented | ||
68 | 89 | |||
69 | 90 | def get_added(self): | ||
70 | 91 | """Return the datetime the song was added to the library.""" | ||
71 | 92 | return NotImplemented | ||
72 | 93 | 92 | ||
73 | 94 | def get_play_frequency(self): | 93 | def get_play_frequency(self): |
74 | 95 | """Return the play frequency of the song (plays / day).""" | 94 | """Return the play frequency of the song (plays / day).""" |
75 | @@ -124,27 +123,19 @@ | |||
76 | 124 | class AutoQueueBase(object): | 123 | class AutoQueueBase(object): |
77 | 125 | """Generic base class for autoqueue plugins.""" | 124 | """Generic base class for autoqueue plugins.""" |
78 | 126 | 125 | ||
79 | 126 | __metaclass__ = ABCMeta | ||
80 | 127 | |||
81 | 127 | def __init__(self): | 128 | def __init__(self): |
82 | 128 | self.artist_block_time = 1 | 129 | self.artist_block_time = 1 |
83 | 129 | self._blocked_artists = deque([]) | 130 | self._blocked_artists = deque([]) |
84 | 130 | self._blocked_artists_times = deque([]) | 131 | self._blocked_artists_times = deque([]) |
85 | 131 | self._cache_dir = None | 132 | self._cache_dir = None |
87 | 132 | self.desired_queue_length = 0 | 133 | self.desired_queue_length = 15 * 60 |
88 | 133 | self.cached_misses = deque([]) | 134 | self.cached_misses = deque([]) |
89 | 134 | self.by_mirage = False | ||
90 | 135 | self.by_tracks = True | ||
91 | 136 | self.by_artists = True | ||
92 | 137 | self.by_tags = True | ||
93 | 138 | self.running = False | 135 | self.running = False |
94 | 139 | self.verbose = False | 136 | self.verbose = False |
95 | 140 | self.weed = False | ||
96 | 141 | self.song = None | 137 | self.song = None |
97 | 142 | self.restrictions = None | 138 | self.restrictions = None |
98 | 143 | self.prune_artists = [] | ||
99 | 144 | self.prune_titles = [] | ||
100 | 145 | self.prune_filenames = [] | ||
101 | 146 | self._rows = [] | ||
102 | 147 | self._nrows = [] | ||
103 | 148 | self.player_set_variables_from_config() | 139 | self.player_set_variables_from_config() |
104 | 149 | self.get_blocked_artists_pickle() | 140 | self.get_blocked_artists_pickle() |
105 | 150 | self.last_songs = [] | 141 | self.last_songs = [] |
106 | @@ -249,45 +240,45 @@ | |||
107 | 249 | song.get_filename() for song in self.player_search(search)]) | 240 | song.get_filename() for song in self.player_search(search)]) |
108 | 250 | return filenames | 241 | return filenames |
109 | 251 | 242 | ||
110 | 243 | @abstractmethod | ||
111 | 252 | def player_construct_file_search(self, filename, restrictions=None): | 244 | def player_construct_file_search(self, filename, restrictions=None): |
112 | 253 | """Construct a search that looks for songs with this artist and title. | 245 | """Construct a search that looks for songs with this artist and title. |
113 | 254 | 246 | ||
114 | 255 | """ | 247 | """ |
115 | 256 | return NotImplemented | ||
116 | 257 | 248 | ||
117 | 249 | @abstractmethod | ||
118 | 258 | def player_construct_track_search(self, artist, title, restrictions=None): | 250 | def player_construct_track_search(self, artist, title, restrictions=None): |
119 | 259 | """Construct a search that looks for songs with this artist | 251 | """Construct a search that looks for songs with this artist |
120 | 260 | and title. | 252 | and title. |
121 | 261 | """ | 253 | """ |
122 | 262 | return NotImplemented | ||
123 | 263 | 254 | ||
124 | 255 | @abstractmethod | ||
125 | 264 | def player_construct_artist_search(self, artist, restrictions=None): | 256 | def player_construct_artist_search(self, artist, restrictions=None): |
126 | 265 | """Construct a search that looks for songs with this artist.""" | 257 | """Construct a search that looks for songs with this artist.""" |
127 | 266 | return NotImplemented | ||
128 | 267 | 258 | ||
129 | 259 | @abstractmethod | ||
130 | 268 | def player_construct_tag_search(self, tags, restrictions=None): | 260 | def player_construct_tag_search(self, tags, restrictions=None): |
131 | 269 | """Construct a search that looks for songs with these tags.""" | 261 | """Construct a search that looks for songs with these tags.""" |
132 | 270 | return NotImplemented | ||
133 | 271 | 262 | ||
134 | 263 | @abstractmethod | ||
135 | 272 | def player_set_variables_from_config(self): | 264 | def player_set_variables_from_config(self): |
136 | 273 | """Initialize user settings from the configuration storage.""" | 265 | """Initialize user settings from the configuration storage.""" |
137 | 274 | return NotImplemented | ||
138 | 275 | 266 | ||
139 | 267 | @abstractmethod | ||
140 | 276 | def player_get_queue_length(self): | 268 | def player_get_queue_length(self): |
141 | 277 | """Get the current length of the queue.""" | 269 | """Get the current length of the queue.""" |
142 | 278 | return 0 | ||
143 | 279 | 270 | ||
144 | 271 | @abstractmethod | ||
145 | 280 | def player_enqueue(self, song): | 272 | def player_enqueue(self, song): |
146 | 281 | """Put the song at the end of the queue.""" | 273 | """Put the song at the end of the queue.""" |
147 | 282 | return NotImplemented | ||
148 | 283 | 274 | ||
149 | 275 | @abstractmethod | ||
150 | 284 | def player_search(self, search): | 276 | def player_search(self, search): |
151 | 285 | """Perform a player search.""" | 277 | """Perform a player search.""" |
152 | 286 | return NotImplemented | ||
153 | 287 | 278 | ||
154 | 279 | @abstractmethod | ||
155 | 288 | def player_get_songs_in_queue(self): | 280 | def player_get_songs_in_queue(self): |
156 | 289 | """Return (wrapped) song objects for the songs in the queue.""" | 281 | """Return (wrapped) song objects for the songs in the queue.""" |
157 | 290 | return [] | ||
158 | 291 | 282 | ||
159 | 292 | def player_execute_async(self, method, *args, **kwargs): | 283 | def player_execute_async(self, method, *args, **kwargs): |
160 | 293 | """Override this if the player has a way to execute methods | 284 | """Override this if the player has a way to execute methods |
161 | @@ -418,7 +409,7 @@ | |||
162 | 418 | return song | 409 | return song |
163 | 419 | self.cached_misses.append((artist, title, filename, tags)) | 410 | self.cached_misses.append((artist, title, filename, tags)) |
164 | 420 | while len(self.cached_misses) > 1000: | 411 | while len(self.cached_misses) > 1000: |
166 | 421 | print self.cached_misses.popleft() | 412 | self.cached_misses.popleft() |
167 | 422 | 413 | ||
168 | 423 | def fill_queue(self): | 414 | def fill_queue(self): |
169 | 424 | """Search for appropriate songs and put them in the queue.""" | 415 | """Search for appropriate songs and put them in the queue.""" |
170 | 425 | 416 | ||
171 | === modified file 'autoqueue/tests/test_autoqueue.py' | |||
172 | --- autoqueue/tests/test_autoqueue.py 2011-05-06 00:03:33 +0000 | |||
173 | +++ autoqueue/tests/test_autoqueue.py 2011-05-06 15:33:27 +0000 | |||
174 | @@ -213,6 +213,10 @@ | |||
175 | 213 | artist_name, | 213 | artist_name, |
176 | 214 | len(self._blocked_artists))) | 214 | len(self._blocked_artists))) |
177 | 215 | 215 | ||
178 | 216 | def player_set_variables_from_config(self): | ||
179 | 217 | """Set configuration variables.""" | ||
180 | 218 | pass | ||
181 | 219 | |||
182 | 216 | def player_construct_file_search(self, filename, restrictions=None): | 220 | def player_construct_file_search(self, filename, restrictions=None): |
183 | 217 | """Construct a search that looks for songs with this artist | 221 | """Construct a search that looks for songs with this artist |
184 | 218 | and title. | 222 | and title. |
185 | 219 | 223 | ||
186 | === modified file 'rhythmbox_autoqueue/__init__.py' | |||
187 | --- rhythmbox_autoqueue/__init__.py 2010-08-12 12:20:37 +0000 | |||
188 | +++ rhythmbox_autoqueue/__init__.py 2011-05-06 15:33:27 +0000 | |||
189 | @@ -1,3 +1,5 @@ | |||
190 | 1 | """Rhythmbox version of the autoqueue plugin.""" | ||
191 | 2 | |||
192 | 1 | # Copyright (C) 2007-2008 - Eric Casteleijn, Alexandre Rosenfeld | 3 | # Copyright (C) 2007-2008 - Eric Casteleijn, Alexandre Rosenfeld |
193 | 2 | # | 4 | # |
194 | 3 | # This program is free software; you can redistribute it and/or modify | 5 | # This program is free software; you can redistribute it and/or modify |
195 | @@ -16,24 +18,29 @@ | |||
196 | 16 | 18 | ||
197 | 17 | import urllib | 19 | import urllib |
198 | 18 | from time import time | 20 | from time import time |
200 | 19 | import gconf, gobject | 21 | import gconf |
201 | 22 | import gobject | ||
202 | 20 | from gtk import gdk | 23 | from gtk import gdk |
204 | 21 | import rb, rhythmdb | 24 | import rb |
205 | 25 | import rhythmdb | ||
206 | 22 | from collections import deque | 26 | from collections import deque |
207 | 23 | from autoqueue import AutoQueueBase, SongBase | 27 | from autoqueue import AutoQueueBase, SongBase |
208 | 24 | 28 | ||
209 | 25 | GCONFPATH = '/apps/rhythmbox/plugins/autoqueue/' | 29 | GCONFPATH = '/apps/rhythmbox/plugins/autoqueue/' |
210 | 26 | 30 | ||
211 | 31 | |||
212 | 27 | class Song(SongBase): | 32 | class Song(SongBase): |
213 | 28 | """A wrapper object around rhythmbox song objects.""" | 33 | """A wrapper object around rhythmbox song objects.""" |
215 | 29 | def __init__(self, song, db): | 34 | |
216 | 35 | def __init__(self, song, db): # pylint: disable=W0231 | ||
217 | 30 | self.song = song | 36 | self.song = song |
218 | 31 | self.db = db | 37 | self.db = db |
219 | 32 | 38 | ||
220 | 33 | def get_artist(self): | 39 | def get_artist(self): |
221 | 34 | """return lowercase UNICODE name of artist""" | 40 | """return lowercase UNICODE name of artist""" |
222 | 35 | return unicode( | 41 | return unicode( |
224 | 36 | self.db.entry_get(self.song, rhythmdb.PROP_ARTIST).lower(), 'utf-8') | 42 | self.db.entry_get(self.song, rhythmdb.PROP_ARTIST).lower(), |
225 | 43 | 'utf-8') | ||
226 | 37 | 44 | ||
227 | 38 | def get_artists(self): | 45 | def get_artists(self): |
228 | 39 | return [self.get_artist()] | 46 | return [self.get_artist()] |
229 | @@ -59,8 +66,18 @@ | |||
230 | 59 | def get_last_started(self): | 66 | def get_last_started(self): |
231 | 60 | return self.db.entry_get(self.song, rhythmdb.PROP_LAST_PLAYED) | 67 | return self.db.entry_get(self.song, rhythmdb.PROP_LAST_PLAYED) |
232 | 61 | 68 | ||
233 | 69 | def get_rating(self): | ||
234 | 70 | """Return the rating of the song.""" | ||
235 | 71 | return 5.0 / self.db.entry_get(self.song, rhythmdb.PROP_RATING) | ||
236 | 72 | |||
237 | 73 | def get_playcount(self): | ||
238 | 74 | """Return the playcount of the song.""" | ||
239 | 75 | return self.db.entry_get(self.song, rhythmdb.PROP_PLAY_COUNT) | ||
240 | 76 | |||
241 | 62 | 77 | ||
242 | 63 | class AutoQueuePlugin(rb.Plugin, AutoQueueBase): | 78 | class AutoQueuePlugin(rb.Plugin, AutoQueueBase): |
243 | 79 | """Plugin implementation.""" | ||
244 | 80 | |||
245 | 64 | def __init__(self): | 81 | def __init__(self): |
246 | 65 | rb.Plugin.__init__(self) | 82 | rb.Plugin.__init__(self) |
247 | 66 | AutoQueueBase.__init__(self) | 83 | AutoQueueBase.__init__(self) |
248 | @@ -69,21 +86,27 @@ | |||
249 | 69 | self.by_mirage = True | 86 | self.by_mirage = True |
250 | 70 | self.log("initialized") | 87 | self.log("initialized") |
251 | 71 | self._generators = deque() | 88 | self._generators = deque() |
252 | 89 | self.pec_id = None | ||
253 | 90 | self.rdb = None | ||
254 | 91 | self.shell = None | ||
255 | 72 | 92 | ||
256 | 73 | def activate(self, shell): | 93 | def activate(self, shell): |
257 | 94 | """Called on activation of the plugin.""" | ||
258 | 74 | self.shell = shell | 95 | self.shell = shell |
259 | 75 | self.rdb = shell.get_property('db') | 96 | self.rdb = shell.get_property('db') |
261 | 76 | sp = shell.get_player () | 97 | sp = shell.get_player() |
262 | 77 | self.pec_id = sp.connect( | 98 | self.pec_id = sp.connect( |
263 | 78 | 'playing-song-changed', self.playing_entry_changed) | 99 | 'playing-song-changed', self.playing_entry_changed) |
264 | 79 | 100 | ||
265 | 80 | def deactivate(self, shell): | 101 | def deactivate(self, shell): |
266 | 102 | """Called on deactivation of the plugin.""" | ||
267 | 81 | self.rdb = None | 103 | self.rdb = None |
268 | 82 | self.shell = None | 104 | self.shell = None |
269 | 83 | sp = shell.get_player() | 105 | sp = shell.get_player() |
270 | 84 | sp.disconnect(self.pec_id) | 106 | sp.disconnect(self.pec_id) |
271 | 85 | 107 | ||
272 | 86 | def _idle_callback(self): | 108 | def _idle_callback(self): |
273 | 109 | """Callback that performs task asynchronously.""" | ||
274 | 87 | gdk.threads_enter() | 110 | gdk.threads_enter() |
275 | 88 | while self._generators: | 111 | while self._generators: |
276 | 89 | if self._generators[0] is None: | 112 | if self._generators[0] is None: |
277 | @@ -97,24 +120,23 @@ | |||
278 | 97 | return False | 120 | return False |
279 | 98 | 121 | ||
280 | 99 | def player_execute_async(self, method, *args, **kwargs): | 122 | def player_execute_async(self, method, *args, **kwargs): |
285 | 100 | """Override this if the player has a way to execute methods | 123 | """Execute method asynchronously.""" |
282 | 101 | asynchronously, like the copooling in autoqueue. | ||
283 | 102 | |||
284 | 103 | """ | ||
286 | 104 | add_callback = False | 124 | add_callback = False |
287 | 105 | if not self._generators: | 125 | if not self._generators: |
289 | 106 | add_callback = True | 126 | add_callback = True |
290 | 107 | self._generators.append(method(*args, **kwargs)) | 127 | self._generators.append(method(*args, **kwargs)) |
291 | 108 | if add_callback: | 128 | if add_callback: |
292 | 109 | gobject.idle_add(self._idle_callback) | 129 | gobject.idle_add(self._idle_callback) |
293 | 110 | 130 | ||
294 | 111 | def log(self, msg): | 131 | def log(self, msg): |
296 | 112 | """print debug messages""" | 132 | """Print debug messages.""" |
297 | 133 | # TODO: replace with real logging | ||
298 | 113 | if not self.verbose: | 134 | if not self.verbose: |
299 | 114 | return | 135 | return |
300 | 115 | print msg | 136 | print msg |
301 | 116 | 137 | ||
302 | 117 | def playing_entry_changed(self, sp, entry): | 138 | def playing_entry_changed(self, sp, entry): |
303 | 139 | """Handler for song change.""" | ||
304 | 118 | if entry: | 140 | if entry: |
305 | 119 | self.on_song_started(Song(entry, self.rdb)) | 141 | self.on_song_started(Song(entry, self.rdb)) |
306 | 120 | 142 |