def _set_from(self, other):
return self._set_from_impl(other, 'name', 'image', 'albums')
+ def get_data(self):
+ return {'name':self.name, 'image':self.image}
+
class Album(LazyQuery):
def __init__(self, ID, json=None):
self.ID = int(ID)
def _set_from(self, other):
return self._set_from_impl(other, 'name', 'image', 'artist_name', 'artist_id', 'license_url', 'tracks')
+ def get_data(self):
+ return {'name':self.name, 'image':self.image,
+ 'artist_name':self.artist_name,
+ 'artist_id':self.artist_id,
+ 'license_url':self.license_url}
+
class Track(LazyQuery):
def __init__(self, ID, json=None):
self.ID = int(ID)
def ogg_url(self):
return _OGGURL%(self.ID)
+ def get_data(self):
+ return {'name':self.name,
+ 'artist_id':self.artist_id,
+ 'artist_name':self.artist_name,
+ 'album_image':self.album_image,
+ 'album_name':self.album_name,
+ 'album_id':self.album_id,
+ 'numalbum':self.numalbum,
+ 'duration':self.duration}
+
def _needs_load(self):
return self._needs_load_impl('name', 'artist_name', 'artist_id', 'album_name', 'album_id', 'numalbum', 'duration')
--- /dev/null
+# what the fuck, GTK
+import gtk
+import hildon
+
+class ListBox(gtk.TreeView):
+ def __init__(self):
+ gtk.TreeView.__init__(self)
+ self.store = gtk.ListStore(str)
+ column = gtk.TreeViewColumn("Text")
+ textRenderer = gtk.CellRendererText()
+ column.pack_start(textRenderer, True)
+ column.set_attributes(textRenderer, text = 0)
+ self.append_column(column)
+ self.set_model(self.store)
+
+ def get_text(self, path):
+ it = self.store.get_iter(path)
+ if not it:
+ return None
+ ret = self.store.get(it, 0)
+ return ret[0]
+
+ def get_selected_text(self):
+ model, it = self.get_selection().get_selected()
+ if not it:
+ return None
+ ret = self.store.get(it, 0)
+ return ret[0]
+
+ def append(self, text):
+ self.store.append([text])
+
+ def on_select(self, callback, *args):
+ def cb(lb, path, col):
+ ret = self.get_text(path)
+ if ret:
+ callback(ret, *args)
+ self.connect('row-activated', cb)
+
+class ListDialog(gtk.Dialog):
+ def __init__(self, title, parent=None):
+ gtk.Dialog.__init__(self, title, parent)
+ self.listbox = ListBox()
+ panarea = hildon.PannableArea()
+ panarea.add(self.listbox)
+ panarea.set_size_request(800, 300)
+ self.vbox.pack_start(panarea, True, True, 0)
+
+ self.selected = None
+
+ def selector(selected, dialog):
+ if selected:
+ self.selected = selected
+ dialog.response(gtk.RESPONSE_OK)
+ self.listbox.on_select(selector, self)
+
+class ButtonListDialog(gtk.Dialog):
+ def __init__(self, title, parent=None):
+ gtk.Dialog.__init__(self, title, parent)
+ self.panarea = hildon.PannableArea()
+ self.panarea.set_size_request(800, 400)
+ self.buttons = gtk.VBox(False, 0)
+ self.panarea.add_with_viewport(self.buttons)
+ self.vbox.pack_start(self.panarea, True, True, 0)
+
+ def add_button(self, label, clickcb, *args):
+ btn = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH|gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
+ btn.set_label(label)
+ btn.connect('clicked', clickcb, *args)
+ self.buttons.pack_end(btn, False, False, 0)
+
import cgi
from songposition import SongPosition
-
+from listbox import ListDialog
log = logging.getLogger(__name__)
class PlayerWindow(hildon.StackableWindow):
b.connect("clicked", to_album)
self.menu.append(b)
+ b = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
+ b.set_label("Add to playlist")
+ b.connect("clicked", self.on_add_to_playlist)
+ self.menu.append(b)
+
self.menu.show_all()
self.set_app_menu(self.menu)
self.artist.set_markup('<span size="large">%s</span>'%(cgi.escape(artist)))
self.album.set_markup('<span foreground="#aaaaaa">%s</span>'%(cgi.escape(album)))
+ def show_banner(self, message, timeout = 2000):
+ banner = hildon.hildon_banner_show_information(self, '', message)
+ banner.set_timeout(2000)
+
+ def on_add_to_playlist(self, button, user_data=None):
+ track = self.player.playlist.current()
+ from playlists import add_to_playlist
+ add_to_playlist(self, track)
def volume_changed_hildon(self, widget):
settings.volume = widget.get_level()/100.0
--- /dev/null
+#!/usr/bin/env python
+#
+# This file is part of Jamaendo.
+# Copyright (c) 2010 Kristoffer Gronlund
+#
+# Jamaendo is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Jamaendo is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Jamaendo. If not, see <http://www.gnu.org/licenses/>.
+#
+# Player code heavily based on http://thpinfo.com/2008/panucci/:
+# A resuming media player for Podcasts and Audiobooks
+# Copyright (c) 2008-05-26 Thomas Perl <thpinfo.com>
+# (based on http://pygstdocs.berlios.de/pygst-tutorial/seeking.html)
+#
+import gtk
+import hildon
+import jamaendo
+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
+
+def _show_banner(parent, message, timeout = 2000):
+ banner = hildon.hildon_banner_show_information(parent, '', message)
+ banner.set_timeout(2000)
+
+from listbox import ListDialog
+
+def add_to_playlist(wnd, track):
+ if not track:
+ _show_banner(wnd, "Nothing to add")
+ return
+
+ dialog = ListDialog('Add to playlist', wnd)
+ for name,_ in settings.playlists.iteritems():
+ dialog.listbox.append(name)
+ dialog.listbox.append("New...")
+ try:
+ dialog.show_all()
+ if dialog.run() == gtk.RESPONSE_OK:
+ selected_playlist = dialog.selected
+ if selected_playlist == "New...":
+ dialog.hide()
+ selected_playlist = create_new_playlist(wnd)
+ if track and selected_playlist:
+ if isinstance(track, (list, tuple)):
+ for t in track:
+ settings.add_to_playlist(selected_playlist, {'id':t.ID, 'data':t.get_data()})
+ else:
+ settings.add_to_playlist(selected_playlist, {'id':track.ID, 'data':track.get_data()})
+ settings.save()
+ _show_banner(wnd, "Added to playlist '%s'" % (selected_playlist))
+ finally:
+ dialog.destroy()
+
+def create_new_playlist(wnd):
+ dia_name = gtk.Dialog()
+ dia_name.set_title("New playlist")
+ dia_name.add_button( gtk.STOCK_OK, gtk.RESPONSE_OK )
+ entry = hildon.Entry(gtk.HILDON_SIZE_FINGER_HEIGHT)
+ entry.set_placeholder("Enter name")
+ entry.set_max_length(32)
+ entry.connect('activate', lambda entry, dialog: dialog.response(gtk.RESPONSE_OK), dia_name)
+ dia_name.vbox.pack_start(entry, True, True, 0)
+ dia_name.show_all()
+ if dia_name.run() != gtk.RESPONSE_OK:
+ return False
+ selected_playlist = entry.get_text()
+ dia_name.destroy()
+ if selected_playlist == '' or selected_playlist == 'New...':
+ return False
+ elif settings.get_playlist(selected_playlist):
+ _show_banner(wnd, "Playlist '%s' already exists!" % (selected_playlist))
+ return False
+ return selected_playlist
+
+
+class PlaylistsWindow(hildon.StackableWindow):
+ def __init__(self):
+ hildon.StackableWindow.__init__(self)
+ self.set_title("Playlists")
+
+ self.panarea = hildon.PannableArea()
+
+ (self.COL_NAME, self.COL_INFO) = range(2)
+ self.store = gtk.ListStore(str, str)
+ self.treeview = gtk.TreeView()
+ self.treeview.set_model(self.store)
+
+ col = gtk.TreeViewColumn('Name')
+ self.treeview.append_column(col)
+ cell = gtk.CellRendererText()
+ col.pack_start(cell, True)
+ col.add_attribute(cell, 'text', self.COL_NAME)
+ self.treeview.set_search_column(self.COL_NAME)
+ col.set_sort_column_id(self.COL_NAME)
+
+ col = gtk.TreeViewColumn('Info')
+ self.treeview.append_column(col)
+ cell = gtk.CellRendererText()
+ col.pack_start(cell, True)
+ col.add_attribute(cell, 'text', self.COL_INFO)
+
+ self.treeview.connect('row-activated', self.row_activated)
+
+ self.panarea.add(self.treeview)
+
+ self.add(self.panarea)
+
+ def trackcount(lst):
+ ln = len(lst)
+ if ln > 1:
+ return "(%d tracks)"%(ln)
+ elif ln == 1:
+ return "(1 track)"
+ return "(empty)"
+
+ for key, lst in sorted(list(settings.playlists.iteritems())):
+ self.store.append([key, trackcount(lst)])
+
+ def row_activated(self, treeview, path, view_column):
+ name = self.store.get(self.store.get_iter(path), self.COL_NAME)[0]
+ pl = settings.get_playlist(name)
+ if pl:
+ from playerwindow import open_playerwindow
+ wnd = open_playerwindow()
+ wnd.play_tracks(pl)
#
import cPickle, os
import logging
+import jamaendo
+import datetime
from postoffice import postoffice
defaults = {
'volume':0.1,
'user':None,
- 'favorites':set([]) # local favorites - until we can sync back
+ 'favorites':set([]), # local favorites - until we can sync back
+ 'playlists':{},
}
def __init__(self):
def favorite(self, album):
self.favorites.add(('album', album.ID))
self.save()
+ postoffice.notify('settings-changed', 'favorites', self.favorites)
+
+ def get_playlist(self, playlist, get_track_objects=True):
+ entry = self.playlists.get(playlist)
+ if entry:
+ if get_track_objects:
+ return [jamaendo.Track(item['id'], item['data']) for item in entry]
+ return entry
+ return None
+
+ def add_to_playlist(self, playlist, track):
+ if isinstance(track, jamaendo.Track):
+ track = {'id':track.ID, 'data':track.get_data()}
+ assert(isinstance(track, dict))
+ lst = self.playlists.get(playlist)
+ if not lst:
+ lst = []
+ self.playlists[playlist] = lst
+ lst.append(track)
+ postoffice.notify('settings-changed', 'playlists', self.playlists)
+ log.debug("playlists is now %s", self.playlists)
def load(self):
if not os.path.isfile(self.__savename):
for k in self.defaults.keys():
if k in settings:
+ if k == 'playlists' and not isinstance(k, dict):
+ continue
setattr(self, k, settings[k])
+ print settings
except Exception, e:
log.exception('failed to load settings')
f = open(self.__savename, 'w')
cPickle.dump(settings, f)
f.close()
+ print settings
except Exception, e:
log.exception('failed to save settings')
import util
import logging
from albumlist import TrackList
+from playlists import add_to_playlist
+
import webbrowser
log = logging.getLogger(__name__)
player.set_label("Open player")
player.connect("clicked", on_player)
self.menu.append(player)
+ player = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
+ player.set_label("Add to playlist")
+ player.connect("clicked", self.on_add_to_playlist)
+ self.menu.append(player)
self.menu.show_all()
self.set_app_menu(self.menu)
if albumid == self.album.ID and size == 300:
self.cover.set_from_file(cover)
+
+ def on_add_to_playlist(self, button, user_data=None):
+ add_to_playlist(self, self.tracklist)
+
def make_imagebutton(self, name, cb):
btn = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
btn.set_relief(gtk.RELIEF_NONE)
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)
+from playlists import PlaylistsWindow
+from listbox import ButtonListDialog
class Jamaui(object):
def __init__(self):
postoffice.connect('request-images', self, self.on_request_images)
log.debug("Created main window.")
- 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)
+ self.menu.append(player)
player = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
player.set_label("Settings")
def destroy(self, widget):
postoffice.disconnect(['request-album-cover', 'request-images'], self)
- self.save_settings()
+ settings.save()
from player import the_player
if the_player:
the_player.stop()
# dialog.hide()
def on_featured(self, button):
- dialog = hildon.PickerDialog(self.window)
- sel = hildon.TouchSelectorEntry(text=True)
- for feature, _ in FeaturedWindow.features:
- sel.append_text(feature)
- dialog.set_selector(sel)
- dialog.set_title("Featured")
- sel.unselect_all(0)
- if dialog.run() == gtk.RESPONSE_OK:
- txt = sel.get_current_text()
- self.featuredwnd = FeaturedWindow(txt)
+ dialog = ButtonListDialog('Featured', self.window)
+ def fn(btn, feature):
+ self.featuredwnd = FeaturedWindow(feature)
self.featuredwnd.show_all()
+ dialog.response(gtk.RESPONSE_OK)
+ for feature, _ in FeaturedWindow.features:
+ dialog.add_button(feature, fn, feature)
+ dialog.show_all()
+ dialog.run()
dialog.destroy()
def on_radios(self, button):
if val and result == gtk.RESPONSE_OK:
#print "new user name:", val
settings.user = val
- self.save_settings()
+ settings.save()
def on_favorites(self, button):
import os, sys
local_module_dir = os.path.join(os.path.dirname(sys.argv[0]), '..')
if os.path.isdir(local_module_dir):
- sys.path.append(local_module_dir)
+ sys.path = [local_module_dir] + sys.path
def main():
from jamaui.ui import Jamaui