# -*- coding: UTF8 -*- # Python module src/gpodder/gpodder.py # Autogenerated from gpodder.glade # Warning: Do not modify any context comment such as #-- # They are required to keep user's code # # gPodder (a media aggregator / podcast client) # Copyright (C) 2005-2007 Thomas Perl # # This program 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 2 # of the License, or (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, # MA 02110-1301, USA. # import os import gtk import gtk.gdk import gobject import pango import sys from threading import Event from threading import Thread from string import strip from SimpleGladeApp import SimpleGladeApp from libpodcasts import podcastChannel from libpodcasts import podcastItem from libpodcasts import channelsToModel from librssreader import rssReader from libopmlwriter import opmlWriter from libopmlreader import opmlReader from libwget import downloadThread from libwget import downloadStatusManager from libgpodder import gPodderLib from libgpodder import gPodderChannelReader from libgpodder import gPodderChannelWriter from liblogger import log from liblocaldb import localDB from libplayers import UserAppsReader from libipodsync import gPodder_iPodSync from libipodsync import gPodder_FSSync from libipodsync import ipod_supported from libtagupdate import tagging_supported app_name = "gpodder" app_version = "unknown" # will be set in main() call app_authors = [ 'Thomas Perl ', '', _('Contributors / patch writers:'), 'Peter Hoffmann ', 'Adrien Beaucreux ', 'Alain Tauch ', '', _('See the AUTHORS file for all contributors') ] app_copyright = 'Copyright (c) 2005-2007 Thomas Perl' app_website = 'http://gpodder.berlios.de/' # these will be filled with pathnames in bin/gpodder glade_dir = [ 'share', 'gpodder' ] icon_dir = [ 'share', 'pixmaps', 'gpodder.png' ] class Gpodder(SimpleGladeApp): # Local DB ldb = None # User Apps Reader uar = None def __init__(self, path="gpodder.glade", root="gPodder", domain=app_name, **kwargs): path = os.path.join(glade_dir, path) SimpleGladeApp.__init__(self, path, root, domain, **kwargs) #-- Gpodder.new { def new(self): if app_version.rfind( "svn") != -1: self.gPodder.set_title( 'gPodder %s' % ( app_version, )) # set up the rendering of the comboAvailable combobox cellrenderer = gtk.CellRendererText() self.comboAvailable.pack_start( cellrenderer, True) self.comboAvailable.add_attribute( cellrenderer, 'text', 1) # See http://www.pygtk.org/pygtk2tutorial/sec-CellRenderers.html namecell = gtk.CellRendererText() namecell.set_property('cell-background', 'white') namecell.set_property('ellipsize', pango.ELLIPSIZE_END) namecolumn = gtk.TreeViewColumn( _("Episode"), namecell, text=1) namecolumn.add_attribute(namecell, "cell-background", 4) sizecell = gtk.CellRendererText() sizecell.set_property('cell-background', 'white') sizecolumn = gtk.TreeViewColumn( _("Size"), sizecell, text=2) sizecolumn.add_attribute(sizecell, "cell-background", 4) releasecell = gtk.CellRendererText() releasecell.set_property('cell-background', 'white') releasecolumn = gtk.TreeViewColumn( _("Released"), releasecell, text=5) releasecolumn.add_attribute(releasecell, "cell-background", 4) desccell = gtk.CellRendererText() desccell.set_property('cell-background', 'white') desccell.set_property('ellipsize', pango.ELLIPSIZE_END) desccolumn = gtk.TreeViewColumn( _("Description"), desccell, text=6) desccolumn.add_attribute(desccell, "cell-background", 4) for itemcolumn in ( namecolumn, sizecolumn, releasecolumn, desccolumn ): itemcolumn.set_resizable( True) itemcolumn.set_reorderable( True) self.treeAvailable.append_column( itemcolumn) # enable multiple selection support self.treeAvailable.get_selection().set_mode( gtk.SELECTION_MULTIPLE) self.treeDownloads.get_selection().set_mode( gtk.SELECTION_MULTIPLE) # columns and renderers for "download progress" tab episodecell = gtk.CellRendererText() episodecolumn = gtk.TreeViewColumn( _("Episode"), episodecell, text=0) speedcell = gtk.CellRendererText() speedcolumn = gtk.TreeViewColumn( _("Speed"), speedcell, text=1) progresscell = gtk.CellRendererProgress() progresscolumn = gtk.TreeViewColumn( _("Progress"), progresscell, value=2) for itemcolumn in ( episodecolumn, speedcolumn, progresscolumn ): self.treeDownloads.append_column( itemcolumn) self.download_status_manager = downloadStatusManager( main_window = self.gPodder, change_notification = self.updateTreeView) self.treeDownloads.set_model( self.download_status_manager.getModel()) # tooltips :) self.tooltips = gtk.Tooltips() self.tooltips.set_tip( self.btnEditChannel, _("Channel Info")) #Add Drag and Drop Support targets = [("text/plain", 0, 2), ('STRING', 0, 3), ('TEXT', 0, 4)] self.main_widget.drag_dest_set(gtk.DEST_DEFAULT_ALL, targets, \ gtk.gdk.ACTION_DEFAULT | gtk.gdk.ACTION_COPY | \ gtk.gdk.ACTION_DEFAULT) self.main_widget.connect("drag_data_received", self.drag_data_received) self.wNotebook.connect("switch-page", self.switched_notebook) gl = gPodderLib() # Subscribed channels self.active_channel = None self.channels = gPodderChannelReader().read( force_update = False) self.update_feed_cache( force_update = gl.update_on_startup) # create a localDB object self.ldb = localDB() # load list of user applications self.user_apps_reader = UserAppsReader() self.user_apps_reader.read() # Clean up old, orphaned download files gl.clean_up_downloads( delete_partial = True) #-- Gpodder.new } #-- Gpodder custom methods { def play_or_download( self): is_download_button = False gl = gPodderLib() try: selection = self.treeAvailable.get_selection() selection_tuple = selection.get_selected_rows() for apath in selection_tuple[1]: selection_iter = self.treeAvailable.get_model().get_iter( apath) url = self.treeAvailable.get_model().get_value( selection_iter, 0) filename = self.active_channel.getPodcastFilename( url) if not os.path.exists( filename): is_download_button = True break except: is_download_button = True if is_download_button: self.btnPlay.hide_all() self.btnDownload.show_all() self.btnTransfer.hide_all() else: self.btnPlay.show_all() self.btnDownload.hide_all() if gl.device_type != 'none': self.btnTransfer.show_all() def updateComboBox( self): try: old_active = self.comboAvailable.get_active() if old_active < 0: old_active = 0 elif old_active > len( self.channels)-1: old_active = len(self.channels)-1 self.comboAvailable.set_model( channelsToModel( self.channels)) self.comboAvailable.set_active( old_active) except: pass def updateTreeView( self): rect = self.treeAvailable.get_visible_rect() if self.channels: self.treeAvailable.set_model( self.active_channel.items_liststore( downloading_callback = self.download_status_manager.is_download_in_progress)) # now, reset the scrolling position self.treeAvailable.scroll_to_point( rect.x, rect.y) while gtk.events_pending(): gtk.main_iteration( False) self.treeAvailable.scroll_to_point( rect.x, rect.y) self.play_or_download() else: if self.treeAvailable.get_model(): self.treeAvailable.get_model().clear() def show_message( self, message, title = None): dlg = gtk.MessageDialog( self.gPodder, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, gtk.BUTTONS_OK) if title: markup = '%s%s%s' % ( title, "\n\n", message, ) else: markup = '%s' % message dlg.set_markup( markup) dlg.run() dlg.destroy() def show_confirmation( self, message, title = None): confirmation_result = False dlg = gtk.MessageDialog( self.gPodder, gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO) if title: markup = '%s%s%s' % ( title, "\n\n", message, ) else: markup = '%s' % message dlg.set_markup( markup) if dlg.run() == gtk.RESPONSE_YES: confirmation_result = True dlg.destroy() return confirmation_result def set_icon(self): icon = self.get_icon('gpodder') self.main_widget.set_icon(icon) def get_icon(self, entry, size=48): path = icon_dir if path == None: pb = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, True, 8, size, size) pb.fill(0x00000000) else: try: pb = gtk.gdk.pixbuf_new_from_file_at_size(path, size, size) except: pb = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, True, 8, size, size) pb.fill(0x00000000) return pb def switched_notebook( self, notebook, page, page_num): if page_num == 0: self.updateTreeView() def drag_data_received(self, widget, context, x, y, sel, ttype, time): result = sel.data self.add_new_channel( result) def refetch_channel_list( self): channels_should_be = len( self.channels) gPodderChannelWriter().write( self.channels) self.update_feed_cache( force_update = False) if channels_should_be > len( self.channels): title = _('Error adding channel') message = _('The channel could not be added. Please check the spelling of the URL or try again later.') self.show_message( message, title) def add_new_channel( self, result = None): gl = gPodderLib() result = gl.sanitize_feed_url( result) if result: for old_channel in self.channels: if old_channel.url == result: log( 'Channel already exists: %s', result) # Select the existing channel in combo box for i in range( len( self.channels)): if self.channels[i] == old_channel: self.comboAvailable.set_active( i) return log( 'Adding new channel: %s', result) channel = podcastChannel( url = result) channel.remove_cache_file() num_channels_before = len(self.channels) self.channels.append( channel) # download changed channels self.refetch_channel_list() if num_channels_before < len(self.channels): # ask user to download some new episodes self.comboAvailable.set_active( len( self.channels)-1) self.on_btnDownloadNewer_clicked( None) else: if result: title = _('URL scheme not supported') message = _('gPodder currently only supports URLs starting with http://, feed:// or ftp://.') self.show_message( message, title) def sync_to_ipod_proc( self, sync, episodes = None): if not sync.open(): sync.close( success = False, access_error = True) return False if episodes == None: i = 0 for channel in self.ldb.channel_list: sync.set_progress_overall( i, len(self.ldb.channel_list)) channel.set_metadata_from_localdb() sync.sync_channel( channel) i += 1 sync.set_progress_overall( i, len(self.ldb.channel_list)) else: sync.sync_channel( self.active_channel, episodes) sync.close( success = not sync.cancelled) def ipod_cleanup_proc( self, sync): if not sync.open(): gobject.idle_add( self.show_message, message, title) sync.close( success = False, access_error = True) return False sync.clean_playlist() sync.close( success = not sync.cancelled, cleaned = True) def update_feed_cache_callback( self, label, progressbar, position, count): title = _('Please wait...') if len(self.channels) > position: try: title = _('Updating %s') % self.channels[position].title except: pass label.set_markup( '%s' % title) progressbar.set_text( _('%d of %d channels updated') % ( position, count )) progressbar.set_fraction( ((1.00*position) / (1.00*count))) def update_feed_cache_proc( self, reader, force_update, callback_proc, callback_error, finish_proc): self.channels = reader.read( force_update, callback_proc = callback_proc, callback_error = callback_error) finish_proc() def update_feed_cache(self, force_update = True): title = _('Downloading podcast feeds') heading = _('Downloading feeds') body = _('Podcast feeds contain channel metadata and information about current episodes.') if not force_update: title = _('Reading podcast feeds') heading = _('Reading feeds') please_wait = gtk.Dialog( title, self.gPodder, gtk.DIALOG_MODAL, ( gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, )) please_wait.set_transient_for( self.gPodder) please_wait.set_position( gtk.WIN_POS_CENTER_ON_PARENT) please_wait.vbox.set_spacing( 5) please_wait.set_border_width( 10) please_wait.set_resizable( False) label_heading = gtk.Label() label_heading.set_alignment( 0.0, 0.5) label_heading.set_markup( '%s' % heading) label_body = gtk.Label() label_body.set_text( body) label_body.set_alignment( 0.0, 0.5) label_body.set_line_wrap( True) myprogressbar = gtk.ProgressBar() mylabel = gtk.Label() mylabel.set_alignment( 0.0, 0.5) mylabel.set_ellipsize( pango.ELLIPSIZE_END) # put it all together please_wait.vbox.pack_start( label_heading) please_wait.vbox.pack_start( label_body) please_wait.vbox.pack_start( myprogressbar) please_wait.vbox.pack_end( mylabel) please_wait.show_all() # hide separator line please_wait.set_has_separator( False) # let's get down to business.. callback_proc = lambda pos, count: gobject.idle_add( self.update_feed_cache_callback, mylabel, myprogressbar, pos, count) callback_error = lambda x: gobject.idle_add( self.show_message, x) finish_proc = lambda: gobject.idle_add( please_wait.destroy) reader = gPodderChannelReader() args = ( reader, force_update, callback_proc, callback_error, finish_proc, ) thread = Thread( target = self.update_feed_cache_proc, args = args) thread.start() if please_wait.run() == gtk.RESPONSE_CANCEL: reader.cancel() self.updateComboBox() # download all new? if force_update and gPodderLib().download_after_update: self.on_itemDownloadAllNew_activate( self.gPodder) def download_podcast_by_url( self, url, want_message_dialog = True, widget = None): current_channel = self.active_channel current_podcast = current_channel.find_episode( url) filename = current_channel.getPodcastFilename( current_podcast.url) if widget: if (widget.get_name() == 'itemPlaySelected' or widget.get_name() == 'btnPlay') and os.path.exists( filename): # addDownloadedItem just to make sure the episode is marked correctly in localdb if current_channel.addDownloadedItem( current_podcast): self.ldb.clear_cache() # open the file now gPodderLib().openFilename( filename) return if widget.get_name() == 'treeAvailable': gpe = Gpodderepisode( gpodderwindow = self.gPodder) gpe.set_episode( current_podcast, current_channel) if os.path.exists( filename): gpe.set_play_callback( lambda: gPodderLib().openFilename( filename)) else: gpe.set_download_callback( lambda: self.download_podcast_by_url( url, want_message_dialog, None)) return if not os.path.exists( filename) and not self.download_status_manager.is_download_in_progress( current_podcast.url): downloadThread( current_podcast.url, filename, None, self.download_status_manager, current_podcast.title, current_channel, current_podcast, self.ldb).download() else: if want_message_dialog and os.path.exists( filename): title = _('Episode already downloaded') message = _('You have already downloaded this episode. Click on the episode to play it.') self.show_message( message, title) elif want_message_dialog: title = _('Download in progress') message = _('You are currently downloading this episode. Please check the download status tab to check when the download is finished.') self.show_message( message, title) if os.path.exists( filename): log( 'Episode has already been downloaded.') if current_channel.addDownloadedItem( current_podcast): self.ldb.clear_cache() # update tree view to mark the episode as being downloaded self.updateTreeView() #-- Gpodder custom methods } #-- Gpodder.close_gpodder { def close_gpodder(self, widget, *args): if self.channels: gPodderChannelWriter().write( self.channels) if self.download_status_manager: self.download_status_manager.cancelAll() self.gtk_main_quit() #-- Gpodder.close_gpodder } #-- Gpodder.on_itemUpdate_activate { def on_itemUpdate_activate(self, widget, *args): if self.channels: self.update_feed_cache() else: title = _('No channels available') message = _('You need to subscribe to some podcast feeds before you can start downloading podcasts. Use your favorite search engine to look for interesting podcasts.') self.show_message( message, title) #-- Gpodder.on_itemUpdate_activate } #-- Gpodder.on_itemDownloadAllNew_activate { def on_itemDownloadAllNew_activate(self, widget, *args): gl = gPodderLib() to_download = [] message_part = '' new_sum = 0 for channel in self.channels: s = 0 last_pubdate = channel.newest_pubdate_downloaded() if not last_pubdate: log( 'Downloading newest three episodes.') for episode in channel[0:min(len(channel),3)]: to_download.append( ( channel, episode )) s = s + 1 else: for episode in channel: if episode.compare_pubdate( last_pubdate) >= 0 and not channel.is_downloaded( episode) and not gl.history_is_downloaded( episode.url) and not self.download_status_manager.is_download_in_progress( episode.url): log( 'Episode "%s" is newer.', episode.title) to_download.append( ( channel, episode )) s += 1 if s: new_part = _(' %d new episodes in %s') % ( s, channel.title, ) if s == 1: new_part = _(' 1 new episode in %s') % ( channel.title, ) message_part += new_part + "\n" new_sum += s if to_download: title = _('New episodes available') if new_sum == 1: title = _('New episode available') message = _('New episodes are available to be downloaded:\n\n%s\nDo you want to download these episodes now?') % ( message_part, ) if self.show_confirmation( message, title): for channel, episode in to_download: filename = channel.getPodcastFilename( episode.url) if not os.path.exists( filename) and not self.download_status_manager.is_download_in_progress( episode.url): downloadThread( episode.url, filename, None, self.download_status_manager, episode.title, channel, episode, self.ldb).download() else: title = _('No new episodes') message = _('There are no new episodes to download from your podcast subscriptions. Please check for new episodes later.') self.show_message( message, title) self.updateTreeView() #-- Gpodder.on_itemDownloadAllNew_activate } #-- Gpodder.on_sync_to_ipod_activate { def on_sync_to_ipod_activate(self, widget, *args): gl = gPodderLib() if gl.device_type == 'none': title = _('No device configured') message = _('To use the synchronization feature, please configure your device in the preferences dialog first.') self.show_message( message, title) return if gl.device_type == 'ipod' and not ipod_supported(): title = _('Libraries needed: gpod, pymad') message = _('To use the iPod synchronization feature, you need to install the python-gpod and python-pymad libraries from your distribution vendor. More information about the needed libraries can be found on the gPodder website.') self.show_message( message, title) return if gl.device_type in [ 'ipod', 'filesystem' ]: sync_class = None if gl.device_type == 'filesystem': sync_class = gPodder_FSSync elif gl.device_type == 'ipod': sync_class = gPodder_iPodSync if not sync_class: return sync_win = Gpoddersync( gpodderwindow = self.gPodder) sync = sync_class( callback_status = sync_win.set_status, callback_progress = sync_win.set_progress, callback_done = sync_win.close) sync_win.set_sync_object( sync) thread_args = [ sync ] if widget == None: thread_args.append( args[0]) thread = Thread( target = self.sync_to_ipod_proc, args = thread_args) thread.start() #-- Gpodder.on_sync_to_ipod_activate } #-- Gpodder.on_cleanup_ipod_activate { def on_cleanup_ipod_activate(self, widget, *args): gl = gPodderLib() if gl.device_type == 'none': title = _('No device configured') message = _('To use the synchronization feature, please configure your device in the preferences dialog first.') self.show_message( message, title) return if gl.device_type == 'ipod' and not ipod_supported(): title = _('Libraries needed: gpod, pymad') message = _('To use the iPod synchronization feature, you need to install the python-gpod and python-pymad libraries from your distribution vendor. More information about the needed libraries can be found on the gPodder website.') self.show_message( message, title) return if gl.device_type in [ 'ipod', 'filesystem' ]: sync_class = None if gl.device_type == 'filesystem': title = _('Delete podcasts from MP3 player?') message = _('Do you really want to completely remove all episodes from your MP3 player?') if self.show_confirmation( message, title): sync_class = gPodder_FSSync elif gl.device_type == 'ipod': title = _('Delete podcasts on iPod?') message = _('Do you really want to completely remove all episodes in the Podcasts playlist on your iPod?') if self.show_confirmation( message, title): sync_class = gPodder_iPodSync if not sync_class: return sync_win = Gpoddersync( gpodderwindow = self.gPodder) sync = sync_class( callback_status = sync_win.set_status, callback_progress = sync_win.set_progress, callback_done = sync_win.close) sync_win.set_sync_object( sync) thread = Thread( target = self.ipod_cleanup_proc, args = ( sync, )) thread.start() #-- Gpodder.on_cleanup_ipod_activate } #-- Gpodder.on_itemPreferences_activate { def on_itemPreferences_activate(self, widget, *args): prop = Gpodderproperties( gpodderwindow = self.gPodder) prop.set_uar( self.user_apps_reader) prop.set_callback_finished( self.updateTreeView) #-- Gpodder.on_itemPreferences_activate } #-- Gpodder.on_itemAddChannel_activate { def on_itemAddChannel_activate(self, widget, *args): self.add_new_channel( Gpodderchannel( gpodderwindow = self.gPodder).requestURL()) #-- Gpodder.on_itemAddChannel_activate } #-- Gpodder.on_itemEditChannel_activate { def on_itemEditChannel_activate(self, widget, *args): if not self.active_channel: title = _('No channel selected') message = _('Please select a channel in the channels list to edit.') self.show_message( message, title) return result = Gpodderchannel( gpodderwindow = self.gPodder).requestURL( self.active_channel) self.updateComboBox() active = self.comboAvailable.get_active() if result != self.active_channel.url and result != None and result != "" and (result[:4] == "http" or result[:3] == "ftp"): log( 'Changing channel #%d from "%s" to "%s"', active, self.active_channel.url, result) self.channels = self.channels[0:active] + [ podcastChannel( url = result) ] + self.channels[active+1:] self.refetch_channel_list() #-- Gpodder.on_itemEditChannel_activate } #-- Gpodder.on_itemRemoveChannel_activate { def on_itemRemoveChannel_activate(self, widget, *args): try: title = _('Remove channel and episodes?') message = _('Do you really want to remove %s and all downloaded episodes?') % ( self.active_channel.title, ) if self.show_confirmation( message, title): self.active_channel.remove_cache_file() self.active_channel.remove_downloaded() # only delete partial files if we do not have any downloads in progress delete_partial = not self.download_status_manager.has_items() gPodderLib().clean_up_downloads( delete_partial) self.channels.remove( self.active_channel) gPodderChannelWriter().write( self.channels) self.update_feed_cache( force_update = False) except: pass #-- Gpodder.on_itemRemoveChannel_activate } #-- Gpodder.on_itemExportChannels_activate { def on_itemExportChannels_activate(self, widget, *args): if not self.channels: title = _('Nothing to export') message = _('Your list of channel subscriptions is empty. Please subscribe to some podcasts first before trying to export your subscription list.') self.show_message( message, title) return dlg = gtk.FileChooserDialog( title=_("Export to OPML"), parent = None, action = gtk.FILE_CHOOSER_ACTION_SAVE) dlg.add_button( gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL) dlg.add_button( gtk.STOCK_SAVE, gtk.RESPONSE_OK) response = dlg.run() if response == gtk.RESPONSE_OK: foutname = dlg.get_filename() if foutname[-5:] != ".opml" and foutname[-4:] != ".xml": foutname = foutname + ".opml" log( 'Exporting channel list to: %s', foutname) w = opmlWriter( foutname) for ch in self.channels: w.addChannel( ch) w.close() dlg.destroy() #-- Gpodder.on_itemExportChannels_activate } #-- Gpodder.on_itemImportChannels_activate { def on_itemImportChannels_activate(self, widget, *args): Gpodderopmllister( gpodderwindow = self.gPodder).get_channels_from_url( gPodderLib().opml_url, self.add_new_channel) #-- Gpodder.on_itemImportChannels_activate } #-- Gpodder.on_btnTransfer_clicked { def on_btnTransfer_clicked(self, widget, *args): self.on_treeAvailable_row_activated( widget, args) #-- Gpodder.on_btnTransfer_clicked } #-- Gpodder.on_homepage_activate { def on_homepage_activate(self, widget, *args): os.system( 'gnome-open ' + app_website) #-- Gpodder.on_homepage_activate } #-- Gpodder.on_wishlist_activate { def on_wishlist_activate(self, widget, *args): os.system( 'gnome-open http://www.amazon.de/gp/registry/2PD2MYGHE6857') #-- Gpodder.on_wishlist_activate } #-- Gpodder.on_mailinglist_activate { def on_mailinglist_activate(self, widget, *args): os.system( 'gnome-open http://lists.berlios.de/mailman/listinfo/gpodder-devel') #-- Gpodder.on_mailinglist_activate } #-- Gpodder.on_itemAbout_activate { def on_itemAbout_activate(self, widget, *args): dlg = gtk.AboutDialog() dlg.set_name( app_name) dlg.set_version( app_version) dlg.set_authors( app_authors) dlg.set_copyright( app_copyright) dlg.set_website( app_website) dlg.set_translator_credits( _('translator-credits')) dlg.connect("response", self.on_aboutDialog_response) try: dlg.set_logo( gtk.gdk.pixbuf_new_from_file_at_size( icon_dir, 200, 200)) except: pass dlg.run() #-- Gpodder.on_itemAbout_activate } def on_aboutDialog_response(self, dialog, response): dialog.destroy() #-- Gpodder.on_wNotebook_switch_page { def on_wNotebook_switch_page(self, widget, *args): pass #-- Gpodder.on_wNotebook_switch_page } #-- Gpodder.on_comboAvailable_changed { def on_comboAvailable_changed(self, widget, *args): try: self.active_channel = self.channels[self.comboAvailable.get_active()] except: self.active_channel = None if self.active_channel: self.itemEditChannel.get_child().set_text( _('Edit "%s"') % ( self.active_channel.title,)) self.itemRemoveChannel.get_child().set_text( _('Remove "%s"') % ( self.active_channel.title,)) self.itemEditChannel.show_all() self.itemRemoveChannel.show_all() else: self.itemEditChannel.hide_all() self.itemRemoveChannel.hide_all() self.updateTreeView() #-- Gpodder.on_comboAvailable_changed } #-- Gpodder.on_btnEditChannel_clicked { def on_btnEditChannel_clicked(self, widget, *args): self.on_itemEditChannel_activate( widget, args) #-- Gpodder.on_btnEditChannel_clicked } #-- Gpodder.on_treeAvailable_row_activated { def on_treeAvailable_row_activated(self, widget, *args): try: selection = self.treeAvailable.get_selection() selection_tuple = selection.get_selected_rows() transfer_files = False episodes = [] if selection.count_selected_rows() > 1: widget_to_send = None show_message_dialog = False else: widget_to_send = widget show_message_dialog = True if widget.get_name() == 'itemTransferSelected' or widget.get_name() == 'btnTransfer': transfer_files = True for apath in selection_tuple[1]: selection_iter = self.treeAvailable.get_model().get_iter( apath) url = self.treeAvailable.get_model().get_value( selection_iter, 0) if transfer_files: episodes.append( self.active_channel.find_episode( url)) else: self.download_podcast_by_url( url, show_message_dialog, widget_to_send) if transfer_files and len(episodes): self.on_sync_to_ipod_activate( None, episodes) except: title = _('Nothing selected') message = _('Please select an episode that you want to download and then click on the download button to start downloading the selected episode.') self.show_message( message, title) #-- Gpodder.on_treeAvailable_row_activated } #-- Gpodder.on_btnDownload_clicked { def on_btnDownload_clicked(self, widget, *args): self.on_treeAvailable_row_activated( widget, args) #-- Gpodder.on_btnDownload_clicked } #-- Gpodder.on_treeAvailable_button_release_event { def on_treeAvailable_button_release_event(self, widget, *args): self.play_or_download() #-- Gpodder.on_treeAvailable_button_release_event } #-- Gpodder.on_btnDownloadNewer_clicked { def on_btnDownloadNewer_clicked(self, widget, *args): channel = self.active_channel episodes_to_download = [] gl = gPodderLib() last_pubdate = channel.newest_pubdate_downloaded() if not last_pubdate: title = _('Download newest podcasts?') message = _('Would you like to download the three newest episodes from %s?') % ( channel.title, ) if self.show_confirmation( message, title): episodes_to_download = channel[0:min(len(channel),3)] else: for episode in channel: if episode.compare_pubdate( last_pubdate) >= 0 and not channel.is_downloaded( episode) and not gl.history_is_downloaded( episode.url) and not self.download_status_manager.is_download_in_progress( episode.url): log( 'Episode "%s" is newer.', episode.title) episodes_to_download.append( episode) if not episodes_to_download: title = _('No episodes to download') message = _('You have already downloaded the most recent episodes from %s.') % ( channel.title, ) self.show_message( message, title) else: if len(episodes_to_download) > 1: if len(episodes_to_download) < 10: e_str = '\n'.join( [ ' '+e.title+'' for e in episodes_to_download ] ) else: e_str = '\n'.join( [ ' '+e.title+'' for e in episodes_to_download[:7] ] ) e_str_2 = _('(...%d more episodes...)') % ( len(episodes_to_download)-7, ) e_str = _('%s\n %s') % ( e_str, e_str_2, ) title = _('Download new episodes?') message = _('New episodes are available for download. If you want, you can download these episodes to your computer now.') message = '%s\n\n%s' % ( message, e_str, ) else: title = _('Download %s?') % episodes_to_download[0].title message = _('A new episode is available for download. If you want, you can download this episode to your computer now.') if not self.show_confirmation( message, title): return for episode in episodes_to_download: self.download_podcast_by_url( episode.url, False) #-- Gpodder.on_btnDownloadNewer_clicked } #-- Gpodder.on_btnSelectAllAvailable_clicked { def on_btnSelectAllAvailable_clicked(self, widget, *args): self.treeAvailable.get_selection().select_all() self.on_treeAvailable_row_activated( self.btnDownload, args) self.treeAvailable.get_selection().unselect_all() #-- Gpodder.on_btnSelectAllAvailable_clicked } #-- Gpodder.on_treeDownloads_row_activated { def on_treeDownloads_row_activated(self, widget, *args): selection = self.treeDownloads.get_selection() selection_tuple = selection.get_selected_rows() if selection.count_selected_rows() == 0: log( 'Nothing selected to cancel.') return title = _('Cancel downloads?') message = _("Cancelling the download will stop the %d selected downloads and remove partially downloaded files.") % selection.count_selected_rows() if selection.count_selected_rows() == 1: title = _('Cancel download?') message = _("Cancelling this download will remove the partially downloaded file and stop the download.") if self.show_confirmation( message, title): # cancel downloads one by one try: for apath in selection_tuple[1]: selection_iter = self.treeDownloads.get_model().get_iter( apath) url = self.download_status_manager.get_url_by_iter( selection_iter) self.download_status_manager.cancel_by_url( url) except: log( 'Error while cancelling downloads.') #-- Gpodder.on_treeDownloads_row_activated } #-- Gpodder.on_btnCancelDownloadStatus_clicked { def on_btnCancelDownloadStatus_clicked(self, widget, *args): self.on_treeDownloads_row_activated( widget, None) #-- Gpodder.on_btnCancelDownloadStatus_clicked } #-- Gpodder.on_btnCancelAll_clicked { def on_btnCancelAll_clicked(self, widget, *args): self.treeDownloads.get_selection().select_all() self.on_treeDownloads_row_activated( self.btnCancelDownloadStatus, None) self.treeDownloads.get_selection().unselect_all() #-- Gpodder.on_btnCancelAll_clicked } #-- Gpodder.on_btnDownloadedExecute_clicked { def on_btnDownloadedExecute_clicked(self, widget, *args): self.on_treeAvailable_row_activated( widget, args) #-- Gpodder.on_btnDownloadedExecute_clicked } #-- Gpodder.on_btnDownloadedDelete_clicked { def on_btnDownloadedDelete_clicked(self, widget, *args): channel_url = self.active_channel.url selection = self.treeAvailable.get_selection() selection_tuple = selection.get_selected_rows() model = self.treeAvailable.get_model() if selection.count_selected_rows() == 0: log( 'Nothing selected - will not remove any downloaded episode.') return title = _('Remove %d episodes?') % selection.count_selected_rows() message = _('If you remove these episodes, they will be deleted from your computer. If you want to listen to any of these episodes again, you will have to re-download the episodes in question.') if selection.count_selected_rows() == 1: title = _('Remove %s?') % model.get_value( model.get_iter( selection_tuple[1][0]), 1) message = _("If you remove this episode, it will be deleted from your computer. If you want to listen to this episode again, you will have to re-download it.") # if user confirms deletion, let's remove some stuff ;) if self.show_confirmation( message, title): try: # iterate over the selection, see also on_treeDownloads_row_activated for apath in selection_tuple[1]: selection_iter = model.get_iter( apath) url = model.get_value( selection_iter, 0) self.active_channel.delete_episode_by_url( url) # now, clear local db cache so we can re-read it self.ldb.clear_cache() self.updateComboBox() except: log( 'Error while deleting (some) downloads.') # only delete partial files if we do not have any downloads in progress delete_partial = not self.download_status_manager.has_items() gPodderLib().clean_up_downloads( delete_partial) #-- Gpodder.on_btnDownloadedDelete_clicked } #-- Gpodder.on_btnDeleteAll_clicked { def on_btnDeleteAll_clicked(self, widget, *args): self.treeAvailable.get_selection().select_all() self.on_btnDownloadedDelete_clicked( widget, args) self.treeAvailable.get_selection().unselect_all() #-- Gpodder.on_btnDeleteAll_clicked } class Gpodderchannel(SimpleGladeApp): event = None channel = None podcast = None thread = None def __init__(self, path="gpodder.glade", root="gPodderChannel", domain=app_name, **kwargs): waiting = None url = "" result = False path = os.path.join(glade_dir, path) SimpleGladeApp.__init__(self, path, root, domain, **kwargs) if 'gpodderwindow' in kwargs: self.gPodderChannel.set_transient_for( kwargs['gpodderwindow']) self.gPodderChannel.set_position( gtk.WIN_POS_CENTER_ON_PARENT) #-- Gpodderchannel.new { def new(self): pass #-- Gpodderchannel.new } #-- Gpodderchannel custom methods { def requestURL( self, channel = None): if channel: self.gPodderChannel.set_title( _('Channel: %s') % channel.title) self.entryURL.set_text( channel.url) self.entryTitle.set_text( channel.title) self.LabelDownloadTo.set_text( channel.save_dir) self.LabelWebsite.set_text( channel.link) channel.set_metadata_from_localdb() self.cbNoSync.set_active( not channel.sync_to_devices) self.musicPlaylist.set_text( channel.device_playlist_name) self.cbMusicChannel.set_active( channel.is_music_channel) description = channel.description if channel.image != None: # load image in background gPodderLib().get_image_from_url( channel.image, self.imgCover.set_from_pixbuf, self.labelCoverStatus.set_text, self.labelCoverStatus.hide, channel.cover_file) else: # Remove all tabs except for the first one while self.notebookChannelEditor.get_n_pages() > 1: self.notebookChannelEditor.remove_page( -1) for widget in ( self.labelDescription, self.scrolledwindow, self.expander, self.labelTitle, self.entryTitle ): widget.hide_all() self.gPodderChannel.set_title( _('Add a new channel')) self.gPodderChannel.queue_resize() description = _("(unknown)") b = gtk.TextBuffer() b.set_text( description) self.channel_description.set_buffer( b) self.waiting = Event() while self.waiting.isSet() == False: self.waiting.wait( 0.01) while gtk.events_pending(): gtk.main_iteration( False) if self.result == True: if channel != None: channel.sync_to_devices = not self.cbNoSync.get_active() channel.is_music_channel = self.cbMusicChannel.get_active() channel.device_playlist_name = self.musicPlaylist.get_text() channel.set_custom_title( self.entryTitle.get_text()) channel.save_metadata_to_localdb() return self.url else: return None #-- Gpodderchannel custom methods } #-- Gpodderchannel.on_gPodderChannel_destroy { def on_gPodderChannel_destroy(self, widget, *args): self.result = False #-- Gpodderchannel.on_gPodderChannel_destroy } #-- Gpodderchannel.on_cbMusicChannel_toggled { def on_cbMusicChannel_toggled(self, widget, *args): self.musicPlaylist.set_sensitive( self.cbMusicChannel.get_active()) #-- Gpodderchannel.on_cbMusicChannel_toggled } #-- Gpodderchannel.on_btnOK_clicked { def on_btnOK_clicked(self, widget, *args): self.url = self.entryURL.get_text() self.gPodderChannel.destroy() self.result = True if self.waiting != None: self.waiting.set() #-- Gpodderchannel.on_btnOK_clicked } #-- Gpodderchannel.on_btnCancel_clicked { def on_btnCancel_clicked(self, widget, *args): self.gPodderChannel.destroy() self.result = False if self.waiting != None: self.waiting.set() #-- Gpodderchannel.on_btnCancel_clicked } class Gpodderproperties(SimpleGladeApp): def __init__(self, path="gpodder.glade", root="gPodderProperties", domain=app_name, **kwargs): path = os.path.join(glade_dir, path) SimpleGladeApp.__init__(self, path, root, domain, **kwargs) if 'gpodderwindow' in kwargs: self.gPodderProperties.set_transient_for( kwargs['gpodderwindow']) self.gPodderProperties.set_position( gtk.WIN_POS_CENTER_ON_PARENT) #-- Gpodderproperties.new { def new(self): self.callback_finished = None gl = gPodderLib() self.httpProxy.set_text( gl.http_proxy) self.ftpProxy.set_text( gl.ftp_proxy) self.openApp.set_text( gl.open_app) self.iPodMountpoint.set_label( gl.ipod_mount) self.ipodIcon.set_from_icon_name( 'gnome-dev-ipod', gtk.ICON_SIZE_BUTTON) self.filesystemMountpoint.set_label( gl.mp3_player_folder) self.opmlURL.set_text( gl.opml_url) if gl.downloaddir: self.chooserDownloadTo.set_filename( gl.downloaddir) self.updateonstartup.set_active(gl.update_on_startup) self.downloadnew.set_active(gl.download_after_update) if tagging_supported(): self.updatetags.set_active(gl.update_tags) else: self.updatetags.set_sensitive( False) new_label = '%s (%s)' % ( self.updatetags.get_label(), _('needs python-eyed3') ) self.updatetags.set_label( new_label) # colors self.reload_colors() # device type self.comboboxDeviceType.set_active( 0) if gl.device_type == 'ipod': self.comboboxDeviceType.set_active( 1) elif gl.device_type == 'filesystem': self.comboboxDeviceType.set_active( 2) # the use proxy env vars check box self.cbEnvironmentVariables.set_active( gl.proxy_use_environment) # if the symlink exists, set the checkbox active self.cbDesktopSymlink.set_active( gl.getDesktopSymlink()) # setup cell renderers cellrenderer = gtk.CellRendererPixbuf() self.comboPlayerApp.pack_start( cellrenderer, False) self.comboPlayerApp.add_attribute( cellrenderer, 'pixbuf', 2) cellrenderer = gtk.CellRendererText() self.comboPlayerApp.pack_start( cellrenderer, True) self.comboPlayerApp.add_attribute( cellrenderer, 'markup', 0) # end setup cell renderers #-- Gpodderproperties.new } #-- Gpodderproperties custom methods { def on_btnRevertToDefault_clicked( self, widget, *args): gl = gPodderLib() gl.set_default_colors() self.reload_colors() def reload_colors( self): gl = gPodderLib() try: self.colorDefault.set_color( gtk.gdk.color_parse( gl.colors['default'])) self.colorDownloading.set_color( gtk.gdk.color_parse( gl.colors['downloading'])) self.colorDownloaded.set_color( gtk.gdk.color_parse( gl.colors['downloaded'])) self.colorDeleted.set_color( gtk.gdk.color_parse( gl.colors['deleted'])) except: log( '(gPodderProperties) Could not parse color strings.') def update_mountpoint( self, ipod): if ipod == None or ipod.mount_point == None: self.iPodMountpoint.set_label( '') else: self.iPodMountpoint.set_label( ipod.mount_point) def set_uar( self, uar): self.comboPlayerApp.set_model( uar.get_applications_as_model()) # try to activate an item index = self.find_active() self.comboPlayerApp.set_active( index) def set_callback_finished( self, cb): self.callback_finished = cb # end set_uar def find_active( self): model = self.comboPlayerApp.get_model() iter = model.get_iter_first() index = 0 while iter != None: command = model.get_value( iter, 1) if command == self.openApp.get_text(): return index iter = model.iter_next( iter) index = index + 1 # return last item = custom command return index-1 # end find_active def set_download_dir( self, new_download_dir, event = None): gl = gPodderLib() gl.downloaddir = self.chooserDownloadTo.get_filename() if event: event.set() #-- Gpodderproperties custom methods } #-- Gpodderproperties.on_gPodderProperties_destroy { def on_gPodderProperties_destroy(self, widget, *args): self.on_btnOK_clicked( widget, *args) #-- Gpodderproperties.on_gPodderProperties_destroy } #-- Gpodderproperties.on_comboPlayerApp_changed { def on_comboPlayerApp_changed(self, widget, *args): # find out which one iter = self.comboPlayerApp.get_active_iter() model = self.comboPlayerApp.get_model() command = model.get_value( iter, 1) if command == '': self.openApp.set_sensitive( True) self.openApp.show() self.labelCustomCommand.show() else: self.openApp.set_text( command) self.openApp.set_sensitive( False) self.openApp.hide() self.labelCustomCommand.hide() #-- Gpodderproperties.on_comboPlayerApp_changed } #-- Gpodderproperties.on_comboboxDeviceType_changed { def on_comboboxDeviceType_changed(self, widget, *args): active_item = self.comboboxDeviceType.get_active() # iPod if active_item == 1: self.ipodLabel.show() self.btn_iPodMountpoint.set_sensitive( True) self.btn_iPodMountpoint.show_all() else: self.ipodLabel.hide() self.btn_iPodMountpoint.set_sensitive( False) self.btn_iPodMountpoint.hide() # filesystem-based MP3 player if active_item == 2: self.filesystemLabel.show() self.btn_filesystemMountpoint.set_sensitive( True) self.btn_filesystemMountpoint.show_all() else: self.filesystemLabel.hide() self.btn_filesystemMountpoint.set_sensitive( False) self.btn_filesystemMountpoint.hide() #-- Gpodderproperties.on_comboboxDeviceType_changed } #-- Gpodderproperties.on_btn_iPodMountpoint_clicked { def on_btn_iPodMountpoint_clicked(self, widget, *args): fs = gtk.FileChooserDialog( title = _('Select iPod mountpoint'), action = gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER) fs.add_button( gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL) fs.add_button( gtk.STOCK_OPEN, gtk.RESPONSE_OK) gl = gPodderLib() fs.set_filename( self.iPodMountpoint.get_label()) if fs.run() == gtk.RESPONSE_OK: self.iPodMountpoint.set_label( fs.get_filename()) fs.destroy() #-- Gpodderproperties.on_btn_iPodMountpoint_clicked } #-- Gpodderproperties.on_btn_FilesystemMountpoint_clicked { def on_btn_FilesystemMountpoint_clicked(self, widget, *args): fs = gtk.FileChooserDialog( title = _('Select folder for MP3 player'), action = gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER) fs.add_button( gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL) fs.add_button( gtk.STOCK_OPEN, gtk.RESPONSE_OK) gl = gPodderLib() fs.set_filename( self.filesystemMountpoint.get_label()) if fs.run() == gtk.RESPONSE_OK: self.filesystemMountpoint.set_label( fs.get_filename()) fs.destroy() #-- Gpodderproperties.on_btn_FilesystemMountpoint_clicked } #-- Gpodderproperties.on_cbEnvironmentVariables_toggled { def on_cbEnvironmentVariables_toggled(self, widget, *args): sens = not self.cbEnvironmentVariables.get_active() self.httpProxy.set_sensitive( sens) self.ftpProxy.set_sensitive( sens) #-- Gpodderproperties.on_cbEnvironmentVariables_toggled } #-- Gpodderproperties.on_btnOK_clicked { def on_btnOK_clicked(self, widget, *args): gl = gPodderLib() gl.http_proxy = self.httpProxy.get_text() gl.ftp_proxy = self.ftpProxy.get_text() gl.open_app = self.openApp.get_text() gl.proxy_use_environment = self.cbEnvironmentVariables.get_active() gl.ipod_mount = self.iPodMountpoint.get_label() gl.mp3_player_folder = self.filesystemMountpoint.get_label() gl.opml_url = self.opmlURL.get_text() try: gl.set_color( 'default', self.colorDefault.get_color()) gl.set_color( 'downloading', self.colorDownloading.get_color()) gl.set_color( 'downloaded', self.colorDownloaded.get_color()) gl.set_color( 'deleted', self.colorDeleted.get_color()) except: log('(gPodderProperties) Could not parse and save color values.') if gl.downloaddir != self.chooserDownloadTo.get_filename(): new_download_dir = self.chooserDownloadTo.get_filename() download_dir_size = gl.get_size( gl.downloaddir) download_dir_size_string = gl.size_to_string( download_dir_size, 'MB') event = Event() dlg = gtk.Dialog( _('Moving downloads folder'), self.gPodderProperties) dlg.vbox.set_spacing( 5) dlg.set_border_width( 5) label = gtk.Label() label.set_line_wrap( True) label.set_markup( _('Moving downloads from %s to %s...') % ( gl.downloaddir, new_download_dir, )) myprogressbar = gtk.ProgressBar() # put it all together dlg.vbox.pack_start( label) dlg.vbox.pack_end( myprogressbar) # switch windows dlg.show_all() self.gPodderProperties.hide_all() # hide action area and separator line dlg.action_area.hide() dlg.set_has_separator( False) args = ( new_download_dir, event, ) thread = Thread( target = self.set_download_dir, args = args) thread.start() while not event.isSet(): new_download_dir_size = gl.get_size( new_download_dir) fract = (1.00*new_download_dir_size) / (1.00*download_dir_size) if fract < 0.99: myprogressbar.set_text( _('%s of %s') % ( gl.size_to_string( new_download_dir_size, 'MB'), download_dir_size_string, )) else: myprogressbar.set_text( _('Finishing... please wait.')) myprogressbar.set_fraction( fract) event.wait( 0.1) while gtk.events_pending(): gtk.main_iteration( False) dlg.destroy() gl.update_on_startup = self.updateonstartup.get_active() gl.download_after_update = self.downloadnew.get_active() gl.update_tags = self.updatetags.get_active() device_type = self.comboboxDeviceType.get_active() if device_type == 0: gl.device_type = 'none' elif device_type == 1: gl.device_type = 'ipod' elif device_type == 2: gl.device_type = 'filesystem' gl.propertiesChanged() # create or remove symlink to download dir on desktop if self.cbDesktopSymlink.get_active(): gl.createDesktopSymlink() else: gl.removeDesktopSymlink() self.gPodderProperties.destroy() if self.callback_finished: self.callback_finished() #-- Gpodderproperties.on_btnOK_clicked } class Gpodderepisode(SimpleGladeApp): def __init__(self, path="gpodder.glade", root="gPodderEpisode", domain=app_name, **kwargs): path = os.path.join(glade_dir, path) SimpleGladeApp.__init__(self, path, root, domain, **kwargs) if 'gpodderwindow' in kwargs: self.gPodderEpisode.set_transient_for( kwargs['gpodderwindow']) self.gPodderEpisode.set_position( gtk.WIN_POS_CENTER_ON_PARENT) #-- Gpodderepisode.new { def new(self): pass #-- Gpodderepisode.new } #-- Gpodderepisode custom methods { # Write your own methods here def set_episode( self, episode, channel = None): self.episode_title.set_markup( '%s' % episode.title) b = gtk.TextBuffer() b.set_text( strip( episode.description)) self.episode_description.set_buffer( b) self.LabelDownloadLink.set_text(episode.url) self.LabelWebsiteLink.set_text(episode.link) self.gPodderEpisode.set_title( episode.title) if not episode.link and channel: self.LabelWebsiteLink.set_text( channel.link) if channel: smalltext = _('from %s') % ( channel.title ) self.channel_title.set_markup( smalltext) self.labelPubDate.set_markup( episode.pubDate) self.download_callback = None self.play_callback = None def set_download_callback( self, callback = None): self.download_callback = callback if callback: self.btnDownload.show_all() def set_play_callback( self, callback = None): self.play_callback = callback if callback: self.btnPlay.show_all() #-- Gpodderepisode custom methods } #-- Gpodderepisode.on_btnCloseWindow_clicked { def on_btnCloseWindow_clicked(self, widget, *args): self.gPodderEpisode.destroy() #-- Gpodderepisode.on_btnCloseWindow_clicked } #-- Gpodderepisode.on_btnDownload_clicked { def on_btnDownload_clicked(self, widget, *args): # if we have a callback, .. well.. call it back! ;) if self.download_callback: self.download_callback() self.gPodderEpisode.destroy() #-- Gpodderepisode.on_btnDownload_clicked } #-- Gpodderepisode.on_btnPlay_clicked { def on_btnPlay_clicked(self, widget, *args): if self.play_callback: self.play_callback() self.gPodderEpisode.destroy() #-- Gpodderepisode.on_btnPlay_clicked } class Gpoddersync(SimpleGladeApp): def __init__(self, path="gpodder.glade", root="gPodderSync", domain=app_name, **kwargs): path = os.path.join(glade_dir, path) SimpleGladeApp.__init__(self, path, root, domain, **kwargs) if 'gpodderwindow' in kwargs: self.gPodderSync.set_transient_for( kwargs['gpodderwindow']) self.gPodderSync.set_position( gtk.WIN_POS_CENTER_ON_PARENT) self.pos_overall = 0 self.max_overall = 1 self.pos_episode = 0 self.max_episode = 1 self.cancel_button.set_sensitive( False) self.sync = None self.default_title = self.gPodderSync.get_title() self.default_header = self.label_header.get_text() self.default_body = self.label_text.get_text() #-- Gpoddersync.new { def new(self): self.imageSync.set_from_icon_name( 'gnome-dev-ipod', gtk.ICON_SIZE_DIALOG) #-- Gpoddersync.new } #-- Gpoddersync custom methods { def set_sync_object( self, sync): self.sync = sync if self.sync.can_cancel: self.cancel_button.set_sensitive( True) def set_progress( self, pos, max, is_overall = False, is_sub_episode = False): pos = min(pos, max) if is_sub_episode: fraction_episode = 1.0*(self.pos_episode+1.0*pos/max)/self.max_episode self.pbEpisode.set_fraction( fraction_episode) self.pbSync.set_fraction( 1.0*(self.pos_overall+fraction_episode)/self.max_overall) return if is_overall: progressbar = self.pbSync self.pos_overall = pos self.max_overall = max progressbar.set_fraction( 1.0*pos/max) else: progressbar = self.pbEpisode self.pos_episode = pos self.max_episode = max progressbar.set_fraction( 1.0*pos/max) self.pbSync.set_fraction( 1.0*(self.pos_overall+1.0*pos/max)/self.max_overall) percent = _('%d of %d done') % ( pos, max ) progressbar.set_text( percent) def set_status( self, episode = None, channel = None, progressbar = None, title = None, header = None, body = None): if episode != None: self.labelEpisode.set_markup( '%s' % episode) if channel != None: self.labelChannel.set_markup( '%s' % channel) if progressbar != None: self.pbSync.set_text( progressbar) if title != None: self.gPodderSync.set_title( title) else: self.gPodderSync.set_title( self.default_title) if header != None: self.label_header.set_markup( '%s' % header) else: self.label_header.set_markup( '%s' % self.default_header) if body != None: self.label_text.set_markup( body) else: self.label_text.set_markup( self.default_body) def close( self, success = True, access_error = False, cleaned = False): if self.sync: self.sync.cancelled = True self.cancel_button.set_label( gtk.STOCK_CLOSE) self.cancel_button.set_use_stock( True) self.cancel_button.set_sensitive( True) self.gPodderSync.set_resizable( True) self.pbSync.hide_all() self.pbEpisode.hide_all() self.labelChannel.hide_all() self.labelEpisode.hide_all() self.gPodderSync.set_resizable( False) if success and not cleaned: title = _('Synchronization finished') header = _('Copied Podcasts') body = _('The selected episodes have been copied to your device. You can now unplug the device.') elif access_error: title = _('Synchronization error') header = _('Cannot access device') body = _('Make sure your device is connected to your computer and mounted. Please also make sure you have set the correct path to your device in the preferences dialog.') elif cleaned: title = _('Device cleaned') header = _('Podcasts removed') body = _('Synchronized Podcasts have been removed from your device.') else: title = _('Synchronization aborted') header = _('Aborted') body = _('The synchronization progress has been interrupted by the user. Please retry synchronization at a later time.') self.gPodderSync.set_title( title) self.label_header.set_markup( '%s' % header) self.label_text.set_text( body) #-- Gpoddersync custom methods } #-- Gpoddersync.on_gPodderSync_destroy { def on_gPodderSync_destroy(self, widget, *args): pass #-- Gpoddersync.on_gPodderSync_destroy } #-- Gpoddersync.on_cancel_button_clicked { def on_cancel_button_clicked(self, widget, *args): if self.sync: if self.sync.cancelled: self.gPodderSync.destroy() else: self.sync.cancelled = True self.cancel_button.set_sensitive( False) else: self.gPodderSync.destroy() #-- Gpoddersync.on_cancel_button_clicked } class Gpodderopmllister(SimpleGladeApp): def __init__(self, path="gpodder.glade", root="gPodderOpmlLister", domain=app_name, **kwargs): path = os.path.join(glade_dir, path) SimpleGladeApp.__init__(self, path, root, domain, **kwargs) if 'gpodderwindow' in kwargs: self.gPodderOpmlLister.set_transient_for( kwargs['gpodderwindow']) self.gPodderOpmlLister.set_position( gtk.WIN_POS_CENTER_ON_PARENT) #-- Gpodderopmllister.new { def new(self): # initiate channels list self.channels = [] self.callback_for_channel = None togglecell = gtk.CellRendererToggle() togglecell.set_property( 'activatable', True) togglecell.connect( 'toggled', self.callback_edited) togglecolumn = gtk.TreeViewColumn( _("Subscribe"), togglecell, active=0) titlecell = gtk.CellRendererText() titlecolumn = gtk.TreeViewColumn( _("Channel name"), titlecell, text=1) for itemcolumn in ( togglecolumn, titlecolumn ): self.treeviewChannelChooser.append_column( itemcolumn) #-- Gpodderopmllister.new } #-- Gpodderopmllister custom methods { # Write your own methods here def callback_edited( self, cell, path): model = self.treeviewChannelChooser.get_model() activated = not model[path][0] url = model[path][2] model[path][0] = activated if activated: self.channels.append( url) else: self.channels.remove( url) log( 'Edited: %s', url) def get_channels_from_url( self, url, callback): reader = opmlReader() reader.parseXML( url) self.treeviewChannelChooser.set_model( reader.get_model()) self.callback_for_channel = callback #-- Gpodderopmllister custom methods } #-- Gpodderopmllister.on_gPodderOpmlLister_destroy { def on_gPodderOpmlLister_destroy(self, widget, *args): pass #-- Gpodderopmllister.on_gPodderOpmlLister_destroy } #-- Gpodderopmllister.on_btnOK_clicked { def on_btnOK_clicked(self, widget, *args): self.gPodderOpmlLister.destroy() # add channels that have been selected for url in self.channels: if self.callback_for_channel != None: self.callback_for_channel (url) #-- Gpodderopmllister.on_btnOK_clicked } #-- Gpodderopmllister.on_btnCancel_clicked { def on_btnCancel_clicked(self, widget, *args): self.gPodderOpmlLister.destroy() #-- Gpodderopmllister.on_btnCancel_clicked } #-- main { def main( __version__ = None): global app_version gobject.threads_init() app_version = __version__ g_podder = Gpodder() g_podder.set_icon() g_podder.run() #-- main }