Back to TouchSelectorEntry for artist and albums
[mussorgsky] / src / edit_panel.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 import album_art_spec
7 from album_art import MussorgskyAlbumArt
8 import os
9
10 # Fields in the tuple!
11 FILE_URI = 0
12 ARTIST_KEY = 2
13 TITLE_KEY = 3
14 ALBUM_KEY = 4
15 MIME_KEY = 5
16
17 class MussorgskyEditPanel (hildon.StackableWindow):
18
19     def __init__ (self, songs_list=None, albums_list=None, artists_list=None):
20         hildon.StackableWindow.__init__ (self)
21         self.set_border_width (12)
22         self.song_counter = 0
23         self.album_callback_id = -1
24         self.album_change_handler = -1
25         self.artist_change_handler = -1
26         self.writer = MutagenBackend ()
27         self.player = MediaPlayer ()
28         self.album_art_retriever = MussorgskyAlbumArt ()
29         self.albums_list = [a [0] for a in albums_list]
30         self.artists_list = [a [0] for a in artists_list]
31         self.add (self.__create_view ())
32         if (songs_list):
33             self.set_songs_list (songs_list)
34         self.update_title ()
35         self.banner = None
36
37     def update_title (self):
38         self.set_title ("Edit (%d/%d)" % (self.song_counter+1, len (self.songs_list)))
39
40         
41     def set_songs_list (self, songs_list):
42         if (songs_list and len (songs_list) > 0):
43             self.songs_list = songs_list
44             self.set_data_in_view (songs_list [0])
45             self.song_counter = 0
46
47     def press_back_cb (self, widget):
48         if (self.player.is_playing ()):
49             self.player.stop ()
50
51         if (self.banner and self.banner.get_property("visible")):
52             self.banner.destroy ()
53
54         if (self.album_callback_id != -1):
55             gobject.source_remove (self.album_callback_id)
56             self.album_callback_id = -1
57
58         if self.__is_view_dirty ():
59             print "Modified data. Save!"
60             self.save_metadata ()
61             
62         if (self.song_counter > 0):
63             self.song_counter -= 1
64             self.set_data_in_view (self.songs_list [self.song_counter])
65             self.update_title ()
66         else:
67             self.destroy ()
68
69     def press_next_cb (self, widget):
70         if (self.player.is_playing ()):
71             self.player.stop ()
72
73         if (self.banner and self.banner.get_property("visible")):
74             self.banner.destroy ()
75
76         if (self.album_callback_id != -1):
77             gobject.source_remove (self.album_callback_id)
78             self.album_callback_id = -1
79
80         if self.__is_view_dirty ():
81             print "Modified data. Save!"
82             self.save_metadata ()
83
84         if (self.song_counter < len (self.songs_list) -1):
85             self.song_counter += 1
86             self.set_data_in_view (self.songs_list [self.song_counter])
87             self.update_title ()
88         else:
89             self.destroy ()
90
91     def save_metadata (self):
92         # Save the data in the online model to show the appropiate data
93         # in the UI while tracker process the update.
94         song = self.songs_list [self.song_counter]
95
96         new_song = (song[FILE_URI], song[1],
97                     self.artist_button.get_value (),
98                     self.title_entry.get_text (),
99                     self.album_button.get_value (),
100                     song[MIME_KEY])
101         self.songs_list [self.song_counter] = new_song
102         try:
103             self.writer.save_metadata_on_file (new_song[FILE_URI],
104                                                new_song[MIME_KEY],
105                                                self.artist_button.get_value (),
106                                                self.title_entry.get_text (),
107                                                self.album_button.get_value ())
108         except IOError, e:
109             # This error in case of tracker returning unexistent files.
110             # Uhm.... for instance after removing a memory card we are editing!
111             dialog = gtk.MessageDialog (self,
112                                         gtk.DIALOG_DESTROY_WITH_PARENT,
113                                         gtk.MESSAGE_ERROR,
114                                         gtk.BUTTONS_CLOSE,
115                                         "%s" % str(e));
116             dialog.run ()
117
118         
119
120     def __is_view_dirty (self):
121         """
122         True if the data has been modified in the widgets
123         """
124         song = self.songs_list [self.song_counter]
125
126         return not (self.filename_data.get_text() == song[FILE_URI] and
127                     self.artist_button.get_value () == song[ARTIST_KEY] and
128                     self.title_entry.get_text () == song[TITLE_KEY] and
129                     self.album_button.get_value () == song[ALBUM_KEY] )
130         
131
132     def __create_view (self):
133         view_vbox = gtk.VBox (homogeneous=False, spacing = 12)
134
135         filename_row = gtk.HBox ()
136         filename_label = gtk.Label ("Filename:")
137         filename_row.pack_start (filename_label, expand=False, padding=12);
138         self.filename_data = gtk.Label ("")
139         filename_row.pack_start (self.filename_data, expand=True)
140
141         play_button = hildon.Button (hildon.BUTTON_STYLE_NORMAL, hildon.BUTTON_ARRANGEMENT_HORIZONTAL)
142         img = gtk.image_new_from_stock (gtk.STOCK_MEDIA_PLAY, gtk.ICON_SIZE_BUTTON)
143         play_button.set_image (img)
144         play_button.connect ("clicked", self.clicked_play)
145         filename_row.pack_start (play_button, expand=False, fill=False)
146         view_vbox.pack_start (filename_row, expand=False);
147
148         central_panel = gtk.HBox (spacing=12)
149
150         table = gtk.Table (3, 2, False)
151         table.set_col_spacings (12)
152         table.set_row_spacings (12)
153
154         central_panel.pack_start (table, fill=True)
155         view_vbox.pack_start (central_panel, expand=True, fill=True)
156
157         # Title row
158         label_title = gtk.Label ("Title:")
159         table.attach (label_title, 0, 1, 0, 1, 0)
160         self.title_entry = gtk.Entry()
161         table.attach (self.title_entry, 1, 2, 0, 1)
162
163         # Artist row
164         artist_selector = hildon.TouchSelectorEntry (text=True)
165         for a in self.artists_list:
166             artist_selector.append_text (a)
167         self.artist_button = hildon.PickerButton (hildon.BUTTON_STYLE_NORMAL,
168                                                   hildon.BUTTON_ARRANGEMENT_HORIZONTAL)
169         self.artist_button.set_title ("Artist: ")
170         self.artist_button.set_selector (artist_selector)
171         table.attach (self.artist_button, 0, 2, 1, 2)
172
173
174         # Album row
175         album_selector = hildon.TouchSelectorEntry (text=True)
176         for a in self.albums_list:
177             album_selector.append_text (a)
178         self.album_button = hildon.PickerButton (hildon.BUTTON_STYLE_NORMAL,
179                                                  hildon.BUTTON_ARRANGEMENT_HORIZONTAL)
180         self.album_button.set_title ("Album: ")
181         self.album_button.set_selector (album_selector)
182         table.attach (self.album_button, 0, 2, 2, 3) 
183         
184
185         # Album art space
186         album_art_button = gtk.Button ()
187         self.album_art = gtk.Image ()
188         self.album_art.set_size_request (124, 124)
189         album_art_button.add (self.album_art)
190         album_art_button.connect ("clicked", self.clicked_album_art)
191         central_panel.pack_start (album_art_button, expand=False, fill=False)
192         
193         # Buttons row
194         button_box = gtk.HButtonBox ()
195         button_box.set_layout (gtk.BUTTONBOX_END)
196
197         back_button = hildon.Button (hildon.BUTTON_STYLE_NORMAL, hildon.BUTTON_ARRANGEMENT_HORIZONTAL)
198         img = gtk.image_new_from_stock (gtk.STOCK_GO_BACK, gtk.ICON_SIZE_BUTTON)
199         back_button.set_image (img)
200         back_button.connect ("clicked", self.press_back_cb)
201         button_box.pack_start (back_button, expand=True, fill=True, padding=6)
202         
203         next_button = hildon.Button (hildon.BUTTON_STYLE_NORMAL, hildon.BUTTON_ARRANGEMENT_HORIZONTAL)
204         img = gtk.image_new_from_stock (gtk.STOCK_GO_FORWARD, gtk.ICON_SIZE_BUTTON)
205         next_button.set_image (img)
206         next_button.connect ("clicked", self.press_next_cb)
207         button_box.pack_start (next_button, expand=True, fill=True, padding=6)
208         
209         view_vbox.pack_start (button_box, expand=False, fill=True, padding=6)
210         
211         return view_vbox
212
213
214     def set_data_in_view (self, song):
215         """
216         Place in the screen the song information.
217         Song is a tuple like (filename, 'Music', title, artist, album, mime)
218         """
219         assert len (song) == 6
220         
221         self.filename_data.set_text (song[FILE_URI])
222         self.title_entry.set_text (song[TITLE_KEY])
223         
224
225         # Disconnect the value-change signal to avoid extra album art retrievals
226         if (self.album_button.handler_is_connected (self.album_change_handler)):
227             self.album_button.disconnect (self.album_change_handler)
228             
229         if (self.artist_button.handler_is_connected (self.artist_change_handler)):
230             self.artist_button.disconnect (self.artist_change_handler)
231
232         # Set values in the picker buttons
233         try:
234             self.artist_button.set_active (self.artists_list.index(song[ARTIST_KEY]))
235         except ValueError:
236             print "'%s' not in artist list!?" % (song[ARTIST_KEY])
237             self.artist_button.set_value ("")
238             
239         try:
240             self.album_button.set_active (self.albums_list.index (song[ALBUM_KEY]))
241         except ValueError:
242             print "'%s' is not in the album list!?" % (song[ALBUM_KEY])
243             self.album_button.set_value ("")
244
245         # Reconnect the signals!
246         self.album_change_handler = self.album_button.connect ("value-changed",
247                                                                self.album_selection_cb)
248
249         self.artist_change_handler = self.artist_button.connect ("value-changed",
250                                                                  self.artist_selection_cb)
251
252         # Set the album art given the current data
253         has_album = False
254         if (song[ALBUM_KEY]):
255             thumb = album_art_spec.getCoverArtThumbFileName (song[ALBUM_KEY])
256             print "%s -> %s" % (song[ALBUM_KEY], thumb)
257             if (os.path.exists (thumb)):
258                 self.album_art.set_from_file (thumb)
259                 has_album = True
260             else:
261                 self.album_callback_id = gobject.idle_add (self.retrieve_album_art,
262                                                            song[ARTIST_KEY], song[ALBUM_KEY])
263                 
264         if (not has_album):
265             self.album_art.set_from_stock (gtk.STOCK_CDROM, gtk.ICON_SIZE_DIALOG)
266
267         if (not song[MIME_KEY] in self.writer.get_supported_mimes ()):
268             print "show notification"
269             self.banner = hildon.Banner ()
270             self.banner.set_text ("Unsupported format (%s)" % song[MIME_KEY])
271             self.banner.show_all ()
272
273     def retrieve_album_art (self, artist, album):
274         print "trying to get the album art"
275         (img, thumb) = self.album_art_retriever.get_album_art (artist, album)
276         if (thumb):
277             self.album_art.set_from_file (thumb)
278         else:
279             print "Unable to retrieve album art"
280
281         return False
282         
283     def clicked_play (self, widget):
284         if (self.player.is_playing ()):
285             self.player.stop ()
286         else:
287             song = self.songs_list [self.song_counter]
288             self.player.play ("file://" + song[FILE_URI])
289
290     def clicked_album_art (self, widget):
291         print "implement me, please"
292
293     def album_selection_cb (self, widget):
294         """
295         On album change, add the album the local list of albums and the selector
296         if it doesn't exist already. So we show the new entry in the selector next time.
297         """
298         song = self.songs_list [self.song_counter]
299         if (not widget.get_value () in self.albums_list):
300             print "Inserting ", widget.get_value ()
301             widget.get_selector ().prepend_text (widget.get_value ())
302             self.albums_list.insert (0, widget.get_value ())
303         self.retrieve_album_art (song[ARTIST_KEY], widget.get_value ())
304
305     def artist_selection_cb (self, widget):
306         """
307         On artist change, add the artist the local list of artists and the selector
308         if it doesn't exist already. So we show the new entry in the selector next time
309         """
310         song = self.songs_list [self.song_counter]
311         if (not widget.get_value () in self.artists_list):
312             print "Inserting artist", widget.get_value ()
313             widget.get_selector ().prepend_text (widget.get_value ())
314             self.artists_list.insert (0, widget.get_value ())
315     
316 # Testing porpuses
317 if __name__ == "__main__":
318
319     TEST_DATA = [("/home/user/Music/dylan.mp3", "Music", "Bob Dylan", "Subterranean homesick blues", "Bring it all back home", "audio/mpeg"),
320                  ("/home/user/mufix/a.mp3", "Music", "", "title", "Album 2", "a/b"),
321                  ("/media/mmc1/Attachments/b.m4a", "Music", "", "b", "Album 9", "audio/mpeg"),
322                  ("/home/user/mufix/3.mp2", "Music", "", "titlex", "Album 3", "audio/mpeg")]
323     #TEST_DATA = []
324     ALBUMS = [["Album %d" % i] for i in range (0, 10)] + [["Bring it all back home"]]
325     ARTISTS = [["Artist %d" % i] for i in range (0, 10)] + [["Bob Dylan"]]
326     window = MussorgskyEditPanel (TEST_DATA, ALBUMS, ARTISTS)
327     window.connect ("destroy", gtk.main_quit)
328     window.show_all ()
329     gtk.main ()