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