gpodder/src/gpodder/gui.py

1837 lines
76 KiB
Python
Raw Normal View History

# -*- coding: utf-8 -*-
# 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 <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 gtk.gdk
import gobject
import pango
import sys
import shutil
from xml.sax import saxutils
from threading import Event
from threading import Thread
from string import strip
from gpodder import util
from gpodder import opml
from SimpleGladeApp import SimpleGladeApp
from libpodcasts import podcastChannel
from libpodcasts import channelsToModel
from librssreader import rssReader
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 <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-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' ]
scalable_dir = [ 'share', 'icons', 'hicolor', 'scalable', 'apps', 'gpodder.svg' ]
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):
gl = gPodderLib()
self.gPodder.resize( gl.main_window_width, gl.main_window_height)
self.gPodder.move( gl.main_window_x, gl.main_window_y)
self.channelPaned.set_position( gl.paned_position)
while gtk.events_pending():
gtk.main_iteration( False)
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, False)
self.comboAvailable.add_attribute( cellrenderer, 'text', 1)
self.comboAvailable.add_attribute( cellrenderer, 'weight', 4)
# new episodes
cellrenderer = gtk.CellRendererText()
cellrenderer.set_property('ellipsize', pango.ELLIPSIZE_END)
try:
cellrenderer.set_property('alignment', pango.ALIGN_RIGHT)
except:
log('Failed to set alignment property for CellRendererText - using old PyGTK?', sender = self)
cellrenderer.set_property( 'foreground', 'gray')
self.comboAvailable.pack_end( cellrenderer, True)
self.comboAvailable.add_attribute( cellrenderer, 'text', 3)
# cell renderers for channel tree
namecolumn = gtk.TreeViewColumn( _('Channel'))
namecolumn.set_resizable( True)
namecolumn.set_reorderable( True)
iconcell = gtk.CellRendererPixbuf()
namecolumn.pack_start( iconcell, False)
namecolumn.add_attribute( iconcell, 'pixbuf', 8)
namecell = gtk.CellRendererText()
namecell.set_property('ellipsize', pango.ELLIPSIZE_END)
namecolumn.pack_start( namecell, True)
namecolumn.add_attribute( namecell, 'markup', 7)
namecolumn.add_attribute( namecell, 'weight', 4)
namecolumn.set_expand( True)
newcell = gtk.CellRendererText()
namecolumn.pack_end( newcell, False)
namecolumn.add_attribute( newcell, 'text', 5)
namecolumn.add_attribute( newcell, 'weight', 4)
namecolumn.set_expand( False)
self.treeChannels.append_column( namecolumn)
self.treeChannels.set_rules_hint( True)
# enable alternating colors hint
self.treeAvailable.set_rules_hint( True)
iconcell = gtk.CellRendererPixbuf()
iconcolumn = gtk.TreeViewColumn( _("Status"), iconcell)
iconcolumn.add_attribute( iconcell, "icon-name", 4)
playedcell = gtk.CellRendererPixbuf()
playedcolumn = gtk.TreeViewColumn( _("New"), playedcell)
playedcolumn.add_attribute( playedcell, "icon-name", 8)
self.played_column = playedcolumn
namecell = gtk.CellRendererText()
#namecell.set_property('ellipsize', pango.ELLIPSIZE_END)
namecolumn = gtk.TreeViewColumn( _("Episode"), namecell, text=1)
namecolumn.set_sizing( gtk.TREE_VIEW_COLUMN_AUTOSIZE)
sizecell = gtk.CellRendererText()
sizecolumn = gtk.TreeViewColumn( _("Size"), sizecell, text=2)
releasecell = gtk.CellRendererText()
releasecolumn = gtk.TreeViewColumn( _("Released"), releasecell, text=5)
desccell = gtk.CellRendererText()
desccell.set_property('ellipsize', pango.ELLIPSIZE_END)
desccolumn = gtk.TreeViewColumn( _("Description"), desccell, text=6)
for itemcolumn in ( iconcolumn, playedcolumn, namecolumn, sizecolumn, releasecolumn, desccolumn ):
itemcolumn.set_resizable( True)
itemcolumn.set_reorderable( True)
self.treeAvailable.append_column( itemcolumn)
# enable search in treeavailable
self.treeAvailable.set_search_equal_func( self.treeAvailable_search_equal)
# 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.download_status_updated)
self.treeDownloads.set_model( self.download_status_manager.getModel())
# tooltips :)
self.tooltips = gtk.Tooltips()
self.tooltips.set_tip( self.btnEditChannel, _("Edit channel information"))
#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)
# Subscribed channels
self.active_channel = None
self.channels = gPodderChannelReader().read( force_update = False)
# 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)
# Now, update the feed cache, when everything's in place
self.update_feed_cache( force_update = gl.update_on_startup)
#-- Gpodder.new }
#-- Gpodder custom methods {
def playback_episode( self, current_channel, current_podcast):
( success, application ) = gPodderLib().playback_episode( current_channel, current_podcast)
if not success:
self.show_message( _('The selected player application cannot be found. Please check your media player settings in the preferences dialog.'), _('Error opening player: %s') % ( saxutils.escape( application), ))
self.updateTreeView()
def treeAvailable_search_equal( self, model, column, key, iter, data = None):
if model == None:
return True
key = key.lower()
# columns, as defined in libpodcasts' get model method
# 1 = episode title, 7 = description
columns = (1, 7)
for column in columns:
value = model.get_value( iter, column).lower()
if value.find( key) != -1:
return False
return True
def downloads_changed( self):
if self.wNotebook.get_current_page() == 0:
self.toolCancel.set_sensitive( False)
else:
self.toolCancel.set_sensitive( self.download_status_manager.has_items())
def play_or_download( self):
if self.wNotebook.get_current_page() > 0:
return
is_download_button = False
is_torrent = False
gl = gPodderLib()
try:
selection = self.treeAvailable.get_selection()
if selection.count_selected_rows() == 0:
self.toolPlay.set_sensitive( False)
self.toolDownload.set_sensitive( False)
self.toolTransfer.set_sensitive( False)
return
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
if self.active_channel.get_file_type( self.active_channel.find_episode(url)) == 'torrent':
is_torrent = True
except:
is_download_button = True
if is_download_button:
self.toolPlay.set_sensitive( False)
self.toolDownload.set_sensitive( True)
self.toolTransfer.set_sensitive( False)
elif is_torrent:
self.toolPlay.set_sensitive( False)
# Only enable download button when using gnome-bittorrent
self.toolDownload.set_sensitive( gl.use_gnome_bittorrent)
self.toolTransfer.set_sensitive( False)
else:
self.toolPlay.set_sensitive( True)
self.toolDownload.set_sensitive( False)
if gl.device_type != 'none':
self.toolTransfer.set_sensitive( True)
def download_status_updated( self):
count = self.download_status_manager.count()
if count:
self.labelDownloads.set_text( _('Downloads (%d)') % count)
else:
self.labelDownloads.set_text( _('Downloads'))
self.updateComboBox()
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, download_status_manager = self.download_status_manager))
self.comboAvailable.set_active( old_active)
self.treeChannels.set_model( self.comboAvailable.get_model())
if old_active > -1:
self.treeChannels.get_selection().select_path( old_active)
except:
pass
def updateTreeView( self):
gl = gPodderLib()
self.played_column.set_visible( gl.show_played)
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, download_status_manager = self.download_status_manager))
# 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.treeAvailable.columns_autosize()
self.play_or_download()
else:
if self.treeAvailable.get_model():
self.treeAvailable.get_model().clear()
index = self.comboAvailable.get_active()
if index > -1:
self.treeChannels.get_selection().select_path( index)
def show_message( self, message, title = None):
dlg = gtk.MessageDialog( self.gPodder, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, gtk.BUTTONS_OK)
if title:
dlg.set_title( title)
markup = '<span weight="bold" size="larger">%s</span>%s%s' % ( title, "\n\n", message, )
else:
markup = '<span weight="bold" size="larger">%s</span>' % 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:
dlg.set_title( title)
markup = '<span weight="bold" size="larger">%s</span>%s%s' % ( title, "\n\n", message, )
else:
markup = '<span weight="bold" size="larger">%s</span>' % message
dlg.set_markup( markup)
if dlg.run() == gtk.RESPONSE_YES:
confirmation_result = True
dlg.destroy()
return confirmation_result
def switched_notebook( self, notebook, page, page_num):
if page_num == 0:
self.updateComboBox()
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)
return False
return True
def add_new_channel( self, result = None, ask_download_new = True):
gl = gPodderLib()
result = util.normalize_feed_url( result)
if result:
for old_channel in self.channels:
if old_channel.url == result:
self.show_message( _('You have already subscribed to this channel: %s') % ( saxutils.escape( old_channel.title), ), _('Already added'))
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):
(username, password) = util.username_password_from_url( result)
if username and self.show_confirmation( _('You have supplied <b>%s</b> as username and a password for this feed. Would you like to use the same authentication data for downloading episodes?') % ( saxutils.escape( username), ), _('Password authentication')):
channel.username = username
channel.password = password
log('Saving authentication data for episode downloads..', sender = self)
channel.save_metadata_to_localdb()
# ask user to download some new episodes
self.comboAvailable.set_active( len( self.channels)-1)
if ask_download_new:
self.on_btnDownloadNewer_clicked( None)
else:
if result:
title = _('URL scheme not supported')
message = _('gPodder currently only supports URLs starting with <b>http://</b>, <b>feed://</b> or <b>ftp://</b>.')
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, sync_played_episodes = not gPodderLib().only_sync_not_played)
i += 1
sync.set_progress_overall( i, len(self.ldb.channel_list))
else:
sync.sync_channel( self.active_channel, episodes, True)
sync.close( success = not sync.cancelled)
gobject.idle_add( self.updateTreeView)
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)
gobject.idle_add( self.updateTreeView)
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( '<i>%s</i>' % title)
progressbar.set_text( _('%d of %d channels updated') % ( position, count ))
if 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( '<span weight="bold" size="larger">%s</span>' % 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() == 'toolPlay') 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
if current_channel.get_file_type( current_podcast) != 'torrent':
self.playback_episode( current_channel, current_podcast)
return
if widget.get_name() == 'treeAvailable':
gpe = Gpodderepisode( gpodderwindow = self.gPodder)
gpe.set_episode( current_podcast, current_channel)
if os.path.exists( filename) and current_channel.get_file_type( current_podcast) == 'torrent':
gpe.set_download_callback( lambda: current_channel.addDownloadedItem( current_podcast))
elif os.path.exists( filename):
gpe.set_play_callback( lambda: self.playback_episode( current_channel, current_podcast))
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) and not current_channel.get_file_type(current_podcast) == 'torrent':
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 and not current_channel.get_file_type(current_podcast) == 'torrent':
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.updateComboBox()
#-- 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()
gl = gPodderLib()
size = self.gPodder.get_size()
pos = self.gPodder.get_position()
gl.main_window_width = size[0]
gl.main_window_height = size[1]
gl.main_window_x = pos[0]
gl.main_window_y = pos[1]
gl.paned_position = self.channelPaned.get_position()
gl.propertiesChanged()
self.gtk_main_quit()
sys.exit( 0)
#-- 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:
new_episodes = channel.get_new_episodes( download_status_manager = self.download_status_manager)
for episode in new_episodes:
to_download.append( ( channel, episode ))
if len(new_episodes):
if len(new_episodes) == 1:
new_part = ' ' + _('<b>1</b> new episode in <b>%s</b>') % ( saxutils.escape(channel.title), )
else:
new_part = ' ' + _('<b>%d</b> new episodes in <b>%s</b>') % ( len( new_episodes), saxutils.escape(channel.title), )
message_part += new_part + "\n"
new_sum += len(new_episodes)
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.updateComboBox()
#-- 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 <b>python-gpod</b> and <b>python-pymad</b> 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 <b>python-gpod</b> and <b>python-pymad</b> 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 <b>Podcasts</b> 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, showmessage = self.show_message)
prop.set_uar( self.user_apps_reader)
prop.set_callback_finished( self.updateComboBox)
#-- Gpodder.on_itemPreferences_activate }
#-- Gpodder.on_itemAddChannel_activate {
def on_itemAddChannel_activate(self, widget, *args):
if self.channelPaned.get_position() < 200:
self.channelPaned.set_position( 200)
self.entryAddChannel.set_text( _('Enter podcast URL'))
self.entryAddChannel.grab_focus()
#-- Gpodder.on_itemAddChannel_activate }
#-- Gpodder.on_itemEditChannel_activate {
def on_itemEditChannel_activate(self, widget, *args):
if self.active_channel == None:
title = _('No channel selected')
message = _('Please select a channel in the channels list to edit.')
self.show_message( message, title)
return
active_channel = self.active_channel
active = self.comboAvailable.get_active()
gpc = Gpodderchannel( gpodderwindow = self.gPodder)
result = gpc.edit_channel( active_channel)
self.updateComboBox()
if result != active_channel.url and result != None and result != "" and (result[:4] == "http" or result[:3] == "ftp"):
old_channels = self.channels
log( 'Changing channel #%d from "%s" to "%s"', active, active_channel.url, result)
self.channels = self.channels[0:active] + [ podcastChannel( url = result) ] + self.channels[active+1:]
if not self.refetch_channel_list():
# Revert to old channel list
self.show_message( _('I will now revert your channel list to its original state.'), _('Editing failed'))
self.channels = old_channels
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 <b>%s</b> 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:
filename = dlg.get_filename()
exporter = opml.Exporter( filename)
if not exporter.write( self.channels):
self.show_message( _('Could not export OPML to file. Please check your permissions.'), _('OPML export failed'))
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, lambda url: self.add_new_channel(url,False), lambda: self.on_itemDownloadAllNew_activate( self.gPodder))
#-- 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( scalable_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):
page_num = args[1]
if page_num == 0:
self.toolCancel.set_sensitive( False)
self.play_or_download()
else:
self.toolDownload.set_sensitive( False)
self.toolPlay.set_sensitive( False)
self.toolTransfer.set_sensitive( False)
self.toolCancel.set_sensitive( self.download_status_manager.has_items())
#-- Gpodder.on_wNotebook_switch_page }
#-- Gpodder.on_treeChannels_row_activated {
def on_treeChannels_row_activated(self, widget, *args):
self.on_itemEditChannel_activate( self.treeChannels)
#-- Gpodder.on_treeChannels_row_activated }
#-- Gpodder.on_treeChannels_cursor_changed {
def on_treeChannels_cursor_changed(self, widget, *args):
(model,iter) = self.treeChannels.get_selection().get_selected()
pos = model.get_value( iter, 6)
self.comboAvailable.set_active( pos)
#-- Gpodder.on_treeChannels_cursor_changed }
#-- Gpodder.on_entryAddChannel_changed {
def on_entryAddChannel_changed(self, widget, *args):
active = self.entryAddChannel.get_text() not in ('', _('Enter podcast URL'))
self.btnAddChannel.set_sensitive( active)
#-- Gpodder.on_entryAddChannel_changed }
#-- Gpodder.on_btnAddChannel_clicked {
def on_btnAddChannel_clicked(self, widget, *args):
url = self.entryAddChannel.get_text()
self.entryAddChannel.set_text('')
self.add_new_channel( url)
#-- Gpodder.on_btnAddChannel_clicked }
#-- 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 != None:
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() == 'toolTransfer':
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)
self.downloads_changed()
#-- 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 = channel.get_new_episodes( download_status_manager = self.download_status_manager)
if not episodes_to_download:
title = _('No episodes to download')
message = _('You have already downloaded the most recent episodes from <b>%s</b>.') % ( channel.title, )
self.show_message( message, title)
else:
if len(episodes_to_download) > 1:
if len(episodes_to_download) < 10:
e_str = '\n'.join( [ ' <b>'+saxutils.escape(e.title)+'</b>' for e in episodes_to_download ] )
else:
e_str = '\n'.join( [ ' <b>'+saxutils.escape(e.title)+'</b>' for e in episodes_to_download[:7] ] )
e_str_2 = _('(...%d more episodes...)') % ( len(episodes_to_download)-7, )
e_str = '%s\n <i>%s</i>' % ( 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?') % saxutils.escape(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.toolDownload, 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.')
self.downloads_changed()
#-- 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.toolCancel, 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):
def __init__(self, path="gpodder.glade",
root="gPodderChannel",
domain=app_name, **kwargs):
self.waiting = Event()
self.channel = None
self.url = None
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 edit_channel( self, channel):
self.channel = channel
self.gPodderChannel.set_title( channel.title)
self.entryTitle.set_text( channel.title)
self.entryURL.set_text( channel.url)
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)
if channel.username:
self.FeedUsername.set_text( channel.username)
if channel.password:
self.FeedPassword.set_text( channel.password)
gPodderLib().get_image_from_url( channel.image, self.imgCover.set_from_pixbuf, self.labelCoverStatus.set_text, self.labelCoverStatus.hide, channel.cover_file)
b = gtk.TextBuffer()
b.set_text( channel.description)
self.channel_description.set_buffer( b)
# Wait for the user to close the window
while not self.waiting.isSet():
self.waiting.wait( 0.01)
while gtk.events_pending():
gtk.main_iteration( False)
return self.url
#-- Gpodderchannel custom methods }
#-- Gpodderchannel.on_gPodderChannel_destroy {
def on_gPodderChannel_destroy(self, widget, *args):
self.url = self.entryURL.get_text()
self.waiting.set()
#-- 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.channel.sync_to_devices = not self.cbNoSync.get_active()
self.channel.is_music_channel = self.cbMusicChannel.get_active()
self.channel.device_playlist_name = self.musicPlaylist.get_text()
self.channel.set_custom_title( self.entryTitle.get_text())
self.channel.username = self.FeedUsername.get_text().strip()
self.channel.password = self.FeedPassword.get_text()
self.channel.save_metadata_to_localdb()
self.gPodderChannel.destroy()
#-- Gpodderchannel.on_btnOK_clicked }
#-- Gpodderchannel.on_btnCancel_clicked {
def on_btnCancel_clicked(self, widget, *args):
self.gPodderChannel.destroy()
#-- 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)
self.message_callback = None
if 'showmessage' in kwargs:
self.message_callback = kwargs['showmessage']
#-- 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)
if gl.torrentdir:
self.chooserBitTorrentTo.set_filename( gl.torrentdir)
self.radio_copy_torrents.set_active( not gl.use_gnome_bittorrent)
self.radio_gnome_bittorrent.set_active( gl.use_gnome_bittorrent)
self.updateonstartup.set_active(gl.update_on_startup)
self.downloadnew.set_active(gl.download_after_update)
self.cbLimitDownloads.set_active(gl.limit_rate)
self.spinLimitDownloads.set_value(gl.limit_rate_value)
self.cbMaxDownloads.set_active(gl.max_downloads_enabled)
self.spinMaxDownloads.set_value(gl.max_downloads)
self.showplayed.set_active(gl.show_played)
self.only_sync_not_played.set_active(gl.only_sync_not_played)
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)
# 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)
# 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 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 self.message_callback and gl.downloaddir != self.chooserDownloadTo.get_filename():
gobject.idle_add( self.message_callback, _('There has been an error moving your downloads to the specified location. The old download directory will be used instead.'), _('Error moving downloads'))
self.message_callback = None
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_cbMaxDownloads_toggled {
def on_cbMaxDownloads_toggled(self, widget, *args):
self.spinMaxDownloads.set_sensitive( self.cbMaxDownloads.get_active())
#-- Gpodderproperties.on_cbMaxDownloads_toggled }
#-- Gpodderproperties.on_cbLimitDownloads_toggled {
def on_cbLimitDownloads_toggled(self, widget, *args):
self.spinLimitDownloads.set_sensitive( self.cbLimitDownloads.get_active())
#-- Gpodderproperties.on_cbLimitDownloads_toggled }
#-- 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_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_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()
if gl.downloaddir != self.chooserDownloadTo.get_filename():
new_download_dir = self.chooserDownloadTo.get_filename()
download_dir_size = util.calculate_size( gl.downloaddir)
download_dir_size_string = util.format_filesize( 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 <b>%s</b> to <b>%s</b>...') % ( 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 = util.calculate_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') % ( util.format_filesize( 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.torrentdir = self.chooserBitTorrentTo.get_filename()
gl.use_gnome_bittorrent = self.radio_gnome_bittorrent.get_active()
gl.update_on_startup = self.updateonstartup.get_active()
gl.download_after_update = self.downloadnew.get_active()
gl.limit_rate = self.cbLimitDownloads.get_active()
gl.limit_rate_value = self.spinLimitDownloads.get_value()
gl.max_downloads_enabled = self.cbMaxDownloads.get_active()
gl.max_downloads = int(self.spinMaxDownloads.get_value())
gl.show_played = self.showplayed.get_active()
gl.update_tags = self.updatetags.get_active()
gl.only_sync_not_played = self.only_sync_not_played.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()
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):
gl = gPodderLib()
self.episode = episode
self.channel = channel
self.episode_title.set_markup( '<span weight="bold" size="larger">%s</span>' % saxutils.escape( 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 channel and channel.is_downloaded( episode):
self.btnSaveFile.show_all()
if not episode.link and channel:
self.LabelWebsiteLink.set_text( channel.link)
if channel:
smalltext = _('<i>from %s</i>') % ( 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 }
#-- Gpodderepisode.on_btnSaveFile_clicked {
def on_btnSaveFile_clicked(self, widget, *args):
fn = self.channel.getPodcastFilename( self.episode.url)
ext = os.path.splitext(fn)[1]
suggestion = self.channel.title + ' - ' + self.episode.title + ext
dlg = gtk.FileChooserDialog( title=_("Save episode as file"), parent = self.gPodderEpisode, action = gtk.FILE_CHOOSER_ACTION_SAVE)
dlg.set_current_name( suggestion)
dlg.set_current_folder( os.path.expanduser('~'))
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[-len(ext):] != ext:
foutname = foutname + ext
log( 'Saving episode as: %s', foutname, sender = self)
try:
shutil.copyfile( fn, foutname)
except:
log('Error saving file :/', sender = self)
dlg.destroy()
#-- Gpodderepisode.on_btnSaveFile_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( '<i>%s</i>' % episode)
if channel != None:
self.labelChannel.set_markup( '<i>%s</i>' % 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( '<b><big>%s</big></b>' % header)
else:
self.label_header.set_markup( '<b><big>%s</big></b>' % 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, error_messages = []):
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.')
elif len(error_messages):
title = _('Synchronization error')
header = _('An error has occurred')
body = '\n'.join( error_messages)
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( '<big><b>%s</b></big>' % 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
self.callback_finished = None
togglecell = gtk.CellRendererToggle()
togglecell.set_property( 'activatable', True)
togglecell.connect( 'toggled', self.callback_edited)
togglecolumn = gtk.TreeViewColumn( '', togglecell, active=0)
titlecell = gtk.CellRendererText()
titlecolumn = gtk.TreeViewColumn( _('Channel'), titlecell, markup=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()
url = model[path][2]
model[path][0] = not model[path][0]
if model[path][0]:
self.channels.append( url)
else:
self.channels.remove( url)
self.btnOK.set_sensitive( bool(len(self.channels)))
def thread_func( self):
url = self.entryURL.get_text()
importer = opml.Importer( url)
model = importer.get_model()
gobject.idle_add( self.treeviewChannelChooser.set_model, model)
gobject.idle_add( self.labelStatus.set_label, '')
gobject.idle_add( self.btnDownloadOpml.set_sensitive, True)
gobject.idle_add( self.entryURL.set_sensitive, True)
gobject.idle_add( self.treeviewChannelChooser.set_sensitive, True)
self.channels = []
def get_channels_from_url( self, url, callback_for_channel = None, callback_finished = None):
if callback_for_channel:
self.callback_for_channel = callback_for_channel
if callback_finished:
self.callback_finished = callback_finished
self.labelStatus.set_label( _('Downloading, please wait...'))
self.entryURL.set_text( url)
self.btnDownloadOpml.set_sensitive( False)
self.entryURL.set_sensitive( False)
self.btnOK.set_sensitive( False)
self.treeviewChannelChooser.set_sensitive( False)
Thread( target = self.thread_func).start()
#-- Gpodderopmllister custom methods }
#-- Gpodderopmllister.on_gPodderOpmlLister_destroy {
def on_gPodderOpmlLister_destroy(self, widget, *args):
pass
#-- Gpodderopmllister.on_gPodderOpmlLister_destroy }
#-- Gpodderopmllister.on_btnDownloadOpml_clicked {
def on_btnDownloadOpml_clicked(self, widget, *args):
self.get_channels_from_url( self.entryURL.get_text())
#-- Gpodderopmllister.on_btnDownloadOpml_clicked }
#-- 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:
self.callback_for_channel( url)
if self.callback_finished:
self.callback_finished()
#-- 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__
gtk.window_set_default_icon_name( 'gpodder')
g_podder = Gpodder()
g_podder.run()
#-- main }