Remove old album_art retrieval code
[mussorgsky] / src / edit_panel_tm.py
1 #!/usr/bin/env python2.5
2 import hildon
3 import gtk, gobject
4 from mutagen_backend import MutagenBackend
5 from player_backend import MediaPlayer
6 from utils import escape_html
7 import album_art_spec
8 import os
9
10 # Fields in the tuple!
11 # Shared with browse_panel
12 URI_COLUMN = 0
13 ARTIST_COLUMN = 2
14 TITLE_COLUMN = 3
15 ALBUM_COLUMN = 4
16 MIME_COLUMN = 5
17 UI_COLUMN = 6
18 SEARCH_COLUMN = 7
19
20 class MussorgskyEditPanel (hildon.StackableWindow):
21
22     def __init__ (self):
23         hildon.StackableWindow.__init__ (self)
24         self.set_border_width (12)
25         self.album_change_handler = -1
26         self.artist_change_handler = -1
27         self.writer = MutagenBackend ()
28         self.player = MediaPlayer ()
29         self.__create_view ()
30         self.data_loaded = False
31         self.artist_list = None
32         self.albums_list = None
33         self.connect ("delete-event", self.close_function)
34
35     def close_function (self, widget, event):
36         if (not self.data_loaded):
37             return
38         
39         if self.__is_view_dirty ():
40             self.save_metadata ()
41
42         
43     def update_title (self):
44         self.set_title ("Edit (%d/%d)" % (self.model.get_path (self.current)[0] + 1,
45                                           len (self.model)))
46
47     def get_current_row (self):
48         if (not self.current):
49             return 6 * (None,)
50         return self.model.get (self.current, 0, 1, 2, 3, 4, 5)
51
52     def set_model (self, model, current=None):
53         try:
54             if self.artists_list or self.albums_list:
55                 pass
56         except AttributeError, e:
57             print "**** Set album and artist alternatives before setting a model"
58             raise e
59         
60         self.model = model
61         if (current):
62             self.current = current
63         else:
64             self.current = self.model.get_iter_first ()
65         self.data_loaded = True
66         self.set_data_in_view (self.get_current_row ())
67         self.update_title ()
68
69     def set_current (self, current):
70         """
71         Iterator to current element
72         """
73         self.current = current
74         self.set_data_in_view (self.get_current_row ())
75         self.update_title ()
76
77
78     def set_artist_alternatives (self, alternatives):
79         self.artists_list = alternatives
80         artist_selector = hildon.TouchSelectorEntry (text=True)
81         for a in self.artists_list:
82             artist_selector.append_text (a)
83         self.artist_button.set_selector (artist_selector)
84
85     def set_album_alternatives (self, alternatives):
86         self.albums_list = alternatives
87         album_selector = hildon.TouchSelectorEntry (text=True)
88         for a in self.albums_list:
89             album_selector.append_text (a)
90         self.album_button.set_selector (album_selector)
91
92
93     def press_back_cb (self, widget):
94         if (self.player.is_playing ()):
95             self.player.stop ()
96
97         if self.__is_view_dirty ():
98             print "Modified data. Save!"
99             self.save_metadata ()
100             
101         path = self.model.get_path (self.current)
102         if (path[0] == 0):
103             self.destroy ()
104         else:
105             new_path = ( path[0] -1, )
106             self.current = self.model.get_iter (new_path)
107             self.set_data_in_view (self.get_current_row ())
108             self.update_title ()
109
110     def press_next_cb (self, widget):
111         if (self.player.is_playing ()):
112             self.player.stop ()
113
114         if self.__is_view_dirty ():
115             print "Modified data. Save!"
116             self.save_metadata ()
117
118         self.current = self.model.iter_next (self.current)
119         if (not self.current):
120             self.destroy ()
121         else:
122             self.set_data_in_view (self.get_current_row ())
123             self.update_title ()
124
125             
126     def save_metadata (self):
127         # Save the data in the online model to show the appropiate data
128         # in the UI while tracker process the update.
129
130         # 0 - filename -> doesn't change
131         # 1 - "Music"  -> doesn't change
132         # 5 - mimetype -> doesn't change
133         if (type (self.model) == gtk.TreeModelFilter):
134             m = self.model.get_model ()
135             c = self.model.convert_iter_to_child_iter (self.current)
136         else:
137             # Very unlikely
138             m = self.model
139             c = self.current
140
141         artist = self.artist_button.get_value ()
142         title = self.title_entry.get_text ()
143         album = self.album_button.get_value ()
144         text = "<b>%s</b>\n<small>%s</small>" % (escape_html (title),
145                                                  escape_html (artist) + " / " + escape_html (album))
146         search_str = artist.lower () + " " + title.lower () + " " + album.lower ()
147
148         m.set (c,
149                ARTIST_COLUMN, artist,
150                TITLE_COLUMN, title,
151                ALBUM_COLUMN, album,
152                UI_COLUMN, text,
153                SEARCH_COLUMN, search_str)
154         new_song = self.get_current_row ()
155         try:
156             self.writer.save_metadata_on_file (new_song[URI_COLUMN],
157                                                new_song[MIME_COLUMN],
158                                                self.artist_button.get_value (),
159                                                self.title_entry.get_text (),
160                                                self.album_button.get_value ())
161         except IOError, e:
162             # This error in case of tracker returning unexistent files.
163             # Uhm.... for instance after removing a memory card we are editing!
164             pass
165             #dialog = gtk.MessageDialog (self,
166             #                            gtk.DIALOG_DESTROY_WITH_PARENT,
167             #                            gtk.MESSAGE_ERROR,
168             #                            gtk.BUTTONS_CLOSE,
169             #                            "%s" % str(e));
170             #dialog.run ()
171         
172
173     def __is_view_dirty (self):
174         """
175         True if the data has been modified in the widgets
176         """
177         song = self.get_current_row ()
178
179         return not (self.filename_data.get_text() == song[URI_COLUMN] and
180                     self.artist_button.get_value () == song[ARTIST_COLUMN] and
181                     self.title_entry.get_text () == song[TITLE_COLUMN] and
182                     self.album_button.get_value () == song[ALBUM_COLUMN] )
183         
184
185     def __create_view (self):
186         view_vbox = gtk.VBox (homogeneous=False, spacing = 12)
187
188         filename_row = gtk.HBox ()
189         filename_label = gtk.Label ()
190         filename_label.set_markup ("<small>Filename:</small>")
191         filename_row.pack_start (filename_label, expand=False, padding=12);
192         self.filename_data = gtk.Label ("")
193         filename_row.pack_start (self.filename_data, expand=True)
194
195         #filename_row.pack_start (play_button, expand=False, fill=False)
196         view_vbox.pack_start (filename_row, expand=False);
197
198         central_panel = gtk.HBox (spacing=12)
199
200         table = gtk.Table (3, 2, False)
201         table.set_col_spacings (12)
202         table.set_row_spacings (12)
203
204         central_panel.pack_start (table, fill=True)
205         view_vbox.pack_start (central_panel, expand=True, fill=True)
206
207         # Title row
208         label_title = gtk.Label ("Title:")
209         table.attach (label_title, 0, 1, 0, 1, 0)
210         self.title_entry = hildon.Entry(gtk.HILDON_SIZE_FINGER_HEIGHT)
211         table.attach (self.title_entry, 1, 2, 0, 1)
212
213         # Artist row
214         self.artist_button = hildon.PickerButton (gtk.HILDON_SIZE_THUMB_HEIGHT,
215                                                   hildon.BUTTON_ARRANGEMENT_HORIZONTAL)
216         self.artist_button.set_title ("Artist: ")
217         # Set data will set the selector
218         table.attach (self.artist_button, 0, 2, 1, 2)
219
220
221         # Album row
222         self.album_button = hildon.PickerButton (gtk.HILDON_SIZE_THUMB_HEIGHT,
223                                                  hildon.BUTTON_ARRANGEMENT_HORIZONTAL)
224         self.album_button.set_title ("Album: ")
225         # set_data will set the selector
226         table.attach (self.album_button, 0, 2, 2, 3)
227         
228
229         # Album art space
230         self.album_art = gtk.Image ()
231         self.album_art.set_size_request (124, 124)
232         central_panel.pack_start (self.album_art, expand=False, fill=False)
233         
234         # Buttons row
235         button_box = gtk.Toolbar ()
236
237         play_button = gtk.ToolButton (gtk.image_new_from_stock (gtk.STOCK_MEDIA_PLAY, gtk.ICON_SIZE_BUTTON))
238         play_button.connect ("clicked", self.clicked_play)                   
239         play_button.set_expand (True)
240         button_box.insert (play_button, -1)
241         
242         separator = gtk.SeparatorToolItem ()
243         separator.set_expand (True)
244         button_box.insert  (separator, -1)
245
246         back_button = gtk.ToolButton (gtk.image_new_from_stock (gtk.STOCK_GO_BACK, gtk.ICON_SIZE_BUTTON))
247         back_button.connect ("clicked", self.press_back_cb)
248         back_button.set_expand (True)
249         button_box.insert (back_button, -1)
250
251         next_button = gtk.ToolButton (gtk.image_new_from_stock (gtk.STOCK_GO_FORWARD, gtk.ICON_SIZE_BUTTON))
252         next_button.connect ("clicked", self.press_next_cb)
253         next_button.set_expand (True)
254         button_box.insert (next_button, -1)
255
256         self.add_toolbar (button_box)
257         
258         self.add (view_vbox)
259
260
261     def set_data_in_view (self, song):
262         """
263         Place in the screen the song information.
264         Song is a tuple like (filename, 'Music', title, artist, album, mime)
265         """
266         assert len (song) == 6
267         
268         self.filename_data.set_markup ("<small>" + song[URI_COLUMN] + "</small>")
269         self.title_entry.set_text (song[TITLE_COLUMN])
270         
271
272         # Disconnect the value-change signal to avoid extra album art retrievals
273         if (self.album_button.handler_is_connected (self.album_change_handler)):
274             self.album_button.disconnect (self.album_change_handler)
275             
276         if (self.artist_button.handler_is_connected (self.artist_change_handler)):
277             self.artist_button.disconnect (self.artist_change_handler)
278
279         # Set values in the picker buttons
280         try:
281             self.artist_button.set_active (self.artists_list.index(song[ARTIST_COLUMN]))
282         except ValueError:
283             print "'%s' not in artist list!?" % (song[ARTIST_COLUMN])
284             self.artist_button.set_value ("")
285         except AttributeError:
286             print "WARNING: Use set_artist_alternatives method to set a list of artists"
287             
288         try:
289             self.album_button.set_active (self.albums_list.index (song[ALBUM_COLUMN]))
290         except ValueError:
291             print "'%s' is not in the album list!?" % (song[ALBUM_COLUMN])
292             self.album_button.set_value ("")
293         except AttributeError:
294             print "WARNING: Use set_album_alternatives method to set a list of artists"
295
296         # Reconnect the signals!
297         self.album_change_handler = self.album_button.connect ("value-changed",
298                                                                self.album_selection_cb)
299
300         self.artist_change_handler = self.artist_button.connect ("value-changed",
301                                                                  self.artist_selection_cb)
302
303         # Set the album art given the current data
304         self.set_album_art (song[ALBUM_COLUMN])
305
306         if (not song[MIME_COLUMN] in self.writer.get_supported_mimes ()):
307             self.artist_button.set_sensitive (False)
308             self.album_button.set_sensitive (False)
309             self.title_entry.set_sensitive (False)
310         else:
311             self.artist_button.set_sensitive (True)
312             self.album_button.set_sensitive (True)
313             self.title_entry.set_sensitive (True)
314
315     def set_album_art (self, album):
316         has_album = False
317         if (album):
318             thumb = album_art_spec.getCoverArtThumbFileName (album)
319             print "%s -> %s" % (album, thumb)
320             if (os.path.exists (thumb)):
321                 self.album_art.set_from_file (thumb)
322                 has_album = True
323             
324         if (not has_album):
325             self.album_art.set_from_stock (gtk.STOCK_CDROM, gtk.ICON_SIZE_DIALOG)
326
327         
328     def clicked_play (self, widget):
329         if (self.player.is_playing ()):
330             self.player.stop ()
331         else:
332             song = self.songs_list [self.song_counter]
333             self.player.play ("file://" + song[URI_COLUMN])
334
335     def album_selection_cb (self, widget):
336         """
337         On album change, add the album the local list of albums and the selector
338         if it doesn't exist already. So we show the new entry in the selector next time.
339         """
340         song = self.get_current_row ()
341         if (not widget.get_value () in self.albums_list):
342             print "Inserting ", widget.get_value ()
343             widget.get_selector ().prepend_text (widget.get_value ())
344             self.albums_list.insert (0, widget.get_value ())
345         self.set_album_art (widget.get_value ())
346
347     def artist_selection_cb (self, widget):
348         """
349         On artist change, add the artist the local list of artists and the selector
350         if it doesn't exist already. So we show the new entry in the selector next time
351         """
352         song = self.get_current_row ()
353         if (not widget.get_value () in self.artists_list):
354             print "Inserting artist", widget.get_value ()
355             widget.get_selector ().prepend_text (widget.get_value ())
356             self.artists_list.insert (0, widget.get_value ())
357
358 # Testing porpuses
359 if __name__ == "__main__":
360
361     TEST_DATA = [("/home/user/Music/dylan.mp3", "Music", "Bob Dylan", "Subterranean homesick blues", "Bring it all back home", "audio/mpeg"),
362                  ("/home/user/mufix/a.mp3", "Music", "", "title", "Album 2", "a/b"),
363                  ("/media/mmc1/Attachments/b.m4a", "Music", "", "b", "Album 9", "audio/mpeg"),
364                  ("/home/user/mufix/3.mp2", "Music", "", "titlex", "Album 3", "audio/mpeg")]
365
366     model = gtk.ListStore (str, str, str, str, str, str)
367     for t in TEST_DATA:
368         model.append (t)
369
370     window = MussorgskyEditPanel ()
371     window.set_artist_alternatives (["", "Bob Dylan"])
372     window.set_album_alternatives (["", "Bring it all back home", "Album 2", "Album 9", "Album 3"])
373     window.set_model (model)
374     window.connect ("destroy", gtk.main_quit)
375     window.show_all ()
376     gtk.main ()