# makes a query internally to get the full story
_ARTIST_FIELDS = ['id', 'name', 'image']
-_ALBUM_FIELDS = ['id', 'name', 'image', 'artist_name', 'artist_id']
+_ALBUM_FIELDS = ['id', 'name', 'image', 'artist_name', 'artist_id', 'license_url']
_TRACK_FIELDS = ['id', 'name', 'image', 'artist_name', 'album_name', 'album_id', 'numalbum', 'duration']
_RADIO_FIELDS = ['id', 'name', 'idstr', 'image']
self.image = None
self.artist_name = None
self.artist_id = None
+ self.license_url = None
self.tracks = None # None means not downloaded
if json:
self.set_from_json(json)
def _needs_load(self):
- return self._needs_load_impl('name', 'image', 'artist_name', 'artist_id', 'tracks')
+ return self._needs_load_impl('name', 'image', 'artist_name', 'artist_id', 'license_url', 'tracks')
def _set_from(self, other):
- return self._set_from_impl(other, 'name', 'image', 'artist_name', 'artist_id', 'tracks')
+ return self._set_from_impl(other, 'name', 'image', 'artist_name', 'artist_id', 'license_url', 'tracks')
class Track(LazyQuery):
def __init__(self, ID, json=None):
# TODO: cache queries?
class Query(object):
- rate_limit = 1.0 # max queries per second
+ rate_limit = 1.1 # seconds between queries
last_query = time.time() - 1.5
@classmethod
self._covers[(int(m.group(1)), int(m.group(2)))] = fl
def fetch_cover(self, albumid, size):
- Query._ratelimit() # ratelimit cover fetching too?
coverdir = _COVERDIR if _COVERDIR else '/tmp'
to = os.path.join(coverdir, '%d-%d.jpg'%(albumid, size))
if not os.path.isfile(to):
class JamendoAPIException(Exception):
def __init__(self, url):
- Exception.__init__(url)
+ Exception.__init__(self, url)
def _update_cache(cache, new_items):
if not isinstance(new_items, list):
--- /dev/null
+import gtk
+import hildon
+import jamaendo
+from playerwindow import open_playerwindow
+from showartist import ShowArtist
+from showalbum import ShowAlbum
+from settings import settings
+import logging
+
+log = logging.getLogger(__name__)
+
+def _alist(l, match):
+ for key, value in l:
+ if key == match:
+ return value
+ return None
+
+class FavoritesWindow(hildon.StackableWindow):
+ def __init__(self):
+ hildon.StackableWindow.__init__(self)
+ self.set_title("Favorites")
+
+ if settings.user:
+ # Results list
+ self.panarea = hildon.PannableArea()
+ self.result_store = gtk.ListStore(str, int)
+ #self.result_store.append(['red'])
+ self.result_view = gtk.TreeView(self.result_store)
+ col = gtk.TreeViewColumn('Name')
+ self.result_view.append_column(col)
+ cell = gtk.CellRendererText()
+ col.pack_start(cell, True)
+ col.add_attribute(cell, 'text', 0)
+ self.result_view.set_search_column(0)
+ col.set_sort_column_id(0)
+ self.result_view.connect('row-activated', self.row_activated)
+
+ self.panarea.add(self.result_view)
+
+ self.idmap = {}
+ try:
+ for item in jamaendo.favorite_albums(settings.user):
+ self.idmap[item.ID] = item
+ self.result_store.append([self.get_item_text(item), item.ID])
+ except jamaendo.JamendoAPIException, e:
+ msg = "Query failed, is the user name '%s' correct?" % (settings.user)
+ banner = hildon.hildon_banner_show_information(self, '',
+ msg)
+ banner.set_timeout(3000)
+
+
+ def add_album(albumid):
+ album = jamaendo.get_album(albumid)
+ self.idmap[albumid] = album
+ self.result_store.append([self.get_item_text(album), albumid])
+
+ for item in settings.favorites:
+ try:
+ if isinstance(item, tuple) and len(item) == 2:
+ ftype, fid = item
+ if ftype == 'album':
+ add_album(fid)
+
+ except jamaendo.JamendoAPIException, e:
+ log.exception("jamaendo.get_album(%s)"%(fid))
+
+ self.add(self.panarea)
+
+ else:
+ vbox = gtk.VBox()
+ lbl = gtk.Label()
+ lbl.set_markup("""<span size="xx-large">jamendo.com
+in the settings dialog
+enter your username</span>
+""")
+ lbl.set_single_line_mode(False)
+ vbox.pack_start(lbl, True, False)
+ self.add(vbox)
+
+ def get_item_text(self, item):
+ if isinstance(item, jamaendo.Album):
+ return "%s - %s" % (item.artist_name, item.name)
+ elif isinstance(item, jamaendo.Track):
+ return "%s - %s" % (item.artist_name, item.name)
+ else:
+ return item.name
+
+ def make_button(self, text, subtext, callback):
+ button = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT,
+ hildon.BUTTON_ARRANGEMENT_VERTICAL)
+ button.set_text(text, subtext)
+
+ if callback:
+ button.connect('clicked', callback)
+
+ #image = gtk.image_new_from_stock(gtk.STOCK_INFO, gtk.ICON_SIZE_BUTTON)
+ #button.set_image(image)
+ #button.set_image_position(gtk.POS_RIGHT)
+
+ return button
+
+ def row_activated(self, treeview, path, view_column):
+ treeiter = self.result_store.get_iter(path)
+ title, _id = self.result_store.get(treeiter, 0, 1)
+ item = self.idmap[_id]
+ print _id, item
+ self.open_item(item)
+
+ def open_item(self, item):
+ if isinstance(item, jamaendo.Album):
+ wnd = ShowAlbum(item)
+ wnd.show_all()
+ elif isinstance(item, jamaendo.Artist):
+ wnd = ShowArtist(item)
+ wnd.show_all()
+ elif isinstance(item, jamaendo.Track):
+ wnd = open_playerwindow()
+ wnd.play_tracks([item])
--- /dev/null
+import gtk
+import hildon
+import jamaendo
+from playerwindow import open_playerwindow
+from showartist import ShowArtist
+from showalbum import ShowAlbum
+
+def _alist(l, match):
+ for key, value in l:
+ if key == match:
+ return value
+ return None
+
+class FeaturedWindow(hildon.StackableWindow):
+ features = (("Albums of the week",jamaendo.albums_of_the_week),
+ ("Tracks of the week",jamaendo.tracks_of_the_week),
+ ("New releases",jamaendo.new_releases)
+ )
+
+ def __init__(self, feature):
+ hildon.StackableWindow.__init__(self)
+ self.set_title("Featured")
+
+ self.featurefn = _alist(self.features, feature)
+
+ # Results list
+ self.panarea = hildon.PannableArea()
+ self.result_store = gtk.ListStore(str, int)
+ #self.result_store.append(['red'])
+ self.result_view = gtk.TreeView(self.result_store)
+ col = gtk.TreeViewColumn('Name')
+ self.result_view.append_column(col)
+ cell = gtk.CellRendererText()
+ col.pack_start(cell, True)
+ col.add_attribute(cell, 'text', 0)
+ self.result_view.set_search_column(0)
+ col.set_sort_column_id(0)
+ self.result_view.connect('row-activated', self.row_activated)
+
+ self.panarea.add(self.result_view)
+
+ self.idmap = {}
+ for item in self.featurefn():
+ self.idmap[item.ID] = item
+ self.result_store.append([self.get_item_text(item), item.ID])
+
+ self.add(self.panarea)
+
+ def get_item_text(self, item):
+ if isinstance(item, jamaendo.Album):
+ return "%s - %s" % (item.artist_name, item.name)
+ elif isinstance(item, jamaendo.Track):
+ return "%s - %s" % (item.artist_name, item.name)
+ else:
+ return item.name
+
+ def make_button(self, text, subtext, callback):
+ button = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT,
+ hildon.BUTTON_ARRANGEMENT_VERTICAL)
+ button.set_text(text, subtext)
+
+ if callback:
+ button.connect('clicked', callback)
+
+ #image = gtk.image_new_from_stock(gtk.STOCK_INFO, gtk.ICON_SIZE_BUTTON)
+ #button.set_image(image)
+ #button.set_image_position(gtk.POS_RIGHT)
+
+ return button
+
+ def row_activated(self, treeview, path, view_column):
+ treeiter = self.result_store.get_iter(path)
+ title, _id = self.result_store.get(treeiter, 0, 1)
+ item = self.idmap[_id]
+ print _id, item
+ self.open_item(item)
+
+ def open_item(self, item):
+ if isinstance(item, jamaendo.Album):
+ wnd = ShowAlbum(item)
+ wnd.show_all()
+ elif isinstance(item, jamaendo.Artist):
+ wnd = ShowArtist(item)
+ wnd.show_all()
+ elif isinstance(item, jamaendo.Track):
+ wnd = open_playerwindow()
+ wnd.play_tracks([item])
import gst
import util
import dbus
-import dbus.service
import jamaendo
+from settings import settings
log = logging.getLogger(__name__)
self.filesrc = None
self.filesrc_property = None
self.volume_control = None
- self.volume_multiplier = 1
+ self.volume_multiplier = 1.
self.volume_property = None
self.eos_callback = lambda: self.stop()
self.player = None
return False
- log.debug("Setting up for %s : %s", filetype, uri)
-
- # On maemo use software decoding to workaround some bugs their gst:
- # 1. Weird volume bugs in playbin when playing ogg or wma files
- # 2. When seeking the DSPs sometimes lie about the real position info
- if True:
- self._maemo_setup_playbin_player(uri)
- elif util.platform == 'maemo':
- if not self._maemo_setup_hardware_player(filetype):
- self._maemo_setup_software_player()
- log.debug( 'Using software decoding (maemo)' )
+ _first = False
+ if self.player is None:
+ _first = True
+ if False:
+ self._maemo_setup_playbin2_player(uri)
+ log.debug('Using playbin2 (maemo)')
+ elif util.platform == 'maemo':
+ self._maemo_setup_playbin_player()
+ log.debug('Using playbin (maemo)')
else:
- log.debug( 'Using hardware decoding (maemo)' )
- else:
- # This is for *ahem* "normal" versions of gstreamer
- self._setup_playbin_player()
- log.debug( 'Using playbin (non-maemo)' )
-
- self._set_uri_to_be_played(uri)
+ self._setup_playbin_player()
+ log.debug( 'Using playbin (non-maemo)' )
- bus = self.player.get_bus()
- bus.add_signal_watch()
- bus.connect('message', self._on_message)
+ bus = self.player.get_bus()
+ bus.add_signal_watch()
+ bus.connect('message', self._on_message)
+ self._set_volume_level(settings.volume)
- self._set_volume_level( 1 )
+ self._set_uri_to_be_played(uri)
self.play()
return True
return self.STATES.get(state, 'none')
return 'none'
+ def _get_position_duration(self):
+ try:
+ pos_int = self.player.query_position(self.time_format, None)[0]
+ dur_int = self.player.query_duration(self.time_format, None)[0]
+ except Exception, e:
+ log.exception('Error getting position')
+ pos_int = dur_int = 0
+ return pos_int, dur_int
+
def playing(self):
return self.get_state() == 'playing'
self.player.set_state(gst.STATE_NULL)
self.player = None
- def _maemo_setup_playbin_player(self, url):
+ def _maemo_setup_playbin2_player(self, url):
self.player = gst.parse_launch("playbin2 uri=%s" % (url,))
self.filesrc = self.player
self.filesrc_property = 'uri'
self.volume_multiplier = 1.
self.volume_property = 'volume'
- def _maemo_setup_hardware_player( self, filetype ):
- """ Setup a hardware player for mp3 or aac audio using
- dspaacsink or dspmp3sink """
-
- if filetype in [ 'mp3', 'aac', 'mp4', 'm4a' ]:
- self.player = gst.element_factory_make('playbin', 'player')
- self.filesrc = self.player
- self.filesrc_property = 'uri'
- self.volume_control = self.player
- self.volume_multiplier = 10.
- self.volume_property = 'volume'
- return True
- else:
- return False
-
- def _maemo_setup_software_player( self ):
- """
- Setup a software decoding player for maemo, this is the only choice
- for decoding wma and ogg or if audio is to be piped to a bluetooth
- headset (this is because the audio must first be decoded only to be
- re-encoded using sbcenc.
- """
-
- self.player = gst.Pipeline('player')
- src = gst.element_factory_make('gnomevfssrc', 'src')
- decoder = gst.element_factory_make('decodebin', 'decoder')
- convert = gst.element_factory_make('audioconvert', 'convert')
- resample = gst.element_factory_make('audioresample', 'resample')
- sink = gst.element_factory_make('dsppcmsink', 'sink')
-
- self.filesrc = src # pointer to the main source element
- self.filesrc_property = 'location'
- self.volume_control = sink
- self.volume_multiplier = 1
- self.volume_property = 'fvolume'
-
- # Add the various elements to the player pipeline
- self.player.add( src, decoder, convert, resample, sink )
-
- # Link what can be linked now, the decoder->convert happens later
- gst.element_link_many( src, decoder )
- gst.element_link_many( convert, resample, sink )
-
- # We can't link the two halves of the pipeline until it comes
- # time to start playing, this singal lets us know when it's time.
- # This is because the output from decoder can't be determined until
- # decoder knows what it's decoding.
- decoder.connect('pad-added',
- self._on_decoder_pad_added,
- convert.get_pad('sink') )
+ def _maemo_setup_playbin_player( self):
+ self.player = gst.element_factory_make('playbin2', 'player')
+ self.filesrc = self.player
+ self.filesrc_property = 'uri'
+ self.volume_control = self.player
+ self.volume_multiplier = 1.
+ self.volume_property = 'volume'
+ return True
def _setup_playbin_player( self ):
""" This is for situations where we have a normal (read: non-maemo)
assert 0 <= value <= 1
if self.volume_control is not None:
- vol = value * self.volume_multiplier
+ vol = value * float(self.volume_multiplier)
self.volume_control.set_property( self.volume_property, vol )
def _set_uri_to_be_played(self, uri):
t = message.type
if t == gst.MESSAGE_EOS:
- self.eos_callback()
log.info("End of stream")
+ self.eos_callback()
elif t == gst.MESSAGE_STATE_CHANGED:
- old, new, pending = message.parse_state_changed()
- log.info("State changed: %s -> %s -> %s", old, new, pending)
+ if (message.src == self.player and
+ message.structure['new-state'] == gst.STATE_PLAYING):
+ log.info("State changed to playing")
elif t == gst.MESSAGE_ERROR:
err, debug = message.parse_error()
log.critical( 'Error: %s %s', err, debug )
self.stop()
- else:
- log.info("? %s", message.type)
def set_eos_callback(self, cb):
self.eos_callback = cb
return self._current
def size(self):
- print type(self)
return len(self.items)
def __repr__(self):
- return "Playlist(%s)"%(", ".join([str(item) for item in self.items]))
+ return "Playlist(%s)"%(", ".join([str(item.ID) for item in self.items]))
-class Player(Playlist):
+class Player(object):
def __init__(self):
self.backend = PlayerBackend()
self.backend.set_eos_callback(self._on_eos)
import gtk
import hildon
import jamaendo
+from settings import settings
from player import Playlist, the_player
class PlayerWindow(hildon.StackableWindow):
track = pl.current()
self.set_labels(track.name, track.artist_name, track.album_name, pl.current_index(), pl.size())
else:
- self.set_labels('track name', 'artist', 'album', 0, 0)
+ self.set_labels('', '', '', 0, 0)
vbox2.pack_start(self.track, True)
vbox2.pack_start(self.artist, False)
self.add_stock_button(btns, gtk.STOCK_MEDIA_STOP, self.on_stop)
self.add_stock_button(btns, gtk.STOCK_MEDIA_NEXT, self.on_next)
+ self.volume = hildon.VVolumebar()
+ self.volume.set_property('can-focus', False)
+ self.volume.connect('level_changed', self.volume_changed_hildon)
+ self.volume.connect('mute_toggled', self.mute_toggled)
+ #self.__gui_root.main_window.connect( 'key-press-event',
+ # self.on_key_press )
+ hbox.pack_start(self.volume, False)
self.add(vbox)
print "Created player window, playlist: %s" % (self.playlist)
self.artist.set_markup('<span size="large">%s</span>'%(artist))
self.album.set_markup('<span foreground="#aaaaaa">%s</span>'%(album))
+
+ def volume_changed_hildon(self, widget):
+ settings.volume = widget.get_level()/100.0
+
+ def mute_toggled(self, widget):
+ if widget.get_mute():
+ settings.volume = 0
+ else:
+ settings.volume = widget.get_level()/100.0
+
def update_state(self):
item = self.playlist.current()
if item:
def on_stop(self, button):
self.player.stop()
-def open_playerwindow(tracks):
+def open_playerwindow(tracks=None):
player = PlayerWindow(tracks)
player.show_all()
return player
--- /dev/null
+# message central
+
+class PostOffice(object):
+ class Client(object):
+ def __init__(self):
+ self.tags = {}
+ def has_tag(self, tag):
+ return tag in self.tags
+ def notify(self, tags, data):
+ for tag in tags:
+ cb = self.tags.get(tag)
+ if cb:
+ cb(data)
+ def register(self, tag, callback):
+ self.tags[tag] = callback
+
+ def __init__(self):
+ self.clients = {}
+
+ def notify(self, tags, data):
+ if not isinstance(tags, list):
+ tags = [tags]
+ for client in clients:
+ client.notify(tags, data)
+
+ def register(self, client_id, tag, callback):
+ client = self.clients.get(client_id)
+ if not client:
+ client = Client()
+ self.clients[client_id] = client
+ client.register(tag, callback)
+
+postoffice = PostOffice()
+
+
--- /dev/null
+import gtk
+import hildon
+import jamaendo
+from playerwindow import open_playerwindow
+
+class RadiosWindow(hildon.StackableWindow):
+ def __init__(self):
+ hildon.StackableWindow.__init__(self)
+ self.set_title("Radios")
+
+ # Results list
+ self.panarea = hildon.PannableArea()
+ self.result_store = gtk.ListStore(str, int)
+ #self.result_store.append(['red'])
+ self.result_view = gtk.TreeView(self.result_store)
+ col = gtk.TreeViewColumn('Name')
+ self.result_view.append_column(col)
+ cell = gtk.CellRendererText()
+ col.pack_start(cell, True)
+ col.add_attribute(cell, 'text', 0)
+ self.result_view.set_search_column(0)
+ col.set_sort_column_id(0)
+ self.result_view.connect('row-activated', self.row_activated)
+
+ self.panarea.add(self.result_view)
+
+ self.radios = {}
+ hildon.hildon_gtk_window_set_progress_indicator(self, 1)
+ for item in jamaendo.starred_radios():
+ self.radios[item.ID] = item
+ self.result_store.append([self.radio_text(item), item.ID])
+ hildon.hildon_gtk_window_set_progress_indicator(self, 0)
+
+ self.add(self.panarea)
+
+ def radio_text(self, radio):
+ if radio.name and radio.idstr:
+ return "%s (%s)" % (radio.name, radio.idstr)
+ elif radio.name:
+ return radio.name
+ elif radio.idstr:
+ return radio.idstr
+ else:
+ return "Radio #%s" % (radio.ID)
+
+ def make_button(self, text, subtext, callback):
+ button = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT,
+ hildon.BUTTON_ARRANGEMENT_VERTICAL)
+ button.set_text(text, subtext)
+
+ if callback:
+ button.connect('clicked', callback)
+
+ #image = gtk.image_new_from_stock(gtk.STOCK_INFO, gtk.ICON_SIZE_BUTTON)
+ #button.set_image(image)
+ #button.set_image_position(gtk.POS_RIGHT)
+
+ return button
+
+ def row_activated(self, treeview, path, view_column):
+ treeiter = self.result_store.get_iter(path)
+ title, _id = self.result_store.get(treeiter, 0, 1)
+ item = self.radios[_id]
+ print _id, item
+ self.open_item(item)
+
+ def open_item(self, item):
+ hildon.hildon_gtk_window_set_progress_indicator(self, 1)
+ tracks = jamaendo.get_radio_tracks(item.ID)
+ hildon.hildon_gtk_window_set_progress_indicator(self, 0)
+ if tracks:
+ wnd = open_playerwindow()
+ wnd.play_tracks(tracks)
import hildon
import jamaendo
from playerwindow import open_playerwindow
+from showartist import ShowArtist
+from showalbum import ShowAlbum
class SearchWindow(hildon.StackableWindow):
def __init__(self):
vbox = gtk.VBox(False, 0)
- hbox = gtk.HBox()
+
+ # Results list
+ self.panarea = hildon.PannableArea()
+ self.result_store = gtk.ListStore(str, int)
+ #self.result_store.append(['red'])
+ self.result_view = gtk.TreeView(self.result_store)
+ col = gtk.TreeViewColumn('Name')
+ self.result_view.append_column(col)
+ cell = gtk.CellRendererText()
+ col.pack_start(cell, True)
+ col.add_attribute(cell, 'text', 0)
+ self.result_view.set_search_column(0)
+ col.set_sort_column_id(0)
+ self.result_view.connect('row-activated', self.row_activated)
+
+ self.panarea.add(self.result_view)
+ vbox.pack_start(self.panarea, True, True, 0)
+
+
+ # Create selector for search mode
+ self.mode_selector = hildon.TouchSelector(text=True)
+
+ self.mode_selector.append_text("Artists")
+ self.mode_selector.append_text("Albums")
+ self.mode_selector.append_text("Tracks")
+ self.mode = hildon.PickerButton(gtk.HILDON_SIZE_FINGER_HEIGHT,
+ hildon.BUTTON_ARRANGEMENT_VERTICAL)
+ self.mode.set_title("Search for")
+ self.mode.set_selector(self.mode_selector)
+ self.mode_selector.connect("changed", self.mode_changed)
+ #vbox.pack_start(self.mode, False)
+ self.mode.set_active(1)
+
+
+ # Search box
+ hbox = gtk.HBox(False, 0)
self.entry = hildon.Entry(gtk.HILDON_SIZE_FINGER_HEIGHT)
self.entry.set_placeholder("Search")
self.entry.connect('activate', self.on_search)
btn = hildon.GtkButton(gtk.HILDON_SIZE_FINGER_HEIGHT)
- btn.set_label("Go")
+ btn.set_label(">>")
btn.connect('clicked', self.on_search)
+ hbox.pack_start(self.mode, False)
hbox.pack_start(self.entry, True, True, 0)
hbox.pack_start(btn, False)
-
- btnbox = gtk.HBox()
- playbtn = hildon.GtkButton(gtk.HILDON_SIZE_FINGER_HEIGHT)
- playbtn.set_label("Play selected")
- playbtn.connect('clicked', self.play_selected)
- btnbox.pack_start(playbtn, False)
-
- self.results = hildon.TouchSelector(text=True)
- self.results.connect("changed", self.selection_changed)
- self.results.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
-
vbox.pack_start(hbox, False)
- vbox.pack_start(self.results, True, True, 0)
- vbox.pack_start(btnbox, False)
+
self.add(vbox)
self.idmap = {}
- self.pwnd = None
+ def mode_changed(self, selector, user_data):
+ current_selection = selector.get_current_text()
+ print current_selection
def on_search(self, w):
+ mode = self.mode.get_active()
txt = self.entry.get_text()
- for album in jamaendo.search_albums(query=txt):
- title = "%s - %s" % (album.artist_name, album.name)
- self.idmap[title] = album
- self.results.append_text(title)
-
- def selection_changed(self, results, userdata):
- pass
-
- def play_selected(self, btn):
- current_selection = self.results.get_current_text()
-
- album = self.idmap[current_selection]
- tracks = jamaendo.get_tracks(album.ID)
- if tracks:
- wnd = open_playerwindow(tracks)
- wnd.on_play(None)
+ self.result_store.clear()
+ if mode == 0:
+ for artist in jamaendo.search_artists(query=txt):
+ title = artist.name
+ self.idmap[artist.ID] = artist
+ self.result_store.append([title, artist.ID])
+ elif mode == 1:
+ for album in jamaendo.search_albums(query=txt):
+ title = "%s - %s" % (album.artist_name, album.name)
+ self.idmap[album.ID] = album
+ self.result_store.append([title, album.ID])
+ elif mode == 2:
+ for track in jamaendo.search_tracks(query=txt):
+ title = "%s - %s" % (track.artist_name, track.name)
+ self.idmap[track.ID] = track
+ self.result_store.append([title, track.ID])
+
+ def row_activated(self, treeview, path, view_column):
+ treeiter = self.result_store.get_iter(path)
+ title, _id = self.result_store.get(treeiter, 0, 1)
+ item = self.idmap[_id]
+ print _id, item
+ self.open_item(item)
+
+ def open_item(self, item):
+ if isinstance(item, jamaendo.Album):
+ wnd = ShowAlbum(item)
+ wnd.show_all()
+ elif isinstance(item, jamaendo.Artist):
+ wnd = ShowArtist(item)
+ wnd.show_all()
+ elif isinstance(item, jamaendo.Track):
+ wnd = open_playerwindow()
+ wnd.play_tracks([item])
--- /dev/null
+import cPickle, os
+import logging
+
+VERSION = 1
+log = logging.getLogger(__name__)
+
+class Settings(object):
+ defaults = {
+ 'volume':0.5,
+ 'user':None,
+ 'favorites':set([]) # local favorites - until we can sync back
+ }
+
+ def __init__(self):
+ self.__savename = "/tmp/jaemendo_uisettings"
+ for k,v in self.defaults.iteritems():
+ setattr(self, k, v)
+
+ def set_filename(self, savename):
+ self.__savename = savename
+
+ def favorite(self, album):
+ self.favorites.add(('album', album.ID))
+ self.save()
+
+ def load(self):
+ if not os.path.isfile(self.__savename):
+ return
+ try:
+ f = open(self.__savename)
+ settings = cPickle.load(f)
+ f.close()
+
+ if settings['version'] > VERSION:
+ log.warning("Settings version %s higher than current version (%s)",
+ settings['version'], VERSION)
+
+ for k in self.defaults.keys():
+ if k in settings:
+ setattr(self, k, settings[k])
+ except Exception, e:
+ log.exception('failed to load settings')
+
+ def save(self):
+ try:
+ settings = {
+ 'version':VERSION,
+ }
+ for k in self.defaults.keys():
+ settings[k] = getattr(self, k)
+ f = open(self.__savename, 'w')
+ cPickle.dump(settings, f)
+ f.close()
+ except Exception, e:
+ log.exception('failed to save settings')
+
+settings = Settings()
--- /dev/null
+import gtk
+import hildon
+import jamaendo
+from playerwindow import open_playerwindow
+from settings import settings
+import util
+
+class ShowAlbum(hildon.StackableWindow):
+ def __init__(self, album):
+ hildon.StackableWindow.__init__(self)
+ self.set_title("Album")
+ self.album = album
+
+ top_vbox = gtk.VBox()
+ top_hbox = gtk.HBox()
+ vbox1 = gtk.VBox()
+ self.cover = gtk.Image()
+ self.bbox = gtk.HButtonBox()
+ self.bbox.set_property('layout-style', gtk.BUTTONBOX_SPREAD)
+ self.goto_artist = self.make_imagebutton('artist', self.on_goto_artist)
+ self.download = self.make_imagebutton('download', self.on_download)
+ self.favorite = self.make_imagebutton('favorite', self.on_favorite)
+ self.license = self.make_imagebutton('license', self.on_license)
+ self.playbtn = hildon.GtkButton(gtk.HILDON_SIZE_FINGER_HEIGHT)
+ self.playbtn.set_label("Play album")
+ self.playbtn.set_border_width(0)
+ self.playbtn.connect('clicked', self.on_play)
+
+ vbox2 = gtk.VBox()
+ self.albumtitle = gtk.Label()
+ self.albumtitle.set_markup('<big>%s</big>' % (album.name))
+ self.artist = gtk.Label()
+ self.artist.set_markup('<span color="#cccccc">%s</span>'%(album.artist_name))
+ self.trackarea = hildon.PannableArea()
+
+ self.album_store = gtk.ListStore(int, str, int)
+ self.album_view = gtk.TreeView(self.album_store)
+ col0 = gtk.TreeViewColumn('Num')
+ col = gtk.TreeViewColumn('Name')
+ self.album_view.append_column(col0)
+ self.album_view.append_column(col)
+ cell0 = gtk.CellRendererText()
+ col0.pack_start(cell0, True)
+ col0.add_attribute(cell0, 'text', 0)
+ cell = gtk.CellRendererText()
+ col.pack_start(cell, True)
+ col.add_attribute(cell, 'text', 1)
+ self.album_view.set_search_column(1)
+ col.set_sort_column_id(0)
+ self.album_view.connect('row-activated', self.row_activated)
+
+ for track in jamaendo.get_tracks(album.ID):
+ self.album_store.append([track.numalbum, track.name, track.ID])
+
+ top_vbox.pack_start(self.albumtitle, False)
+ top_vbox.pack_start(top_hbox)
+ top_hbox.pack_start(vbox1, False)
+ top_hbox.pack_start(vbox2, True)
+ vbox1.pack_start(self.cover, True)
+ vbox1.pack_start(self.playbtn, False)
+ vbox1.pack_start(self.bbox, False)
+ self.bbox.add(self.goto_artist)
+ self.bbox.add(self.download)
+ self.bbox.add(self.favorite)
+ self.bbox.add(self.license)
+ vbox2.pack_start(self.artist, False)
+ vbox2.pack_start(self.trackarea, True)
+ self.trackarea.add(self.album_view)
+
+ self.add(top_vbox)
+
+ coverfile = jamaendo.get_album_cover(self.album.ID, size=200)
+ self.cover.set_from_file(coverfile)
+
+ self.show_all()
+
+ def make_imagebutton(self, name, cb):
+ btn = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
+ btn.set_relief(gtk.RELIEF_NONE)
+ img = util.find_resource('icon_%s.png'%(name))
+ if img:
+ btn.set_image(gtk.image_new_from_file(img))
+ else:
+ btn.set_image(gtk.image_new_from_stock(gtk.STOCK_MEDIA_STOP, gtk.ICON_SIZE_SMALL_TOOLBAR))
+ btn.set_border_width(0)
+ btn.connect('clicked', cb)
+ return btn
+
+ def on_goto_artist(self, btn):
+ artist = jamaendo.get_artist(int(self.album.artist_id))
+ self.open_item(artist)
+
+ def on_download(self, btn):
+ banner = hildon.hildon_banner_show_information(self, '', "Downloads disabled in this version")
+ banner.set_timeout(2000)
+
+ def on_favorite(self, btn):
+ settings.favorite(self.album)
+ banner = hildon.hildon_banner_show_information(self, '', "Favorite added")
+ banner.set_timeout(2000)
+
+
+ def on_license(self, btn):
+ url = self.album.license_url
+ import webbrowser
+ webbrowser.open_new(url)
+
+ def on_play(self, btn):
+ self.open_item(self.album)
+
+ def row_activated(self, treeview, path, view_column):
+ pass
+
+ def open_item(self, item):
+ if isinstance(item, jamaendo.Album):
+ tracks = jamaendo.get_tracks(item.ID)
+ if tracks:
+ wnd = open_playerwindow()
+ wnd.play_tracks(tracks)
+ elif isinstance(item, jamaendo.Artist):
+ from showartist import ShowArtist
+ wnd = ShowArtist(item)
+ wnd.show_all()
+ elif isinstance(item, jamaendo.Track):
+ wnd = open_playerwindow()
+ wnd.play_tracks([item])
--- /dev/null
+import gtk
+import hildon
+import jamaendo
+from playerwindow import open_playerwindow
+
+class ShowArtist(hildon.StackableWindow):
+ def __init__(self, artist):
+ hildon.StackableWindow.__init__(self)
+ self.set_title("Artist")
+ self.artist = artist
+
+ self.panarea = hildon.PannableArea()
+ vbox = gtk.VBox(False, 0)
+
+ name = gtk.Label()
+ name.set_markup('<big>%s</big>'%(artist.name))
+ vbox.pack_start(name, False)
+
+ self.album_store = gtk.ListStore(str, int)
+ self.album_view = gtk.TreeView(self.album_store)
+ col = gtk.TreeViewColumn('Name')
+ self.album_view.append_column(col)
+ cell = gtk.CellRendererText()
+ col.pack_start(cell, True)
+ col.add_attribute(cell, 'text', 0)
+ self.album_view.set_search_column(0)
+ col.set_sort_column_id(0)
+ self.album_view.connect('row-activated', self.row_activated)
+
+
+ self.panarea.add(self.album_view)
+ vbox.pack_start(self.panarea, True, True, 0)
+ self.add(vbox)
+
+ for album in jamaendo.get_albums(artist.ID):
+ self.album_store.append([album.name, album.ID])
+
+ def row_activated(self, treeview, path, view_column):
+ treeiter = self.album_store.get_iter(path)
+ title, _id = self.album_store.get(treeiter, 0, 1)
+ album = jamaendo.get_album(_id)
+ if isinstance(album, list):
+ album = album[0]
+ self.open_item(album)
+
+ def open_item(self, item):
+ if isinstance(item, jamaendo.Album):
+ from showalbum import ShowAlbum
+ wnd = ShowAlbum(item)
+ wnd.show_all()
+ elif isinstance(item, jamaendo.Artist):
+ wnd = ShowArtist(item)
+ wnd.show_all()
+ elif isinstance(item, jamaendo.Track):
+ wnd = open_playerwindow()
+ wnd.play_tracks([item])
import gobject
import util
import logging
+from settings import settings
import ossohelper
log = logging.getLogger(__name__)
+VERSION = '1'
+
try:
import hildon
except:
DBusGMainLoop(set_as_default=True)
+import jamaendo
+
from playerwindow import open_playerwindow
from search import SearchWindow
-
-class ArtistWindow(hildon.StackableWindow):
- def __init__(self):
- hildon.StackableWindow.__init__(self)
- self.set_title("View Artist")
-
- label = gtk.Label("Artist information")
- vbox = gtk.VBox(False, 0)
- vbox.pack_start(label, True, True, 0)
- self.add(vbox)
-
-class AlbumWindow(hildon.StackableWindow):
- def __init__(self):
- hildon.StackableWindow.__init__(self)
- self.set_title("View Album")
-
- label = gtk.Label("Album information")
- vbox = gtk.VBox(False, 0)
- vbox.pack_start(label, True, True, 0)
- self.add(vbox)
-
-class RadiosWindow(hildon.StackableWindow):
- def __init__(self):
- hildon.StackableWindow.__init__(self)
- self.set_title("Radios")
-
- label = gtk.Label("Radios")
- vbox = gtk.VBox(False, 0)
- vbox.pack_start(label, True, True, 0)
- self.add(vbox)
-
-class FeaturedWindow(hildon.StackableWindow):
- def __init__(self):
- hildon.StackableWindow.__init__(self)
- self.set_title("Featured")
-
- label = gtk.Label("featured")
- vbox = gtk.VBox(False, 0)
- vbox.pack_start(label, True, True, 0)
- self.add(vbox)
+from featured import FeaturedWindow
+from radios import RadiosWindow
+from favorites import FavoritesWindow
class PlaylistsWindow(hildon.StackableWindow):
def __init__(self):
hildon.StackableWindow.__init__(self)
self.set_title("Playlists")
- label = gtk.Label("playlists")
- vbox = gtk.VBox(False, 0)
- vbox.pack_start(label, True, True, 0)
- self.add(vbox)
-
-class FavoritesWindow(hildon.StackableWindow):
- def __init__(self):
- hildon.StackableWindow.__init__(self)
- self.set_title("Favorites")
-
- label = gtk.Label("favorites")
+ label = gtk.Label("Playlists")
vbox = gtk.VBox(False, 0)
vbox.pack_start(label, True, True, 0)
self.add(vbox)
class Jamaui(object):
- _BG = 'bg.png' # /opt/jamaendo/data/bg.png
-
def __init__(self):
self.app = None
self.menu = None
self.window.set_title("jamaendo")
self.window.connect("destroy", self.destroy)
+ self.CONFDIR = os.path.expanduser('~/MyDocs/.jamaendo')
+ jamaendo.set_cache_dir(self.CONFDIR)
+ settings.set_filename(os.path.join(self.CONFDIR, 'ui_settings'))
+ settings.load()
+
+ def save_settings(self):
+ settings.save()
+
def create_menu(self):
self.menu = hildon.AppMenu()
player.connect("clicked", self.on_favorites)
self.menu.append(player)
+ #player = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
+ #player.set_label("Playlists")
+ #player.connect("clicked", self.on_playlists)
+ #self.menu.append(player)
+
player = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
- player.set_label("Playlists")
- player.connect("clicked", self.on_playlists)
+ player.set_label("Settings")
+ player.connect("clicked", self.on_settings)
self.menu.append(player)
+
# Don't use localdb ATM
#refresh = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
#refresh.set_label("Refresh")
self.menu.show_all()
self.window.set_app_menu(self.menu)
- def find_resource(self, name):
- if os.path.isfile(os.path.join('data', name)):
- return os.path.join('data', name)
- elif os.path.isfile(os.path.join('/opt/jaemendo', name)):
- return os.path.join('/opt/jaemendo', name)
- elif os.path.isfile(os.path.join('/usr/share/jaemendo', name)):
- return os.path.join('/usr/share/jaemendo', name)
- else:
- return None
def setup_widgets(self):
- bgimg = self.find_resource(self._BG)
+ bgimg = util.find_resource('bg.png')
if bgimg:
background, mask = gtk.gdk.pixbuf_new_from_file(bgimg).render_pixmap_and_mask()
self.window.realize()
btn.connect('clicked', callback)
self.bbox.add(btn)
+ #def add_featured_button(self):
+ # self.featured_sel = hildon.TouchSelector(text=True)
+ # self.featured_sel.append_text("Albums of the week")
+ # self.featured_sel.append_text("Tracks of the week")
+ # self.featured_sel.append_text("New releases")
+ # btn = hildon.PickerButton(gtk.HILDON_SIZE_THUMB_HEIGHT,
+ # hildon.BUTTON_ARRANGEMENT_VERTICAL)
+ # btn.set_text("Featured", "Most listened to")
+ # btn.set_property('width-request', 225)
+ # btn.set_selector(self.featured_sel)
+ # self.featured_btn = btn
+ # self.bbox.add(btn)
+
def destroy(self, widget):
gtk.main_quit()
def show_about(self, w, win):
dialog = gtk.AboutDialog()
+ dialog.set_program_name("jamaendo")
dialog.set_website("http://github.com/krig")
dialog.set_website_label("http://github.com/krig")
- dialog.set_authors(("Kristoffer Gronlund (Purple Scout AB)",))
+ dialog.set_version(VERSION)
+ dialog.set_authors(("Kristoffer Gronlund <kristoffer.gronlund@purplescout.se>",))
dialog.set_comments("""Jamaendo plays music from the music catalog of JAMENDO.
JAMENDO is an online platform that distributes musical works under Creative Commons licenses.""")
- dialog.set_version('')
+ gtk.about_dialog_set_email_hook(self.open_link, dialog)
+ gtk.about_dialog_set_url_hook(self.open_link, dialog)
+ dialog.connect( 'response', lambda dlg, response: dlg.destroy())
+ for parent in dialog.vbox.get_children():
+ for child in parent.get_children():
+ if isinstance(child, gtk.Label):
+ child.set_selectable(False)
+ child.set_alignment(0.0, 0.5)
dialog.run()
dialog.destroy()
def open_link(self, d, url, data):
+ print "url: %s" % (url)
import webbrowser
webbrowser.open_new(url)
# dialog.hide()
def on_featured(self, button):
- self.featuredwnd = FeaturedWindow()
- self.featuredwnd.show_all()
+ dialog = hildon.PickerDialog(self.window)
+ sel = hildon.TouchSelector(text=True)
+ for feature, _ in FeaturedWindow.features:
+ sel.append_text(feature)
+ dialog.set_selector(sel)
+ dialog.set_title("Featured")
+ if dialog.run() == gtk.RESPONSE_OK:
+ txt = sel.get_current_text()
+ self.featuredwnd = FeaturedWindow(txt)
+ self.featuredwnd.show_all()
+ dialog.destroy()
def on_radios(self, button):
self.radioswnd = RadiosWindow()
self.playlistswnd = PlaylistsWindow()
self.playlistswnd.show_all()
+ def on_settings(self, button):
+ dialog = gtk.Dialog()
+ dialog.set_title("Settings")
+ dialog.add_button( gtk.STOCK_OK, gtk.RESPONSE_OK )
+ vbox = dialog.vbox
+ tbl = gtk.Table(1, 2)
+ tbl.attach(gtk.Label("Username:"), 0, 1, 0, 1)
+ entry = hildon.Entry(gtk.HILDON_SIZE_FINGER_HEIGHT)
+ entry.set_placeholder("jamendo.com username")
+ if settings.user:
+ entry.set_text(settings.user)
+ tbl.attach(entry, 1, 2, 0, 1)
+ vbox.pack_start(tbl, True, True, 2)
+ dialog.show_all()
+ result = dialog.run()
+ val = entry.get_text()
+ dialog.destroy()
+ print val, result
+ if val and result == gtk.RESPONSE_OK:
+ print "new user name:", val
+ settings.user = val
+ self.save_settings()
+
+
def on_favorites(self, button):
self.favoriteswnd = FavoritesWindow()
self.favoriteswnd.show_all()
def jsonprint(x):
print simplejson.dumps(x, sort_keys=True, indent=4)
+
+def find_resource(name):
+ if os.path.isfile(os.path.join('data', name)):
+ return os.path.join('data', name)
+ elif os.path.isfile(os.path.join('/opt/jaemendo', name)):
+ return os.path.join('/opt/jaemendo', name)
+ elif os.path.isfile(os.path.join('/usr/share/jaemendo', name)):
+ return os.path.join('/usr/share/jaemendo', name)
+ else:
+ return None
+