gpodder/src/gpodder/gpodder.py

1290 lines
51 KiB
Python
Raw Normal View History

# -*- 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-2006 Thomas Perl <thp at perli.net>
#
# 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 gobject
import pango
import sys
from threading import Event
from threading import Thread
from string import strip
from SimpleGladeApp import SimpleGladeApp
from SimpleGladeApp import bindtextdomain
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 ipod_supported
app_name = "gpodder"
app_version = "unknown" # will be set in main() call
app_authors = [
'Thomas Perl <thp@perli.net>', '',
_('Contributors / patch writers:'),
'Peter Hoffmann <tosh@cs.tu-berlin.de>',
'Adrien Beaucreux <informancer@web.de>',
'Alain Tauch <contrib@maisondubonheur.com>', '',
_('See the AUTHORS file for all contributors')
]
app_copyright = 'Copyright (c) 2005-2006 Thomas Perl'
app_website = 'http://perli.net/projekte/gpodder/'
glade_dir = '/usr/share/gpodder/'
icon_dir = '/usr/share/gpodder/images/gpodder.png'
artwork_dir = '/usr/share/gpodder/images/'
locale_dir = '/usr/share/locale/'
class Gpodder(SimpleGladeApp):
channels = []
active_item = None
items_model = None
active_channel = None
channels_model = None
channels_loaded = False
download_status_manager = None
tooltips = None
# 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):
# set up the rendering of the comboAvailable combobox
cellrenderer = gtk.CellRendererText()
self.comboAvailable.pack_start( cellrenderer, True)
self.comboAvailable.add_attribute( cellrenderer, 'text', 1)
# set up the rendering of the comboDownloaded combobox
cellrenderer = gtk.CellRendererText()
self.comboDownloaded.pack_start( cellrenderer, True)
self.comboDownloaded.add_attribute( cellrenderer, 'text', 1)
#See http://www.pygtk.org/pygtk2tutorial/sec-CellRenderers.html
#gtk.TreeViewColumn( "", gtk.CellRendererToggle(), active=3),
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)
self.treeDownloaded.get_selection().set_mode( gtk.SELECTION_MULTIPLE)
# columns and renderers for the "downloaded" tab
# more information: see above..
namecell = gtk.CellRendererText()
namecell.set_property('cell-background', 'white')
namecolumn = gtk.TreeViewColumn( _("Episode"), namecell, text=1)
namecolumn.add_attribute(namecell, "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, releasecolumn, desccolumn ):
itemcolumn.set_resizable( True)
itemcolumn.set_reorderable( True)
self.treeDownloaded.append_column( itemcolumn)
# 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)
new_model = gtk.ListStore( gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_INT)
self.download_status_manager = downloadStatusManager( main_window = self.gPodder, change_notification = self.updateTreeView)
self.treeDownloads.set_model( self.download_status_manager.getModel())
# read and display subscribed channels
reader = gPodderChannelReader()
self.channels = reader.read( False)
self.channels_loaded = True
# keep Downloaded channels list
self.downloaded_channels = None
self.active_downloaded_channels = 0
# update view
self.updateComboBox()
self.updateDownloadedComboBox()
# 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)
# if we are running a SVN-based version, notify the user :)
if app_version.rfind( "svn") != -1:
self.showMessage( _("<b>gPodder development version %s</b>\nUse at your own risk, but also enjoy new features :)") % app_version)
gl = gPodderLib()
# Update the feed list if the user has set it up
if gl.update_on_startup:
self.update_feed_cache()
# Clean up old, orphaned download files
gl.clean_up_downloads()
#-- Gpodder.new }
#-- Gpodder custom methods {
def updateComboBox( self):
self.channels_model = channelsToModel( self.channels)
self.comboAvailable.set_model( self.channels_model)
try:
self.comboAvailable.set_active( 0)
except:
pass
#self.updateTreeView()
def updateDownloadedComboBox( self):
# now, update downloaded feeds tab:
if self.ldb == None:
self.ldb = localDB()
# update downloaded_channels list
self.downloaded_channels = self.ldb.channel_list
self.comboDownloaded.set_model( self.ldb.get_model())
try:
self.comboDownloaded.set_active( self.active_downloaded_channels)
except:
self.active_downloaded_channels = 0
log( _('No downloaded podcasts found.'))
# end of self.updateDownloadedComboBox()
def updateTreeView( self):
if self.channels:
self.items_model = self.channels[self.active_channel].getItemsModel( downloading_callback = self.download_status_manager.is_download_in_progress)
self.treeAvailable.set_model( self.items_model)
self.treeAvailable.columns_autosize()
else:
if self.treeAvailable.get_model():
self.treeAvailable.get_model().clear()
def showMessage( self, message, title = _('gPodder message')):
dlg = gtk.MessageDialog( self.gPodder, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, gtk.BUTTONS_OK)
dlg.set_title( title)
dlg.set_markup( message)
dlg.run()
dlg.destroy()
def showConfirmation( self, message = _('Do you really want to do this?'), title = _('gPodder confirmation')):
myresult = False
dlg = gtk.MessageDialog( self.gPodder, gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO)
dlg.set_title( title)
dlg.set_markup( message)
if gtk.RESPONSE_YES == dlg.run():
myresult = True
dlg.destroy()
log( "I Asked: %s\nUser answered: %s", message, str( myresult))
return myresult
def set_icon(self):
icon = self.get_icon('gpodder')
self.main_widget.set_icon(icon)
def get_icon(self, entry, size=24):
#path = self.custom_handler.getIconPath(entry, size)
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:
# when switching to first page, update the "downloading" list
self.updateTreeView()
elif page_num == 2:
# when switching to last page, update the "downloaded" combo box
self.updateDownloadedComboBox()
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.channels = gPodderChannelReader().read( False)
# check if gPodderChannelReader has successfully added the channel
if channels_should_be > len( self.channels):
self.showMessage( _("There has been an error adding the channel.\nMaybe the URL is wrong?"))
def add_new_channel( self, result = None):
# Treat "feed://" URLs like "http://" ones
if result and result[:4] == 'feed':
result = 'http' + result[4:]
if result != None and result != "" and (result[:4] == "http" or result[:3] == "ftp"):
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)
self.statusLabel.set_text( _("Fetching channel index..."))
self.channels.append( podcastChannel( url = result))
# download changed channels
self.refetch_channel_list()
# try to update combo box
self.updateComboBox()
self.statusLabel.set_text( "")
# ask user to download some new episodes
self.comboAvailable.set_active( len( self.channels)-1)
self.on_btnDownloadNewer_clicked( None)
else:
if result != None and result != "":
self.showMessage( _('Could not add new channel.\n\nThe URL must start with <b>http://</b>, <b>feed://</b> or <b>ftp://</b>.'))
def get_current_channel_downloaded( self):
iter = self.comboDownloaded.get_active_iter()
return self.comboDownloaded.get_model().get_value( iter, 0)
def sync_to_ipod_proc( self, sync_win):
gpl = gPodderLib()
gpl.loadConfig()
sync = gPodder_iPodSync( ipod_mount = gpl.ipod_mount, callback_status = sync_win.set_status, callback_progress = sync_win.set_progress, callback_done = sync_win.close)
if not sync.open():
gobject.idle_add( self.showMessage, _('Cannot access iPod.\nMake sure your iPod is connected and mounted.'))
sync.close()
return False
for channel in self.downloaded_channels:
channel.set_metadata_from_localdb()
sync.copy_channel_to_ipod( channel)
sync.close()
def ipod_cleanup_proc( self, sync_win):
gpl = gPodderLib()
gpl.loadConfig()
sync = gPodder_iPodSync( ipod_mount = gpl.ipod_mount, callback_status = sync_win.set_status, callback_progress = sync_win.set_progress, callback_done = sync_win.close)
if not sync.open():
gobject.idle_add( self.showMessage, _('Cannot access iPod.\nMake sure your iPod is connected.'))
sync.close()
return False
sync.clean_playlist()
sync.close()
def update_feed_cache_callback( self, progressbar, position, count):
progressbar.set_text( _("%d of %d") % ( position, count ))
progressbar.set_fraction( ((1.00*position) / (1.00*count)))
def update_feed_cache(self):
reader = gPodderChannelReader()
please_wait = gtk.Dialog( _("Updating feed cache"), self.gPodder)
#please_wait.vbox.set_border_width( 10)
please_wait.vbox.set_spacing( 5)
please_wait.set_border_width( 5)
label = gtk.Label( _("Please wait - gPodder is updating its feed cache..."))
myprogressbar = gtk.ProgressBar()
# put it all together
please_wait.vbox.pack_start( label)
please_wait.vbox.pack_end( myprogressbar)
please_wait.show_all()
# hide action anre and separator line
please_wait.action_area.hide()
please_wait.set_has_separator( False)
# let's get down to business..
self.channels = reader.read( True, lambda pos, count: self.update_feed_cache_callback( myprogressbar, pos, count))
please_wait.destroy()
#self.labelStatus.set_text( "")
self.updateComboBox()
def download_podcast_by_url( self, url, want_message_dialog = True, widget = None):
self.active_item = self.channels[self.active_channel].getActiveByUrl( url)
current_channel = self.channels[self.active_channel]
current_podcast = current_channel[self.active_item]
filename = current_channel.getPodcastFilename( current_podcast.url)
if widget and widget.get_name() == 'treeAvailable':
gpe = Gpodderepisode()
gpe.set_episode( current_podcast, current_channel)
# to download, the dialog calls this function again but without widget param (widget = None)
gpe.set_download_callback( lambda: self.download_podcast_by_url( url, want_message_dialog, None))
return
if os.path.exists( filename) == False and self.download_status_manager.is_download_in_progress( current_podcast.url) == False:
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:
self.showMessage( _("You have already downloaded this episode\nor you are currently downloading it."))
# if we're not downloading it, but it exists: add to localdb (if not already done so)
if os.path.exists( filename) == True:
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_loaded:
gPodderChannelWriter().write( self.channels)
# cancel downloads by killing all threads in the list
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:
self.showMessage( _('Subscribe to some channels first.'))
#-- Gpodder.on_itemUpdate_activate }
#-- Gpodder.on_sync_to_ipod_activate {
def on_sync_to_ipod_activate(self, widget, *args):
gl = gPodderLib()
if gl.device_type == 'none':
self.showMessage( _('Configure your device in the preferences dialog first.'))
elif gl.device_type == 'ipod':
if not ipod_supported():
self.showMessage( _('Please install python-gpod and pymad libraries.\nMore information on the gPodder homepage.'))
return
sync_win = Gpoddersync()
while gtk.events_pending():
gtk.main_iteration( False)
args = ( sync_win, )
thread = Thread( target = self.sync_to_ipod_proc, args = args)
thread.start()
elif gl.device_type == 'filesystem':
self.showMessage( _('Sync to %s currently not supported.') % ( gl.mp3_player_folder, ))
#-- 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':
self.showMessage( _('Configure your device in the preferences dialog first.'))
elif gl.device_type == 'ipod':
if not self.showConfirmation( _('Do you really want to truncate the Podcasts playlist on your iPod?')):
return
sync_win = Gpoddersync()
while gtk.events_pending():
gtk.main_iteration( False)
args = ( sync_win, )
thread = Thread( target = self.ipod_cleanup_proc, args = args)
thread.start()
elif gl.device_type == 'filesystem':
self.showMessage( _('Cleanup of %s currently not supported.') % ( gl.mp3_player_folder, ))
#-- Gpodder.on_cleanup_ipod_activate }
#-- Gpodder.on_itemPreferences_activate {
def on_itemPreferences_activate(self, widget, *args):
if self.uar == None:
self.uar = UserAppsReader()
self.uar.read()
prop = Gpodderproperties()
prop.set_uar( self.uar)
#-- Gpodder.on_itemPreferences_activate }
#-- Gpodder.on_itemAddChannel_activate {
def on_itemAddChannel_activate(self, widget, *args):
ch = Gpodderchannel()
result = ch.requestURL()
self.add_new_channel( result)
#-- Gpodder.on_itemAddChannel_activate }
#-- Gpodder.on_itemEditChannel_activate {
def on_itemEditChannel_activate(self, widget, *args):
channel = None
try:
channel = self.channels[self.active_channel]
except:
self.showMessage( _('Please select a channel to edit.'))
return
result = Gpodderchannel().requestURL( channel)
active = self.active_channel
if result != channel.url and result != None and result != "" and (result[:4] == "http" or result[:3] == "ftp"):
log( 'Changing channel #%d from "%s" to "%s"', active, channel.url, result)
self.statusLabel.set_text( _("Fetching channel index..."))
self.channels = self.channels[0:active] + [ podcastChannel( url = result) ] + self.channels[active+1:]
# fetch new channels
self.refetch_channel_list()
self.updateComboBox()
self.statusLabel.set_text( "")
# end if result != None etc etc
#-- Gpodder.on_itemEditChannel_activate }
#-- Gpodder.on_itemRemoveChannel_activate {
def on_itemRemoveChannel_activate(self, widget, *args):
try:
if self.showConfirmation( _("Do you really want to remove this channel and downloaded episodes?\n\n %s") % self.channels[self.active_channel].title) == False:
return
self.channels[self.active_channel].remove_cache_file()
self.channels[self.active_channel].remove_downloaded()
gPodderLib().clean_up_downloads()
self.channels.remove( self.channels[self.active_channel])
gPodderChannelWriter().write( self.channels)
self.channels = gPodderChannelReader().read( False)
self.updateComboBox()
except:
pass
#-- Gpodder.on_itemRemoveChannel_activate }
#-- Gpodder.on_itemExportChannels_activate {
def on_itemExportChannels_activate(self, widget, *args):
if len( self.channels) == 0:
self.showMessage( _("Your channel list is empty. Nothing to export."))
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()
# end response is ok
dlg.destroy()
# end dlg.run()
#-- Gpodder.on_itemExportChannels_activate }
#-- Gpodder.on_itemImportChannels_activate {
def on_itemImportChannels_activate(self, widget, *args):
opml_lister = Gpodderopmllister()
gl = gPodderLib()
url = gl.opml_url
opml_lister.get_channels_from_url( url, self.add_new_channel)
#-- Gpodder.on_itemImportChannels_activate }
#-- Gpodder.on_homepage_activate {
def on_homepage_activate(self, widget, *args):
os.system( 'gnome-open http://perli.net/projekte/gpodder/')
#-- 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:
None
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):
self.active_channel = self.comboAvailable.get_active()
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()
if selection.count_selected_rows() > 1:
widget_to_send = None
show_message_dialog = False
else:
widget_to_send = widget
show_message_dialog = True
for apath in selection_tuple[1]:
selection_iter = self.items_model.get_iter( apath)
url = self.items_model.get_value( selection_iter, 0)
self.download_podcast_by_url( url, show_message_dialog, widget_to_send)
except:
self.showMessage( _("You have not selected an episode to download."))
#-- 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_btnDownloadNewer_clicked {
def on_btnDownloadNewer_clicked(self, widget, *args):
channel = self.channels[self.active_channel]
episodes_to_download = []
last_pubdate = channel.newest_pubdate_downloaded()
if not last_pubdate:
if self.showConfirmation( _('Would you like to download the three newest episodes in this channel?')):
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):
log( 'Episode "%s" is newer.', episode.title)
episodes_to_download.append( episode)
if not episodes_to_download:
self.showMessage( _('You have already downloaded the most recent episode.'))
else:
e_str = '\n'.join( [ e.title for e in episodes_to_download ] )
if not self.showConfirmation( _('Do you want to download these episodes?\n\n%s') % ( e_str, )):
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
if selection.count_selected_rows() == 1:
msg = _("Do you really want to cancel this download?")
else:
msg = _("Do you really want to cancel %d downloads?") % selection.count_selected_rows()
if self.showConfirmation( msg):
# 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_comboDownloaded_changed {
def on_comboDownloaded_changed(self, widget, *args):
self.active_downloaded_channels = self.comboDownloaded.get_active()
try:
new_model = self.ldb.get_tree_model( self.get_current_channel_downloaded())
self.treeDownloaded.set_model( new_model)
self.treeDownloaded.columns_autosize()
except:
if self.treeDownloaded.get_model() != None:
self.treeDownloaded.get_model().clear()
#-- Gpodder.on_comboDownloaded_changed }
#-- Gpodder.on_treeDownloaded_row_activated {
def on_treeDownloaded_row_activated(self, widget, *args):
try:
selection = self.treeDownloaded.get_selection()
model = self.treeDownloaded.get_model()
if selection.count_selected_rows() != 1:
# bug out, 'cos we only want one, really!
return
selection_tuple = selection.get_selected_rows()
apath = selection_tuple[1][0]
selection_iter = model.get_iter( apath)
url = model.get_value( selection_iter, 0)
if widget.get_name() == "treeDownloaded":
podcast = self.ldb.get_podcast( url)
Gpodderepisode().set_episode( podcast)
return
filename = self.ldb.get_filename_by_podcast( self.get_current_channel_downloaded(), url)
gPodderLib().openFilename( filename)
except:
self.showMessage( _("No episode selected."))
#-- Gpodder.on_treeDownloaded_row_activated }
#-- Gpodder.on_btnDownloadedExecute_clicked {
def on_btnDownloadedExecute_clicked(self, widget, *args):
self.on_treeDownloaded_row_activated( widget, args)
#-- Gpodder.on_btnDownloadedExecute_clicked }
#-- Gpodder.on_btnDownloadedDelete_clicked {
def on_btnDownloadedDelete_clicked(self, widget, *args):
channel_url = self.get_current_channel_downloaded()
selection = self.treeDownloaded.get_selection()
selection_tuple = selection.get_selected_rows()
model = self.treeDownloaded.get_model()
if selection.count_selected_rows() == 0:
log( 'Nothing selected - will not remove any downloaded episode.')
return
if selection.count_selected_rows() == 1:
msg = _("Do you really want to remove this episode?")
else:
msg = _("Do you really want to remove %d episodes?" % ( selection.count_selected_rows() ))
# if user confirms deletion, let's remove some stuff ;)
if self.showConfirmation( msg):
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)
episode_filename = self.ldb.get_filename_by_podcast( channel_url, url)
current_channel = self.downloaded_channels[self.comboDownloaded.get_active()]
current_channel.delete_episode_by_url( url)
gPodderLib().deleteFilename( episode_filename)
# now, clear local db cache so we can re-read it
self.ldb.clear_cache()
self.updateComboBox()
self.updateDownloadedComboBox()
except:
log( 'Error while deleting (some) downloads.')
gPodderLib().clean_up_downloads()
#-- Gpodder.on_btnDownloadedDelete_clicked }
#-- Gpodder.on_btnDeleteAll_clicked {
def on_btnDeleteAll_clicked(self, widget, *args):
self.treeDownloaded.get_selection().select_all()
self.on_btnDownloadedDelete_clicked( widget, args)
self.treeDownloaded.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)
#-- Gpodderchannel.new {
def new(self):
pass
#-- Gpodderchannel.new }
#-- Gpodderchannel custom methods {
def requestURL( self, channel = None):
if channel != None:
self.entryURL.set_text( channel.url)
self.downloadTo.set_text( channel.save_dir)
self.channel_title.set_markup( "<b>%s</b>" % channel.title)
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:
self.channel_title.set_markup( "<b>%s</b>" % _("(unknown)"))
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.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)
#-- Gpodderproperties.new {
def new(self):
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_pixbuf( gtk.gdk.pixbuf_new_from_file_at_size( artwork_dir + 'ipod-mini.png', 24, 24))
self.filesystemMountpoint.set_label( gl.mp3_player_folder)
self.opmlURL.set_text( gl.opml_url)
self.downloadTo.set_label( gl.downloaddir)
self.updateonstartup.set_active(gl.update_on_startup)
# 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
self.on_close = None
#-- Gpodderproperties.new }
#-- Gpodderproperties custom methods {
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)
# 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
#-- Gpodderproperties custom methods }
#-- Gpodderproperties.on_gPodderProperties_destroy {
def on_gPodderProperties_destroy(self, widget, *args):
if self.on_close != None:
self.on_close()
#-- 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_browseDownloadTo_clicked {
def on_browseDownloadTo_clicked(self, widget, *args):
fs = gtk.FileChooserDialog( title = _('Select download folder'), action = gtk.FILE_CHOOSER_ACTION_CREATE_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.downloadTo.get_label())
if fs.run() == gtk.RESPONSE_OK:
self.downloadTo.set_label( fs.get_filename())
fs.destroy()
#-- Gpodderproperties.on_browseDownloadTo_clicked }
#-- 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()
gl.downloaddir = self.downloadTo.get_label()
gl.update_on_startup = self.updateonstartup.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()
#-- Gpodderproperties.on_btnOK_clicked }
#-- Gpodderproperties.on_btnCancel_clicked {
def on_btnCancel_clicked(self, widget, *args):
self.gPodderProperties.destroy()
#-- Gpodderproperties.on_btnCancel_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)
#-- 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( '<big><b>%s</b></big>' % episode.title)
b = gtk.TextBuffer()
b.set_text( strip( episode.description))
self.episode_description.set_buffer( b)
self.entryURL.set_text(episode.url)
self.entryLink.set_text(episode.link)
if episode.link == '' and channel != None:
self.entryLink.set_text( channel.link)
self.labelPubDate.set_markup( '<b>%s</b>' % ( episode.pubDate ))
self.download_callback = None
def set_download_callback( self, callback = None):
self.download_callback = callback
if callback != None:
# show the button if we have a callback!
self.btnDownload.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 != None:
self.download_callback()
self.gPodderEpisode.destroy()
#-- Gpodderepisode.on_btnDownload_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)
#-- Gpoddersync.new {
def new(self):
global artwork_dir
self.imageSyncServer.set_from_file( artwork_dir + 'computer.png')
self.imageSyncAnimation.set_from_file( artwork_dir + 'sync-anim.gif')
self.imageSyncClient.set_from_file( artwork_dir + 'ipod-mini.png')
#-- Gpoddersync.new }
#-- Gpoddersync custom methods {
def set_progress( self, pos, max):
self.pbSync.set_fraction( 1.0*pos/max)
percent = _('%d of %d') % ( pos, max )
self.pbSync.set_text( percent)
def set_status( self, episode = None, channel = None, progressbar = None):
if episode != None:
self.labelEpisode.set_text( episode)
if channel != None:
self.labelChannel.set_text( channel)
if progressbar != None:
self.pbSync.set_text( progressbar)
def close( self):
self.gPodderSync.destroy()
#-- Gpoddersync custom methods }
#-- Gpoddersync.on_gPodderSync_destroy {
def on_gPodderSync_destroy(self, widget, *args):
pass
#-- Gpoddersync.on_gPodderSync_destroy }
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)
#-- 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
#gtk.gdk.threads_init()
gobject.threads_init()
bindtextdomain( app_name, locale_dir)
app_version = __version__
g_podder = Gpodder()
#g_podder_channel = Gpodderchannel()
#g_podder_properties = Gpodderproperties()
#g_podder_episode = Gpodderepisode()
#g_podder_sync = Gpoddersync()
#g_podder_opml_lister = Gpodderopmllister()
g_podder.set_icon()
g_podder.run()
if __name__ == "__main__":
print _("Please do not call gpodder.py directly. Instead, call the gpodder binary.")
sys.exit( -1)
#-- main }