tiny color change
[jamaendo] / jamaui / playerwindow.py
1 #!/usr/bin/env python
2 #
3 # This file is part of Jamaendo.
4 # Copyright (c) 2010 Kristoffer Gronlund
5 #
6 # Jamaendo is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
10 #
11 # Jamaendo is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with Jamaendo.  If not, see <http://www.gnu.org/licenses/>.
18 #
19 # Player code heavily based on http://thpinfo.com/2008/panucci/:
20 #  A resuming media player for Podcasts and Audiobooks
21 #  Copyright (c) 2008-05-26 Thomas Perl <thpinfo.com>
22 #  (based on http://pygstdocs.berlios.de/pygst-tutorial/seeking.html)
23 #
24 import gtk
25 import gobject
26 import hildon
27 import util
28 import pango
29 import jamaendo
30 from settings import settings
31 from postoffice import postoffice
32 from player import Playlist, the_player
33 import logging
34 import cgi
35
36 from songposition import SongPosition
37 from listbox import ListDialog
38 import colors
39 log = logging.getLogger(__name__)
40
41 class PlayerWindow(hildon.StackableWindow):
42     instance = None
43
44     def __init__(self):
45         hildon.StackableWindow.__init__(self)
46         self.set_title("jamaendo")
47
48         PlayerWindow.instance = self
49
50         self.connect('destroy', self.on_destroy)
51
52         self.player = the_player
53         self.playlist = the_player.playlist
54
55         vbox = gtk.VBox()
56
57         hbox = gtk.HBox(False, 8)
58
59         self.cover = gtk.Image()
60         self.set_default_cover()
61
62         vbox2 = gtk.VBox()
63
64         self.playlist_pos = gtk.Label()
65         self.playlist_pos.set_alignment(1.0,0)
66         self.track = gtk.Label()
67         self.track.set_alignment(0,1)
68         self.track.set_ellipsize(pango.ELLIPSIZE_END)
69         self.artist = gtk.Label()
70         self.artist.set_alignment(0,0.5)
71         self.artist.set_ellipsize(pango.ELLIPSIZE_END)
72         self.album = gtk.Label()
73         self.album.set_alignment(0,0)
74         self.album.set_ellipsize(pango.ELLIPSIZE_END)
75         self.progress = SongPosition()
76
77         self.set_labels('', '', '', 0, 0)
78
79         self._position_timer = None
80
81         vbox2.pack_start(self.playlist_pos, False)
82         vbox2.pack_start(self.track, True)
83         vbox2.pack_start(self.artist, True)
84         vbox2.pack_start(self.album, True)
85
86         hbox.pack_start(self.cover, False, True, 0)
87         hbox.pack_start(vbox2, True, True, 0)
88
89         vbox.pack_start(hbox, True, True, 0)
90
91         btns = gtk.HButtonBox()
92         btns.set_property('layout-style', gtk.BUTTONBOX_SPREAD)
93
94         vbox.pack_start(btns, False, True, 0)
95
96         vbox.pack_start(self.progress, False)
97
98         self.add_stock_button(btns, gtk.STOCK_MEDIA_PREVIOUS, self.on_prev)
99         self.add_play_button(btns)
100         self.add_stock_button(btns, gtk.STOCK_MEDIA_STOP, self.on_stop)
101         self.add_stock_button(btns, gtk.STOCK_MEDIA_NEXT, self.on_next)
102
103         self.add(vbox)
104
105         postoffice.connect('album-cover', self, self.set_album_cover)
106         postoffice.connect(['next', 'prev', 'play', 'pause', 'stop'], self, self.on_state_changed)
107
108         self.on_state_changed()
109
110         self.create_menu()
111
112     def create_menu(self):
113         self.menu = hildon.AppMenu()
114
115         def to_artist(*args):
116             from showartist import ShowArtist
117             track = self.playlist.current()
118             artist = jamaendo.get_artist(int(track.artist_id))
119             wnd = ShowArtist(artist)
120             wnd.show_all()
121         def to_album(*args):
122             from showalbum import ShowAlbum
123             track = self.playlist.current()
124             album = jamaendo.get_album(int(track.album_id))
125             wnd = ShowAlbum(album)
126             wnd.show_all()
127
128         b = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
129         b.set_label("Artist")
130         b.connect("clicked", to_artist)
131         self.menu.append(b)
132
133         b = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
134         b.set_label("Album")
135         b.connect("clicked", to_album)
136         self.menu.append(b)
137
138         b = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
139         b.set_label("Add to playlist")
140         b.connect("clicked", self.on_add_to_playlist)
141         self.menu.append(b)
142
143         self.menu.show_all()
144         self.set_app_menu(self.menu)
145
146     def on_state_changed(self, *args):
147         self.update_state()
148         self.update_play_button()
149
150         if self.player.playing():
151             self.start_position_timer()
152         else:
153             self.stop_position_timer()
154
155
156     def get_album_id(self):
157         if self.player.playlist and self.player.playlist.current():
158             c = self.player.playlist.current()
159             if not c.album_id:
160                 c.load()
161             if c.album_id:
162                 return c.album_id
163         return None
164
165     def on_destroy(self, wnd):
166         PlayerWindow.instance = None
167         self.stop_position_timer()
168         postoffice.disconnect(['album-cover', 'next', 'prev', 'play', 'stop'], self)
169
170     def add_stock_button(self, btns, stock, cb):
171         btn = hildon.GtkButton(gtk.HILDON_SIZE_FINGER_HEIGHT)
172         btn.set_relief(gtk.RELIEF_NONE)
173         btn.set_focus_on_click(False)
174         sz = gtk.ICON_SIZE_BUTTON
175         btn.set_image(gtk.image_new_from_stock(stock, sz))
176         btn.connect('clicked', cb)
177         btns.add(btn)
178
179     def add_play_button(self, btns):
180         sz = gtk.ICON_SIZE_BUTTON
181         self.playimg = gtk.image_new_from_stock(gtk.STOCK_MEDIA_PLAY, sz)
182         self.pauseimg = gtk.image_new_from_stock(gtk.STOCK_MEDIA_PAUSE, sz)
183         btn = hildon.GtkButton(gtk.HILDON_SIZE_FINGER_HEIGHT)
184         btn.set_relief(gtk.RELIEF_NONE)
185         if self.player.playing():
186             btn.set_image(self.pauseimg)
187             btn.set_data('state', 'pause')
188         else:
189             btn.set_image(self.playimg)
190             btn.set_data('state', 'play')
191         btn.connect('clicked', self.on_play)
192         btns.add(btn)
193         self.playbtn = btn
194
195     def update_play_button(self):
196         if self.player.playing():
197             self.playbtn.set_image(self.pauseimg)
198             self.playbtn.set_data('state', 'pause')
199         else:
200             self.playbtn.set_image(self.playimg)
201             self.playbtn.set_data('state', 'play')
202
203     def set_labels(self, track, artist, album, playlist_pos, playlist_size):
204
205         if self.playlist.radio_mode:
206             ppstr = '<span size="small">Radio: %s</span>'%(cgi.escape(self.playlist.radio_name))
207         else:
208             ppstr = '<span font_desc="%s" foreground="%s">Track %s of %s</span>'%(colors.SmallSystemFont, colors.SecondaryTextColor, int(playlist_pos)+1, playlist_size)
209         self.playlist_pos.set_markup(ppstr)
210         self.track.set_markup('<span font_desc="%s">%s</span>'%(colors.LargeSystemFont, cgi.escape(track)))
211         self.artist.set_markup('%s'%(cgi.escape(artist)))
212         self.album.set_markup('<span foreground="%s">%s</span>'%(colors.SecondaryTextColor, cgi.escape(album)))
213
214     def show_banner(self, message, timeout = 2000):
215         banner = hildon.hildon_banner_show_information(self, '', message)
216         banner.set_timeout(2000)
217
218     def on_add_to_playlist(self, button, user_data=None):
219         track = self.player.playlist.current()
220         from playlists import add_to_playlist
221         add_to_playlist(self, track)
222
223     def volume_changed_hildon(self, widget):
224         settings.volume = widget.get_level()/100.0
225
226     def mute_toggled(self, widget):
227         if widget.get_mute():
228             settings.volume = 0
229         else:
230             settings.volume = widget.get_level()/100.0
231
232     def on_position_timeout(self):
233         if the_player.playing():
234             self.set_song_position(*the_player.get_position_duration())
235         else:
236             log.debug("position timeout, but not playing")
237         return True
238
239     def start_position_timer(self):
240         if self._position_timer is not None:
241             self.stop_position_timer()
242         self._position_timer = gobject.timeout_add(1000, self.on_position_timeout)
243
244     def stop_position_timer(self):
245         if self._position_timer is not None:
246             gobject.source_remove(self._position_timer)
247             self._position_timer = None
248
249     def clear_position(self):
250         self.progress.set_position(0)
251
252     def set_song_position(self, time_elapsed, total_time):
253         value = (float(time_elapsed) / float(total_time)) if total_time else 0
254         self.progress.set_position(value)
255
256     def set_default_cover(self):
257         tmp = util.find_resource('album.png')
258         if tmp:
259             log.debug("Setting cover to %s", tmp)
260             self.cover.set_from_file(tmp)
261
262     def update_state(self):
263         item = self.playlist.current()
264         if item:
265             if not item.name:
266                 item.load()
267             self.set_labels(item.name, item.artist_name, item.album_name,
268                             self.playlist.current_index(), self.playlist.size())
269             self.set_default_cover()
270             postoffice.notify('request-album-cover', int(item.album_id), 300)
271         else:
272             self.set_labels('', '', '', 0, 0)
273             self.set_default_cover()
274
275     def set_album_cover(self, albumid, size, cover):
276         if size == 300:
277             playing = self.get_album_id()
278             if playing and albumid and (int(playing) == int(albumid)):
279                 log.debug("Setting cover to %s", cover)
280                 self.cover.set_from_file(cover)
281
282     def play_radio(self, radio_name, radio_id):
283         playlist = Playlist([])
284         playlist.radio_mode = True
285         playlist.radio_name = radio_name
286         playlist.radio_id = radio_id
287         playlist.add(jamaendo.get_radio_tracks(playlist.radio_id))
288         log.debug("Playing radio: %s", playlist)
289         self.__play_tracks(playlist)
290         log.debug("Playlist current: %s, playing? %s",
291                   playlist.current_index(),
292                   self.player.playing())
293
294     def play_tracks(self, tracks):
295         self.__play_tracks(tracks)
296
297     def __play_tracks(self, tracks):
298         self.clear_position()
299         if isinstance(tracks, Playlist):
300             self.playlist = tracks
301         else:
302             self.playlist = Playlist(tracks)
303         log.debug("Playing: %s", self.playlist)
304         self.player.stop()
305         self.player.play(self.playlist)
306
307     def on_play(self, button):
308         if not self.player.playing():
309             self.player.play(self.playlist)
310         else:
311             self.player.pause()
312     def on_prev(self, button):
313         self.player.prev()
314
315     def on_next(self, button):
316         self.player.next()
317
318     def on_stop(self, button):
319         self.clear_position()
320         self.player.stop()
321
322 def open_playerwindow():
323     if PlayerWindow.instance:
324         player = PlayerWindow.instance
325         stack = player.get_stack()
326         sz = stack.size()
327         windows = stack.pop(sz)
328         windows.remove(player)
329         windows.append(player)
330         stack.push_list(windows)
331     else:
332         player = PlayerWindow()
333         player.show_all()
334     return player