immsview
branchimmsview
changeset 6 ad5b932e411d
parent 5 c51b96c1690e
child 7 c5c0a84bb15b
equal deleted inserted replaced
5:c51b96c1690e 6:ad5b932e411d
     1 #!/usr/bin/python
     1 #!/usr/bin/python
     2 
     2 
     3 _immsview_version = "$Id: immsview 1687 2004-02-01 17:21:02Z fabien $"
     3 _immsview_version = "$Id: immsview 1688 2004-02-02 04:51:35Z fabien $"
     4 
     4 
     5 # Copyright (C) 2004 by Fabien Ninoles
     5 # Copyright (C) 2004 by Fabien Ninoles
     6 
     6 
     7 # IMMSView is aim to be a replacement to XMMS playlist editor
     7 # IMMSView is aim to be a replacement to XMMS playlist editor
     8 # with better support for IMMS plugin.
     8 # with better support for IMMS plugin.
    36 #   - editing playlist
    36 #   - editing playlist
    37 # * File support:
    37 # * File support:
    38 #   - adding, deleting, suppressing a file (including updating other
    38 #   - adding, deleting, suppressing a file (including updating other
    39 #     interface).
    39 #     interface).
    40 
    40 
       
    41 import pygtk
       
    42 pygtk.require('2.0')
       
    43 
    41 import sys
    44 import sys
    42 import os
    45 import os
    43 import sqlite
    46 import sqlite
    44 import Tkinter
    47 import gobject
    45 import ScrolledText
    48 import gtk
       
    49 import gtk.glade
    46 import gettext
    50 import gettext
    47 import xmms.control
    51 import xmms.control
    48 import time
    52 import time
    49 
    53 
    50 gettext.bindtextdomain('immsview', '/usr/share/immsview/LANG')
    54 gtk.glade.bindtextdomain('immsview', '/usr/share/immsview/LANG')
    51 gettext.textdomain('immsview')
    55 gtk.glade.textdomain('immsview')
    52 _ = gettext.gettext
    56 _ = gettext.gettext
    53 
       
    54 _dbname = os.environ['HOME'] + '/.imms/imms.db'
       
    55 
    57 
    56 def strtime(seconds):
    58 def strtime(seconds):
    57     secs = abs(round(seconds))
    59     secs = abs(round(seconds))
    58     minutes = secs / 60;
    60     minutes = secs / 60;
    59     hours = minutes / 60;
    61     hours = minutes / 60;
    87             if filename == self.get_playlist_file(idx):
    89             if filename == self.get_playlist_file(idx):
    88                 return idx
    90                 return idx
    89         return -1
    91         return -1
    90     def play_file(self, filename):
    92     def play_file(self, filename):
    91         idx = self.find_in_playlist(filename)
    93         idx = self.find_in_playlist(filename)
    92         print idx
       
    93         if idx == -1:
    94         if idx == -1:
    94             self.enqueue_and_play((filename,))
    95             self.enqueue_and_play((filename,))
    95         else:
    96         else:
    96             self.set_playlist_pos(idx)
    97             self.set_playlist_pos(idx)
    97 
    98 
    98 class IMMSDb:
    99 class IMMSDb:
       
   100     _dbname = os.environ['HOME'] + '/.imms/imms.db'
       
   101     # _dbname = os.environ['HOME'] + '/.imms/imms.backup.db'
       
   102     def __init__(self):
       
   103         self.cx = sqlite.connect(IMMSDb._dbname)
       
   104     def commit(self):
       
   105         self.cx.commit()
       
   106     def _get_ratings(self, min = 0, max = 250):
       
   107         cu = self.cx.cursor()
       
   108         cu.execute('''SELECT Rating.uid, Rating.rating 
       
   109                    FROM Rating
       
   110                    WHERE Rating.rating >= %d
       
   111                    AND Rating.rating <= %d
       
   112                    ORDER BY Rating.rating;''' % (min, max))
       
   113         return cu.fetchall()
       
   114     def _get_library_uid(self, uid):
       
   115         cu = self.cx.cursor()
       
   116         cu.execute('''SELECT Library.path
       
   117                    FROM Library
       
   118                    WHERE Library.uid = %d;''' % (uid,))
       
   119         return cu.fetchone()
       
   120     def _get_library_by_path(self, path):
       
   121         cu = self.cx.cursor()
       
   122         cu.execute('''SELECT Library.uid, Library.sid
       
   123                    FROM Library
       
   124                    WHERE Library.path = '%s';''' % (path))
       
   125         return cu.fetchone()
       
   126     def get_ratings_and_info(self):
       
   127         cu = self.cx.cursor()
       
   128         cu.execute('''SELECT Rating.uid, Rating.rating,
       
   129                    Library.path, Last.last
       
   130                    FROM Rating, Library, Last
       
   131                    WHERE Rating.uid = Library.uid AND
       
   132                    Library.sid = Last.sid
       
   133                    ORDER BY Rating.rating DESC;''')
       
   134         # Better to fetch everything since locking can really mess
       
   135         # things in imms plugin.
       
   136         res = cu.fetchall()
       
   137         self.commit()
       
   138         results = []
       
   139         for tune in res:
       
   140             try:
       
   141                 tmp = {'rating' : tune[1],
       
   142                        'path' : tune[2].decode('latin_1', 'replace'),
       
   143                        'last' : int(tune[3])}
       
   144                 results.append(tmp)
       
   145             except UnicodeDecodeError:
       
   146                 print tune[2]
       
   147         return results
       
   148 
       
   149 class IMMSStore(gtk.ListStore):
       
   150     COL_RATING = 0
       
   151     COL_PATH = 1
       
   152     COL_LAST_STR = 2
       
   153     COL_LAST = 3
       
   154     COL_SELECT = 4
       
   155     def __init__(self, db, xmms):
       
   156         gtk.ListStore.__init__(self,
       
   157                                gobject.TYPE_INT,
       
   158                                gobject.TYPE_STRING,
       
   159                                gobject.TYPE_STRING,
       
   160                                gobject.TYPE_INT,
       
   161                                gobject.TYPE_BOOLEAN)
       
   162         self.db = db
       
   163         self.xmms = xmms
       
   164     def refresh(self):
       
   165         curtime = time.time()
       
   166         tunes = self.db.get_ratings_and_info()
       
   167         self.clear()
       
   168         for tune in tunes:
       
   169             iter = self.append(None)
       
   170             self.set(iter,
       
   171                      IMMSStore.COL_RATING, tune['rating'],
       
   172                      IMMSStore.COL_PATH, tune['path'],
       
   173                      IMMSStore.COL_LAST_STR, strtime(curtime-tune['last']),
       
   174                      IMMSStore.COL_LAST, tune['last'],
       
   175                      IMMSStore.COL_SELECT, gtk.FALSE)
       
   176     def _check_song_path(self, path, giter, song):
       
   177         self.set(giter, 4, self.get_value(giter, 1) == song)
       
   178     def set_current_song(self):
       
   179         self.foreach(IMMSStore._check_song_path, self.xmms.get_current_file())
       
   180 
       
   181 class IMMSView(gtk.TreeView):
       
   182 	def __init__(self, model, xmms):
       
   183             gtk.TreeView.__init__(self, model)
       
   184             self.create_widgets()
       
   185 	def create_widgets(self):
       
   186             renderer = gtk.CellRendererText()
       
   187             renderer.set_property('weight', 700)
       
   188             # renderer.connect('double-clicked', on_clicked)
       
   189             column = gtk.TreeViewColumn(_("Rating"), renderer,
       
   190                                         weight_set = IMMSStore.COL_SELECT,
       
   191                                         text = IMMSStore.COL_RATING)
       
   192             column.set_sort_column_id(IMMSStore.COL_RATING)
       
   193             self.append_column(column)
       
   194             column = gtk.TreeViewColumn(_("File"), renderer,
       
   195                                         weight_set = IMMSStore.COL_SELECT,
       
   196                                         text = IMMSStore.COL_PATH)
       
   197             column.set_resizable(gtk.TRUE)
       
   198             column.set_sort_column_id(IMMSStore.COL_PATH)
       
   199             column.set_clickable(gtk.TRUE)
       
   200             self.append_column(column)
       
   201             column = gtk.TreeViewColumn(_("Last"), renderer,
       
   202                                         weight_set = IMMSStore.COL_SELECT,
       
   203                                         text = IMMSStore.COL_LAST_STR)
       
   204             column.set_sort_column_id(IMMSStore.COL_LAST)
       
   205             self.append_column(column)
       
   206             self.set_search_column(IMMSStore.COL_PATH)
       
   207             self.set_headers_clickable(gtk.TRUE)
       
   208         def get_selected(self):
       
   209             model, giter = self.get_selection().get_selected()
       
   210             return model.get_value(giter, IMMSStore.COL_PATH)
       
   211 
       
   212 class IMMSToolbar(gtk.Toolbar):
    99 	def __init__(self):
   213 	def __init__(self):
   100 		self.cx = sqlite.connect(_dbname)
   214 		gtk.Toolbar.__init__(self)
   101 	def commit(self):
   215 		self.refresh_command = None
   102 		self.cx.commit()
   216                 self.select_command = None
   103 	def _get_ratings(self, min = 0, max = 250):
       
   104 		cu = self.cx.cursor()
       
   105 		cu.execute('''SELECT Rating.uid, Rating.rating 
       
   106 			FROM Rating
       
   107 			WHERE Rating.rating >= %d
       
   108 			AND Rating.rating <= %d
       
   109 			ORDER BY Rating.rating;''' % (min, max))
       
   110 		return cu.fetchall()
       
   111 	def _get_library_uid(self, uid):
       
   112 		cu = self.cx.cursor()
       
   113 		cu.execute('''SELECT Library.path
       
   114 			FROM Library
       
   115 			WHERE Library.uid = %d;''' % (uid,))
       
   116 		return cu.fetchone()
       
   117 	def _get_library_by_path(self, path):
       
   118 		cu = self.cx.cursor()
       
   119 		cu.execute('''SELECT Library.uid, Library.sid
       
   120 			FROM Library
       
   121 			WHERE Library.path = '%s';''' % (path))
       
   122         def get_ratings_and_info(self):
       
   123                 cu = self.cx.cursor()
       
   124                 cu.execute('''SELECT Rating.uid, Rating.rating,
       
   125                                 Library.path, Last.last
       
   126                                 FROM Rating, Library, Last
       
   127                                 WHERE Rating.uid = Library.uid AND
       
   128                                 Library.sid = Last.sid
       
   129                                 ORDER BY Rating.rating DESC;''')
       
   130                 # Better to fetch everything since locking can really mess
       
   131                 # things in imms plugin.
       
   132                 res = cu.fetchall()
       
   133                 self.commit()
       
   134                 results = []
       
   135                 for tune in res:
       
   136                         tmp = {'rating' : tune[1],
       
   137                                'path' : tune[2],
       
   138                                'last' : int(tune[3])}
       
   139                         results.append(tmp)
       
   140                 return results
       
   141 
       
   142 class IMMSView(Tkinter.Frame):
       
   143 	def __init__(self, xmms, db, master = None):
       
   144 		Tkinter.Frame.__init__(self, master)
       
   145 		self.db = db
       
   146                 self.xmms = xmms
       
   147 		self.create_widgets()
   217 		self.create_widgets()
   148 	def create_widgets(self):
   218 	def create_widgets(self):
   149 		lf = Tkinter.Frame(self)
   219 		self.append_item(_('Refresh'), _('Refresh list'),
   150 		lf.pack(expand = 1, fill = Tkinter.BOTH, side = Tkinter.LEFT)
   220                                  None, None, self.do_refresh)
   151 		self.lb = Tkinter.Listbox(lf, relief = Tkinter.RAISED)
   221                 self.append_item(_('Plot'), _('Show graph of rates'),
   152 		self.lb.pack(expand = 1, side = Tkinter.TOP, fill = Tkinter.BOTH)
   222                                  None, None, self.plot)
   153 		xsb = Tkinter.Scrollbar(lf, orient = Tkinter.HORIZONTAL)
   223                 self.append_item(_('Current'), _('Get current song'),
   154 		xsb.pack({'fill' :  Tkinter.X, 'side' : Tkinter.BOTTOM})
   224                                  None, None, self.do_get_current)
   155 		ysb = Tkinter.Scrollbar(self)
   225 	def plot(self, data):
   156 		ysb.pack({'fill' :  Tkinter.Y, 'side' : Tkinter.RIGHT})
   226 		os.system('/home/fabien/bin/immsplot &')
   157 
   227 	def do_refresh(self, data):
   158 		ysb['command'] = self.lb.yview
       
   159 		xsb['command'] = self.lb.xview
       
   160 		self.lb['yscrollcommand'] = ysb.set
       
   161 		self.lb['xscrollcommand'] = xsb.set
       
   162                 self.lb.bind("<Double-Button-1>", self.play_selection)
       
   163 		
       
   164 		self.refresh()
       
   165                 
       
   166 	def refresh(self):
       
   167                 curtime = time.time()
       
   168 		self.tunes = self.db.get_ratings_and_info()
       
   169 		self.lb.delete(0,self.lb.size())
       
   170                 l = []
       
   171                 for tune in self.tunes:
       
   172                         l.append("%4d: %s [%s]" %
       
   173                                 (tune['rating'], tune['path'],
       
   174                                  strtime(curtime - tune['last'])))
       
   175                 apply(self.lb.insert, [0]+ l)
       
   176                 self.select_current()
       
   177         def select_current(self):
       
   178                 fn = self.xmms.get_current_file()
       
   179                 tune = filter(lambda x: x['path'] == fn, self.tunes)
       
   180                 if len(tune):
       
   181                         idx = self.tunes.index(tune[0])
       
   182                         self.lb.see(idx)
       
   183                         self.lb.select_set(idx)
       
   184         def play_selection(self, event):
       
   185             sel = self.lb.curselection()
       
   186             if len(sel) > 0:
       
   187                 fn = self.tunes[int(sel[0])]['path']
       
   188                 self.xmms.play_file(fn)
       
   189 
       
   190 class IMMSToolbar(Tkinter.Frame):
       
   191 	def __init__(self, master = None):
       
   192 		Tkinter.Frame.__init__(self, master)
       
   193 		self.refresh_command = None
       
   194 		self.create_widgets()
       
   195 	def create_widgets(self):
       
   196 		button = Tkinter.Button(self, text = _('Refresh'),
       
   197                                         command = self.do_refresh)
       
   198 		button.pack(side = Tkinter.LEFT)
       
   199 		button = Tkinter.Button(self, text = _('Plot'),
       
   200                                         command = self.plot)
       
   201 		button.pack(side = Tkinter.LEFT)
       
   202                 button = Tkinter.Button(self, text = _('Current'),
       
   203                                         command = self.do_get_current)
       
   204 		button.pack(side = Tkinter.LEFT)
       
   205 	def plot(self):
       
   206 		os.system('exec immsplot &')
       
   207 	def do_refresh(self):
       
   208 		if (self.refresh_command):
   228 		if (self.refresh_command):
   209 			self.refresh_command()
   229 			self.refresh_command()
   210         def do_get_current(self):
   230         def do_get_current(self, data):
   211                 if (self.get_current):
   231                 if (self.get_current):
   212                         self.get_current()
   232                         self.get_current()
   213 
   233 
   214 root = Tkinter.Tk()
   234 root = gtk.Window()
   215 root.wm_title(_("IMMSview"))
   235 root.set_name(_("IMMSview"))
   216 iview = IMMSView(XMMSControl(), IMMSDb(), root)
   236 root.connect('destroy', gtk.mainquit)
   217 iview.pack(side = Tkinter.BOTTOM, expand = 1, fill = Tkinter.BOTH)
   237 vbox = gtk.VBox(spacing = 3)
       
   238 root.add(vbox)
       
   239 vbox.show()
       
   240 xmms_control = XMMSControl()
       
   241 model = IMMSStore(IMMSDb(), xmms_control)
       
   242 iview = IMMSView(model, xmms_control)
       
   243 scroll = gtk.ScrolledWindow()
       
   244 scroll.add(iview)
       
   245 vbox.pack_end(scroll)
       
   246 iview.show()
       
   247 scroll.show()
   218 toolbar = IMMSToolbar()
   248 toolbar = IMMSToolbar()
   219 toolbar.refresh_command = iview.refresh
   249 toolbar.refresh_command = model.refresh
   220 toolbar.get_current = iview.select_current
   250 toolbar.get_current = model.set_current_song
   221 toolbar.pack(side = Tkinter.TOP, fill = Tkinter.X)
   251 vbox.pack_start(toolbar, expand = gtk.FALSE)
   222 root.mainloop()
   252 toolbar.show()
       
   253 root.show()
       
   254 model.refresh()
       
   255 model.set_current_song()
       
   256 gtk.main()