18 # You should have received a copy of the GNU General Public License |
18 # You should have received a copy of the GNU General Public License |
19 # along with GNU Emacs; see the file COPYING. If not, write to the |
19 # along with GNU Emacs; see the file COPYING. If not, write to the |
20 # Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
20 # Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
21 # Boston, MA 02111-1307, USA. |
21 # Boston, MA 02111-1307, USA. |
22 |
22 |
23 _version_ = "$Id: immsview 1713 2004-02-08 21:55:24Z fabien $" |
23 _version_ = "$Id: immsview 1715 2004-02-10 04:29:08Z fabien $" |
24 |
24 |
25 # $Log$ |
25 # $Log$ |
|
26 # Revision 1.30 2004/02/10 04:29:08 fabien |
|
27 # Many cleanup, both architecture (division of interface), encoding |
|
28 # behavior and also many bugs corrected. |
|
29 # |
26 # Revision 1.29 2004/02/08 21:55:24 fabien |
30 # Revision 1.29 2004/02/08 21:55:24 fabien |
27 # New bestofimms, cleanimms, imms.py and utils.py. |
31 # New bestofimms, cleanimms, imms.py and utils.py. |
28 # |
32 # |
29 # Revision 1.28 2004/02/07 15:58:54 fabien |
33 # Revision 1.28 2004/02/07 15:58:54 fabien |
30 # Use Python-Gnuplot instead of immsplot. |
34 # Use Python-Gnuplot instead of immsplot. |
147 import gtk.glade |
151 import gtk.glade |
148 import gettext |
152 import gettext |
149 import xmms.control |
153 import xmms.control |
150 import time |
154 import time |
151 import Gnuplot |
155 import Gnuplot |
152 from imms import IMMSDb |
156 from imms import IMMSDb, rating_to_color |
|
157 from utils import strdelay |
153 |
158 |
154 gtk.glade.bindtextdomain('immsview', '/usr/share/immsview/LANG') |
159 gtk.glade.bindtextdomain('immsview', '/usr/share/immsview/LANG') |
155 gtk.glade.textdomain('immsview') |
160 gtk.glade.textdomain('immsview') |
156 _ = gettext.gettext |
161 _ = gettext.gettext |
157 |
162 stderr = sys.stderr |
158 def strtime(seconds): |
|
159 secs = abs(round(seconds)) |
|
160 minutes = secs / 60; |
|
161 hours = minutes / 60; |
|
162 days = hours / 24; |
|
163 secs = secs % 60; |
|
164 minutes %= 60; |
|
165 hours %= 24; |
|
166 |
|
167 if seconds < 0: |
|
168 s = "-" |
|
169 else: |
|
170 s = "" |
|
171 if days >= 1: |
|
172 s += "%dd %dh" % (days, hours) |
|
173 elif hours >= 1: |
|
174 s += "%dh%02d" % (hours, minutes) |
|
175 elif minutes >= 1: |
|
176 s += "%d'%02d\"" % (minutes, secs) |
|
177 else: |
|
178 s += "%d\"" % (secs) |
|
179 return s |
|
180 |
163 |
181 class XMMSControl: |
164 class XMMSControl: |
182 def __getattr__(self, name): |
165 def __getattr__(self, name): |
183 return xmms.control.__dict__[name] |
166 return xmms.control.__dict__[name] |
184 def get_current_file(self): |
167 def get_current_file(self): |
194 if idx == -1: |
177 if idx == -1: |
195 self.enqueue_and_play((filename,)) |
178 self.enqueue_and_play((filename,)) |
196 else: |
179 else: |
197 self.set_playlist_pos(idx) |
180 self.set_playlist_pos(idx) |
198 |
181 |
199 |
|
200 _gdk_colors = [] |
182 _gdk_colors = [] |
201 for i in range(76): |
183 for i in range(75,150+1): |
202 if i <= 25: |
184 _gdk_colors.append(rating_to_color(i)) |
203 red = 255 |
185 |
204 green = i * 255 / 25 |
186 def gdk_rating_to_color(rate): |
205 blue = 0 |
|
206 elif i <= 50: |
|
207 red = (50-i) * 255 / 25 |
|
208 green = 255 |
|
209 blue = 0 |
|
210 else: |
|
211 red = 0 |
|
212 green = 255 |
|
213 blue = (i-50) * 255 / 25 |
|
214 _gdk_colors.append("#%02X%02X%02X" % (red, green, blue)) |
|
215 |
|
216 def rating_to_color(rate): |
|
217 rate = min(max(rate,75),150) |
187 rate = min(max(rate,75),150) |
218 return _gdk_colors[rate-75] |
188 return _gdk_colors[rate-75] |
219 |
189 |
220 # This class must be derived from a GtkTreeModel |
190 # This class must be derived from a GtkTreeModel |
221 # with persistent GtkTreeIter !!! |
191 # with persistent GtkTreeIter !!! |
225 COL_LAST_STR = 2 |
195 COL_LAST_STR = 2 |
226 COL_LAST = 3 |
196 COL_LAST = 3 |
227 COL_SELECT = 4 |
197 COL_SELECT = 4 |
228 COL_UID = 5 |
198 COL_UID = 5 |
229 COL_RATING_COLOR = 6 |
199 COL_RATING_COLOR = 6 |
230 COL_PATHS_NUMBER = 7 |
200 COL_PATHS = 7 |
231 def __init__(self, db): |
201 def __init__(self, db): |
232 gtk.ListStore.__init__(self, |
202 gtk.ListStore.__init__(self, |
233 gobject.TYPE_INT, |
203 gobject.TYPE_INT, |
234 gobject.TYPE_STRING, |
204 gobject.TYPE_STRING, |
235 gobject.TYPE_STRING, |
205 gobject.TYPE_STRING, |
236 gobject.TYPE_INT, |
206 gobject.TYPE_INT, |
237 gobject.TYPE_BOOLEAN, |
207 gobject.TYPE_BOOLEAN, |
238 gobject.TYPE_INT, |
208 gobject.TYPE_INT, |
239 gobject.TYPE_STRING, |
209 gobject.TYPE_STRING, |
240 gobject.TYPE_INT, |
210 gobject.TYPE_PYOBJECT, |
241 ) |
211 ) |
242 self.db = db |
212 self.db = db |
243 self.set_default_sort_func(self.default_sort) |
213 self.set_default_sort_func(self.default_sort) |
244 self.set_sort_column_id(IMMSStore.COL_RATING, gtk.SORT_DESCENDING) |
214 self.set_sort_column_id(IMMSStore.COL_RATING, gtk.SORT_DESCENDING) |
245 self.curtime = time.time() |
215 self.curtime = time.time() |
263 if not giter: |
233 if not giter: |
264 giter = self.append(None) |
234 giter = self.append(None) |
265 self.set(giter, |
235 self.set(giter, |
266 IMMSStore.COL_UID, uid, |
236 IMMSStore.COL_UID, uid, |
267 IMMSStore.COL_RATING, tune['rating'], |
237 IMMSStore.COL_RATING, tune['rating'], |
268 IMMSStore.COL_PATH, fn, |
238 IMMSStore.COL_PATH, fn.decode('utf-8','replace'), |
269 IMMSStore.COL_LAST, tune['last'], |
239 IMMSStore.COL_LAST, tune['last'], |
270 IMMSStore.COL_LAST_STR, strtime(self.curtime-tune['last']), |
240 IMMSStore.COL_LAST_STR, strdelay(self.curtime-tune['last']), |
271 IMMSStore.COL_RATING_COLOR, rating_to_color(tune['rating']), |
241 IMMSStore.COL_RATING_COLOR, gdk_rating_to_color(tune['rating']), |
272 IMMSStore.COL_PATHS_NUMBER, len(tune['path']), |
242 IMMSStore.COL_PATHS, tune['path'], |
273 IMMSStore.COL_SELECT, gtk.FALSE) |
243 IMMSStore.COL_SELECT, gtk.FALSE) |
274 return giter |
244 return giter |
275 def update(self): |
245 def update(self): |
276 self.curtime = time |
246 self.curtime = time |
277 self.curtime = time.time() |
247 self.curtime = time.time() |
278 col, order = self.get_sort_column_id() |
248 col, order = self.get_sort_column_id() |
279 if col: |
249 if col: |
280 self.set_sort_column_id(-1, gtk.SORT_ASCENDING) |
250 self.set_sort_column_id(-1, gtk.SORT_ASCENDING) |
281 tunes = self.db.get_ratings_and_info() |
251 tunes = self.db.get_ratings_and_paths() |
282 print time.ctime(time.time()) + ": inserting" |
252 print >> stderr, time.ctime(time.time()) + ": inserting" |
283 self._update_me(tunes) |
253 self._update_me(tunes) |
284 print time.ctime(time.time()) + ": end insert" |
254 print time.ctime(time.time()) + ": end insert" |
285 if col: |
255 if col: |
286 self.set_sort_column_id(col, order) |
256 self.set_sort_column_id(col, order) |
287 def replace(self, tunes): |
257 def replace(self, tunes): |
298 giter = self.iter_next(giter) |
268 giter = self.iter_next(giter) |
299 while giter: |
269 while giter: |
300 next = self.iter_next(giter) |
270 next = self.iter_next(giter) |
301 self.remove(giter) |
271 self.remove(giter) |
302 giter = next |
272 giter = next |
303 def refresh(self, tunes): |
|
304 "refresh only get new data and update last time and rating." |
|
305 freed_giters = [] |
|
306 # first, try to recycle current giter. |
|
307 giter = self.get_iter_first() |
|
308 while giter: |
|
309 # check if giter exist in DB. |
|
310 guid = self.get_value(giter, IMMSStore.COL_UID) |
|
311 if tunes.has_key(guid): |
|
312 tune = tunes[guid] |
|
313 self.set(giter, |
|
314 IMMSStore.COL_RATING, tune['rating'], |
|
315 IMMSStore.COL_LAST, tune['last'], |
|
316 IMMSStore.COL_LAST_STR, |
|
317 strtime(self.curtime-tune['last']), |
|
318 IMMSStore.COL_RATING_COLOR, |
|
319 rating_to_color(tune['rating'])) |
|
320 else: |
|
321 # Elsewhere, collect them for further use... |
|
322 # Hopefully, ListStore have persistent giter! |
|
323 freed_giters.append(giter) |
|
324 giter = self.iter_next(giter) |
|
325 # populate the remeaning tunes into the collected giters |
|
326 for uid, tune in tunes.iteritems(): |
|
327 if len(freed_giters) > 0: |
|
328 giter = freed_giters.pop() |
|
329 else: |
|
330 giter = None |
|
331 self.tune_to_giter(uid, tune, None, giter) |
|
332 # then erase the remeaning giter. |
|
333 for giter in freed_giters: |
|
334 self.remove(giter) |
|
335 def find_selected_giter(self): |
273 def find_selected_giter(self): |
336 giter = self.get_iter_first() |
274 giter = self.get_iter_first() |
337 while giter: |
275 while giter: |
338 if self.get_value(giter, IMMSStore.COL_SELECT): |
276 if self.get_value(giter, IMMSStore.COL_SELECT): |
339 break |
277 break |
368 res.append((uid, rating)) |
306 res.append((uid, rating)) |
369 giter = self.iter_next(giter) |
307 giter = self.iter_next(giter) |
370 return res |
308 return res |
371 def update_giter(self, giter, path = None): |
309 def update_giter(self, giter, path = None): |
372 uid = self.get_value(giter, IMMSStore.COL_UID) |
310 uid = self.get_value(giter, IMMSStore.COL_UID) |
373 tunes = self.db.get_ratings_and_info([uid,]) |
311 tunes = self.db.get_ratings_and_paths([uid,]) |
374 if len(tunes) > 0: |
312 if len(tunes) > 0: |
375 return self.tune_to_giter(uid, tunes[uid], path, giter) |
313 return self.tune_to_giter(uid, tunes[uid], path, giter) |
376 return giter |
314 return giter |
377 ## def get_value(self, giter, col): |
315 ## def get_value(self, giter, col): |
378 ## # sniff! Can't override built-ins |
316 ## # sniff! Can't override built-ins |
379 ## if col == IMMSStore.COL_LAST_STR: |
317 ## if col == IMMSStore.COL_LAST_STR: |
380 ## return strtime(time.time() - |
318 ## return strdelay(time.time() - |
381 ## self.get_value(giter, IMMSStore.COL_LAST)) |
319 ## self.get_value(giter, IMMSStore.COL_LAST)) |
382 ## else: |
320 ## else: |
383 ## return gtk.ListStore.get_value(self, giter, col) |
321 ## return gtk.ListStore.get_value(self, giter, col) |
384 |
322 |
385 class IMMSPlot(Gnuplot.Gnuplot): |
323 class IMMSPlot(Gnuplot.Gnuplot): |
419 column = gtk.TreeViewColumn(_("Last"), renderer, |
357 column = gtk.TreeViewColumn(_("Last"), renderer, |
420 weight_set = IMMSStore.COL_SELECT, |
358 weight_set = IMMSStore.COL_SELECT, |
421 text = IMMSStore.COL_LAST_STR) |
359 text = IMMSStore.COL_LAST_STR) |
422 column.set_sort_column_id(IMMSStore.COL_LAST) |
360 column.set_sort_column_id(IMMSStore.COL_LAST) |
423 self.append_column(column) |
361 self.append_column(column) |
424 column = gtk.TreeViewColumn(_("#"), renderer, |
|
425 weight_set = IMMSStore.COL_SELECT, |
|
426 text = IMMSStore.COL_PATHS_NUMBER) |
|
427 self.append_column(column) |
|
428 column = gtk.TreeViewColumn(_("File"), renderer, |
362 column = gtk.TreeViewColumn(_("File"), renderer, |
429 weight_set = IMMSStore.COL_SELECT, |
363 weight_set = IMMSStore.COL_SELECT, |
430 text = IMMSStore.COL_PATH) |
364 text = IMMSStore.COL_PATH) |
431 column.set_resizable(gtk.TRUE) |
365 column.set_resizable(gtk.TRUE) |
432 column.set_sort_column_id(IMMSStore.COL_PATH) |
366 column.set_sort_column_id(IMMSStore.COL_PATH) |
444 if giter: |
378 if giter: |
445 model.set_value(giter, IMMSStore.COL_SELECT, gtk.TRUE) |
379 model.set_value(giter, IMMSStore.COL_SELECT, gtk.TRUE) |
446 self.set_cursor(model.get_path(giter)) |
380 self.set_cursor(model.get_path(giter)) |
447 def get_filename(self, giter): |
381 def get_filename(self, giter): |
448 model = self.get_model() |
382 model = self.get_model() |
449 fn = model.get_value(model.update_giter(giter), IMMSStore.COL_PATH) |
383 model.update_giter(giter) |
450 try: |
384 paths = model.get_value(giter, IMMSStore.COL_PATHS) |
451 os.stat(fn) |
385 enc_path = model.get_value(giter, IMMSStore.COL_PATH) |
452 except OSError: |
386 paths = filter(os.path.isfile, paths) |
|
387 # No valid file in list |
|
388 if len(paths) == 0: |
453 return None |
389 return None |
454 return fn |
390 # Try to find the currently display file |
|
391 for fn in paths: |
|
392 if enc_path == fn.decode('utf8', 'replace'): |
|
393 return fn |
|
394 # Else, return the first find |
|
395 return paths[0] |
455 def get_file_selected(self): |
396 def get_file_selected(self): |
456 model, giter = self.get_selection().get_selected() |
397 model, giter = self.get_selection().get_selected() |
457 if giter: |
398 if giter: |
458 return self.get_filename(giter) |
399 return self.get_filename(giter) |
459 return None |
400 return None |
492 self.iview.get_model().update() |
433 self.iview.get_model().update() |
493 def do_get_current(self, dummy): |
434 def do_get_current(self, dummy): |
494 song = self.xmms.get_current_file() |
435 song = self.xmms.get_current_file() |
495 try: |
436 try: |
496 self.iview.set_current_song(song) |
437 self.iview.set_current_song(song) |
497 except: |
438 except Exception, e: |
498 pass |
439 print >> stderr, e |
499 def do_play(self, dummy): |
440 def do_play(self, dummy): |
500 fn = self.iview.get_file_selected() |
441 fn = self.iview.get_file_selected() |
501 if fn: |
442 if fn: |
502 self.xmms.play_file(fn) |
443 self.xmms.play_file(fn) |
503 self.sleep(self._SLEEP_TIME) |
444 self.sleep(self._SLEEP_TIME) |