def __init__(self, client=None): super(MainWindow, self).__init__() if client is None: client = Client() def channel_compare_cb(model, a, b): a, = model.get(a, 0) b, = model.get(b, 0) return cmp(a.title, b.title) or cmp(a.tags, b.tags) or cmp(a.station.title, b.station.title) channels = gtk.ListStore(object) channels.set_sort_func(0, channel_compare_cb) channels.set_sort_column_id(0, gtk.SORT_ASCENDING) tag_completion = TagsCompletion(client.get_tags()) self.__current_title = None def read_wishlist(): filename = get_config_filename("wishlist") wishlist = [] try: text = file(filename).read().strip() wishlist = text and text.split("\n") or [] except IOError: pass return dict(zip(wishlist, range(len(wishlist)))) self.__wishlist = read_wishlist() def channel_added_cb(client, channel): tree_iter = channels.insert(-1, (channel,)) tag_completion.add(channel.tags) if channel == client.current_channel or ( channel.uri == self.__config.channel_uri and client.current_channel is None ): model = tree_view.get_model() tree_iter = model.convert_child_iter_to_iter(tree_iter) tree_view.get_selection().select_iter(tree_iter) def state_changed_cb(client): if client.is_playing: self.__config.channel_uri = client.current_channel.uri self.__play_button.set_active(True) stream_tags_changed_cb(client) else: self.__pause_button.set_active(True) self.__stream_info.hide() tree_view.queue_draw() def stream_tags_changed_cb(client): self.__current_title = client.stream_tags.get("title").strip() or None org = client.stream_tags.get("organization").strip() or None markup = [] if self.__current_title: markup.append("<b>%s</b>" % glib.markup_escape_text(self.__current_title)) if org: markup.append("<small>%s</small>" % glib.markup_escape_text(org)) if markup: self.__stream_info.set_markup(" - ".join(markup)) self.__stream_info.set_tooltip_markup("\n".join(markup)) self.__stream_info.show() else: self.__stream_info.set_markup("") self.__stream_info.hide() self.__favorite_button.set_sensitive(bool(self.__current_title)) self.__favorite_button.set_active(self.__current_title in self.__wishlist) self.__client = client self.__client.connect("channel-added", channel_added_cb) self.__client.connect("state-changed", state_changed_cb) self.__client.connect("stream-tags-changed", stream_tags_changed_cb) self.__client.wait(Client.STATE_STATIONS_LOADED) self.__filter_timeout = 0 self.__current_tags = [] self.__config = Configuration() def channel_visible_cb(model, iter): channel, = model.get(iter, 0) return channel.matches(self.__current_tags) matching_channels = gtk.TreeModel.filter_new(channels) matching_channels.set_visible_func(channel_visible_cb) self.set_title("WebRadio") self.set_default_size(500, 400) self.set_icon_name("rhythmbox") self.connect("destroy", gtk.main_quit) vbox = gtk.VBox() self.add(vbox) toolbar = gtk.Toolbar() toolbar.set_show_arrow(False) vbox.pack_start(toolbar, expand=False) def play_button_clicked_cb(button): if not button.get_active(): self.__client.pause() return model, tree_iter = tree_view.get_selection().get_selected() channel = None if model and tree_iter: channel, = model.get(tree_iter, 0) if channel is not None: if channel == self.__client.current_channel: self.__client.resume() else: self.__client.play(channel) def favorite_button_clicked_cb(button): if not self.__current_title: return if button.get_active(): self.__wishlist[self.__current_title] = True else: self.__wishlist.pop(self.__current_title, False) wishlist_text = "\n".join(self.__wishlist.keys()) + "\n" filename = get_config_filename("wishlist") file(filename, "w").write(wishlist_text) self.__play_button = gtk.RadioToolButton(None, gtk.STOCK_MEDIA_PLAY) self.__play_button.connect("clicked", play_button_clicked_cb) self.__play_button.set_is_important(True) toolbar.insert(self.__play_button, -1) self.__pause_button = gtk.RadioToolButton(self.__play_button, gtk.STOCK_MEDIA_PAUSE) toolbar.insert(self.__pause_button, -1) self.__favorite_button = gtk.ToggleToolButton(gtk.STOCK_ABOUT) self.__favorite_button.connect("clicked", favorite_button_clicked_cb) toolbar.insert(self.__favorite_button, -1) item = gtk.SeparatorToolItem() item.set_expand(True) item.set_draw(False) toolbar.insert(item, -1) item = gtk.ToolItem() toolbar.insert(item, -1) def filter_timeout_cb(entry): self.__current_tags = filter(None, entry.get_text().split(" ")) matching_channels.refilter() self.__filter_timeout = 0 return False def filter_entry_changed_cb(entry, *args): if self.__filter_timeout: glib.source_remove(self.__filter_timeout) self.__filter_timeout = glib.timeout_add(200, filter_timeout_cb, entry) self.__filter_entry = gtk.Entry() self.__filter_entry.set_width_chars(40) self.__filter_entry.set_completion(tag_completion) self.__filter_entry.connect("changed", filter_entry_changed_cb) item.add(self.__filter_entry) scrolled = gtk.ScrolledWindow() scrolled.set_shadow_type(gtk.SHADOW_IN) scrolled.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) vbox.pack_start(scrolled, expand=True) def row_activated_cb(view, path, column): model = view.get_model() channel, = model.get(model.get_iter(path), 0) self.__client.play(channel) tree_view = gtk.TreeView(matching_channels) tree_view.set_headers_visible(False) tree_view.connect("row-activated", row_activated_cb) scrolled.add(tree_view) def adjustment_cb(adjustment): model, tree_iter = tree_view.get_selection().get_selected() if model and tree_iter: path = model.get_path(tree_iter) tree_view.scroll_to_cell(path, None, True, 0.5, 0.5) tree_view.get_vadjustment().connect("changed", adjustment_cb) def icon_cell_data_cb(column, cell, model, iter): channel, = model.get(iter, 0) if channel != self.__client.current_channel: cell.set_property("stock-id", None) elif self.__client.is_playing: cell.set_property("stock-id", gtk.STOCK_MEDIA_PLAY) else: cell.set_property("stock-id", gtk.STOCK_MEDIA_PAUSE) def text_cell_data_cb(column, cell, model, iter): channel, = model.get(iter, 0) title, tags = channel.title, channel.tags tags = [channel.station.id] + list(tags) details = title, " ".join(tags), channel.station.title details = tuple(map(glib.markup_escape_text, details)) markup = "<b>%s</b>\n<small>%s - %s</small>" % details cell.set_property("markup", markup) cell = gtk.CellRendererPixbuf() cell.set_properties(stock_size=gtk.ICON_SIZE_MENU) column = gtk.TreeViewColumn("Icon", cell) column.set_cell_data_func(cell, icon_cell_data_cb) column.set_expand(False) tree_view.insert_column(column, -1) cell = gtk.CellRendererText() cell.set_properties(ellipsize=pango.ELLIPSIZE_END) column = gtk.TreeViewColumn(None, cell) column.set_cell_data_func(cell, text_cell_data_cb) tree_view.insert_column(column, -1) self.__stream_info = MarqueLabel() self.__stream_info.set_no_show_all(True) vbox.pack_start(self.__stream_info, expand=False) stream_tags_changed_cb(self.__client) state_changed_cb(self.__client) tags = (self.__config.tags or "").strip() self.__filter_entry.grab_focus() self.__filter_entry.set_text(tags + " ") self.__filter_entry.set_position(-1) for station in self.__client.get_stations(): for channel in station.channels: channel_added_cb(self.__client, channel) self.get_child().show_all()
class CommandLineClient(object): def __init__(self): self.__client = Client() @staticmethod def list_channels(channels): stations = dict() for c in channels: l = stations.get(c.station, []) + [c] stations[c.station] = l for s, l in stations.items(): print '\n - '.join( ['%s: %s' % (s.id, s.title)] + ['%s [%s]' % (c.title, ' '.join(c.tags)) for c in l]) @command def list(self, args): '''List matching stations''' self.__client.wait(Client.STATE_CHANNELS_LOADED) channels = self.__client.find_channels(args[2:]) self.list_channels(channels) @command def status(self, args=None): '''Print service status''' state = self.__client.is_playing and 'playing' or 'paused' channel = self.__client.current_channel if channel is not None: state += (': "%s" from "%s" [%s]' % ( channel.title, channel.station.title, ' '.join(channel.tags))) print state @command def equalizer(self, args): '''Select equalizer profile''' if 3 == len(args): self.__client.equalizer_profile = args[2] else: profiles = self.__client.get_equalizer_profiles() profiles = ' '.join(profiles) print 'Supported profiles: %s' % profiles @command def play(self, args): '''Play music from matching station''' query = args[2:] self.__client.wait(Client.STATE_CHANNELS_LOADED) channels = self.__client.find_channels(query) channels = list(channels) if not channels: print 'No matching channels found' return self.list_channels(channels) if 1 == len(channels): self.__client.play(channels[0]) @command def pause(self, args): '''Pause music player''' self.__client.pause() @command def resume(self, args): '''Resume music player''' self.__client.resume() @command def tags(self, args): '''Lists known tags''' self.__client.wait(Client.STATE_STATIONS_LOADED) for tag in self.__client.get_tags(): print tag @command def ui(self, args): '''Run user interface''' MainWindow(self.__client).run() @command def help(self, args): '''Show usage information''' commands = [(f.__name__, f.__doc__) for f in __commands__] length = max([len(name) for name, desc in commands]) print 'Usage: %s COMMAND [ARGS]' % args[0] print print 'Commands:' print for name, desc in commands: print ' %-*s - %s' % (length, name, desc) print @command def quit(self, args): '''Stop the background service''' self.__client.quit() def run(self, args): command_name = len(args) > 1 and args[1] command_table = dict([(f.__name__, f) for f in __commands__]) command_table.get(command_name, CommandLineClient.help)(self, args)
def __init__(self, client=None): super(MainWindow, self).__init__() if client is None: client = Client() def channel_compare_cb(model, a, b): a, = model.get(a, 0) b, = model.get(b, 0) return (cmp(a.title, b.title) or cmp(a.tags, b.tags) or cmp(a.station.title, b.station.title)) channels = gtk.ListStore(object) channels.set_sort_func(0, channel_compare_cb) channels.set_sort_column_id(0, gtk.SORT_ASCENDING) tag_completion = TagsCompletion(client.get_tags()) self.__current_title = None def read_wishlist(): filename = get_config_filename('wishlist') wishlist = [] try: text = file(filename).read().strip() wishlist = text and text.split('\n') or [] except IOError: pass return dict(zip(wishlist, range(len(wishlist)))) self.__wishlist = read_wishlist() def channel_added_cb(client, channel): tree_iter = channels.insert(-1, (channel, )) tag_completion.add(channel.tags) if (channel == client.current_channel or (channel.uri == self.__config.channel_uri and client.current_channel is None)): model = tree_view.get_model() tree_iter = model.convert_child_iter_to_iter(tree_iter) tree_view.get_selection().select_iter(tree_iter) def state_changed_cb(client): if client.is_playing: self.__config.channel_uri = client.current_channel.uri self.__play_button.set_active(True) stream_tags_changed_cb(client) else: self.__pause_button.set_active(True) self.__stream_info.hide() tree_view.queue_draw() def stream_tags_changed_cb(client): self.__current_title = client.stream_tags.get( 'title').strip() or None org = client.stream_tags.get('organization').strip() or None markup = [] if self.__current_title: markup.append('<b>%s</b>' % glib.markup_escape_text(self.__current_title)) if org: markup.append('<small>%s</small>' % glib.markup_escape_text(org)) if markup: self.__stream_info.set_markup(' - '.join(markup)) self.__stream_info.set_tooltip_markup('\n'.join(markup)) self.__stream_info.show() else: self.__stream_info.set_markup('') self.__stream_info.hide() self.__favorite_button.set_sensitive(bool(self.__current_title)) self.__favorite_button.set_active( self.__current_title in self.__wishlist) self.__client = client self.__client.connect('channel-added', channel_added_cb) self.__client.connect('state-changed', state_changed_cb) self.__client.connect('stream-tags-changed', stream_tags_changed_cb) self.__client.wait(Client.STATE_STATIONS_LOADED) self.__filter_timeout = 0 self.__current_tags = [] self.__config = Configuration() def channel_visible_cb(model, iter): channel, = model.get(iter, 0) return channel.matches(self.__current_tags) matching_channels = gtk.TreeModel.filter_new(channels) matching_channels.set_visible_func(channel_visible_cb) self.set_title('WebRadio') self.set_default_size(500, 400) self.set_icon_name('rhythmbox') self.connect('destroy', gtk.main_quit) vbox = gtk.VBox() self.add(vbox) toolbar = gtk.Toolbar() toolbar.set_show_arrow(False) vbox.pack_start(toolbar, expand=False) def play_button_clicked_cb(button): if not button.get_active(): self.__client.pause() return model, tree_iter = tree_view.get_selection().get_selected() channel = None if model and tree_iter: channel, = model.get(tree_iter, 0) if channel is not None: if channel == self.__client.current_channel: self.__client.resume() else: self.__client.play(channel) def favorite_button_clicked_cb(button): if not self.__current_title: return if button.get_active(): self.__wishlist[self.__current_title] = True else: self.__wishlist.pop(self.__current_title, False) wishlist_text = '\n'.join(self.__wishlist.keys()) + '\n' filename = get_config_filename('wishlist') file(filename, 'w').write(wishlist_text) self.__play_button = gtk.RadioToolButton(None, gtk.STOCK_MEDIA_PLAY) self.__play_button.connect('clicked', play_button_clicked_cb) self.__play_button.set_is_important(True) toolbar.insert(self.__play_button, -1) self.__pause_button = gtk.RadioToolButton(self.__play_button, gtk.STOCK_MEDIA_PAUSE) toolbar.insert(self.__pause_button, -1) self.__favorite_button = gtk.ToggleToolButton(gtk.STOCK_ABOUT) self.__favorite_button.connect('clicked', favorite_button_clicked_cb) toolbar.insert(self.__favorite_button, -1) item = gtk.SeparatorToolItem() item.set_expand(True) item.set_draw(False) toolbar.insert(item, -1) item = gtk.ToolItem() toolbar.insert(item, -1) def filter_timeout_cb(entry): self.__current_tags = filter(None, entry.get_text().split(' ')) matching_channels.refilter() self.__filter_timeout = 0 return False def filter_entry_changed_cb(entry, *args): if self.__filter_timeout: glib.source_remove(self.__filter_timeout) self.__filter_timeout = glib.timeout_add(200, filter_timeout_cb, entry) self.__filter_entry = gtk.Entry() self.__filter_entry.set_width_chars(40) self.__filter_entry.set_completion(tag_completion) self.__filter_entry.connect('changed', filter_entry_changed_cb) item.add(self.__filter_entry) scrolled = gtk.ScrolledWindow() scrolled.set_shadow_type(gtk.SHADOW_IN) scrolled.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) vbox.pack_start(scrolled, expand=True) def row_activated_cb(view, path, column): model = view.get_model() channel, = model.get(model.get_iter(path), 0) self.__client.play(channel) tree_view = gtk.TreeView(matching_channels) tree_view.set_headers_visible(False) tree_view.connect('row-activated', row_activated_cb) scrolled.add(tree_view) def adjustment_cb(adjustment): model, tree_iter = tree_view.get_selection().get_selected() if model and tree_iter: path = model.get_path(tree_iter) tree_view.scroll_to_cell(path, None, True, 0.5, 0.5) tree_view.get_vadjustment().connect('changed', adjustment_cb) def icon_cell_data_cb(column, cell, model, iter): channel, = model.get(iter, 0) if channel != self.__client.current_channel: cell.set_property('stock-id', None) elif self.__client.is_playing: cell.set_property('stock-id', gtk.STOCK_MEDIA_PLAY) else: cell.set_property('stock-id', gtk.STOCK_MEDIA_PAUSE) def text_cell_data_cb(column, cell, model, iter): channel, = model.get(iter, 0) title, tags = channel.title, channel.tags tags = [channel.station.id] + list(tags) details = title, ' '.join(tags), channel.station.title details = tuple(map(glib.markup_escape_text, details)) markup = '<b>%s</b>\n<small>%s - %s</small>' % details cell.set_property('markup', markup) cell = gtk.CellRendererPixbuf() cell.set_properties(stock_size=gtk.ICON_SIZE_MENU) column = gtk.TreeViewColumn('Icon', cell) column.set_cell_data_func(cell, icon_cell_data_cb) column.set_expand(False) tree_view.insert_column(column, -1) cell = gtk.CellRendererText() cell.set_properties(ellipsize=pango.ELLIPSIZE_END) column = gtk.TreeViewColumn(None, cell) column.set_cell_data_func(cell, text_cell_data_cb) tree_view.insert_column(column, -1) self.__stream_info = MarqueLabel() self.__stream_info.set_no_show_all(True) vbox.pack_start(self.__stream_info, expand=False) stream_tags_changed_cb(self.__client) state_changed_cb(self.__client) tags = (self.__config.tags or '').strip() self.__filter_entry.grab_focus() self.__filter_entry.set_text(tags + ' ') self.__filter_entry.set_position(-1) for station in self.__client.get_stations(): for channel in station.channels: channel_added_cb(self.__client, channel) self.get_child().show_all()