2007-04-01 19:53:04 +02:00
# -*- coding: utf-8 -*-
2005-11-21 19:21:25 +01:00
#
2007-08-29 20:30:26 +02:00
# gPodder - A media aggregator and podcast client
2009-02-01 21:22:21 +01:00
# Copyright (c) 2005-2009 Thomas Perl and the gPodder Team
2006-04-07 22:22:30 +02:00
#
2007-08-29 20:30:26 +02:00
# gPodder is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
2006-04-07 22:22:30 +02:00
#
2007-08-29 20:30:26 +02:00
# gPodder is distributed in the hope that it will be useful,
2006-04-07 22:22:30 +02:00
# 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
2007-08-29 20:30:26 +02:00
# along with this program. If not, see <http://www.gnu.org/licenses/>.
2005-11-21 19:21:25 +01:00
#
import os
import gtk
2007-01-28 10:21:39 +01:00
import gtk . gdk
2005-11-21 19:21:25 +01:00
import gobject
2006-11-20 12:51:20 +01:00
import pango
2005-11-21 19:21:25 +01:00
import sys
2007-04-23 17:18:31 +02:00
import shutil
2008-01-21 10:52:09 +01:00
import subprocess
2008-03-03 23:09:34 +01:00
import glob
2008-03-20 11:24:26 +01:00
import time
2008-08-04 16:54:18 +02:00
import urllib
2008-06-14 15:17:55 +02:00
import urllib2
2008-06-30 03:10:18 +02:00
import datetime
2009-06-29 04:20:29 +02:00
import fnmatch
2009-08-10 18:32:08 +02:00
import tempfile
2009-08-24 18:11:58 +02:00
import collections
2006-03-04 21:45:01 +01:00
2007-07-05 23:07:16 +02:00
from xml . sax import saxutils
2006-04-07 03:43:06 +02:00
from threading import Thread
2006-03-04 21:45:01 +01:00
from string import strip
2005-11-21 19:21:25 +01:00
2008-04-06 02:19:03 +02:00
import gpodder
2009-05-09 15:21:04 +02:00
2009-05-30 11:31:02 +02:00
try :
import dbus
import dbus . service
import dbus . mainloop
import dbus . glib
except ImportError :
# Mock the required D-Bus interfaces with no-ops (ugly? maybe.)
2009-05-09 15:21:04 +02:00
class dbus :
class SessionBus :
def __init__ ( self , * args , * * kwargs ) :
pass
class glib :
class DBusGMainLoop :
pass
class service :
@staticmethod
def method ( interface ) :
return lambda x : x
class BusName :
def __init__ ( self , * args , * * kwargs ) :
pass
class Object :
def __init__ ( self , * args , * * kwargs ) :
pass
2009-06-12 00:51:13 +02:00
from gpodder import feedcore
2007-08-07 20:11:31 +02:00
from gpodder import util
2007-08-19 16:28:24 +02:00
from gpodder import opml
2007-08-24 16:49:41 +02:00
from gpodder import services
2008-01-28 12:38:53 +01:00
from gpodder import sync
2007-09-18 20:25:25 +02:00
from gpodder import download
2008-12-08 17:10:53 +01:00
from gpodder import my
2008-03-11 18:45:52 +01:00
from gpodder . liblogger import log
2009-08-24 17:02:35 +02:00
from gpodder import youtube
2008-03-11 18:45:52 +01:00
2009-05-07 16:26:07 +02:00
_ = gpodder . gettext
2008-03-11 18:45:52 +01:00
try :
from gpodder import trayicon
have_trayicon = True
except Exception , exc :
2008-04-22 22:24:19 +02:00
log ( ' Warning: Could not import gpodder.trayicon. ' , traceback = True )
2008-03-11 18:45:52 +01:00
log ( ' Warning: This probably means your PyGTK installation is too old! ' )
have_trayicon = False
2005-11-21 19:21:25 +01:00
2009-08-13 23:36:18 +02:00
from gpodder . model import PodcastChannel
2009-08-24 18:11:58 +02:00
from gpodder . dbsqlite import Database
2009-08-13 23:36:18 +02:00
2009-08-13 23:41:34 +02:00
from gpodder . gtkui . base import GtkBuilderWidget
2009-08-13 23:36:18 +02:00
from gpodder . gtkui . model import PodcastListModel
from gpodder . gtkui . model import EpisodeListModel
2009-08-13 23:56:25 +02:00
from gpodder . gtkui . opml import OpmlListModel
2009-08-24 18:11:58 +02:00
from gpodder . gtkui . config import UIConfig
2009-08-24 16:17:32 +02:00
from gpodder . gtkui . download import DownloadStatusModel
2009-08-24 16:27:17 +02:00
from gpodder . gtkui . services import DependencyModel
2009-08-24 16:47:59 +02:00
from gpodder . gtkui . services import CoverDownloader
2009-08-24 16:50:06 +02:00
from gpodder . gtkui . widgets import SimpleMessageArea
2009-08-24 16:52:37 +02:00
from gpodder . gtkui . desktopfile import UserAppsReader
2009-08-24 19:04:28 +02:00
2009-08-24 18:54:45 +02:00
from gpodder . gtkui . interface . common import BuilderWidget
2009-08-24 19:04:28 +02:00
from gpodder . gtkui . interface . channel import gPodderChannel
2009-08-24 19:10:52 +02:00
from gpodder . gtkui . interface . addpodcast import gPodderAddPodcast
2005-11-21 19:21:25 +01:00
2009-08-24 19:31:58 +02:00
if gpodder . interface == gpodder . GUI :
from gpodder . gtkui . interface . preferences import gPodderPreferences
else :
from gpodder . gtkui . maemo . preferences import gPodderDiabloPreferences as gPodderPreferences
2009-08-24 19:43:49 +02:00
from gpodder . gtkui . interface . shownotes import gPodderShownotes
2009-08-24 19:50:13 +02:00
from gpodder . gtkui . interface . syncprogress import gPodderSyncProgress
2009-08-24 20:04:13 +02:00
from gpodder . gtkui . interface . podcastdirectory import gPodderPodcastDirectory
2009-08-24 20:12:12 +02:00
from gpodder . gtkui . interface . episodeselector import gPodderEpisodeSelector
2009-08-24 20:20:51 +02:00
from gpodder . gtkui . interface . deviceplaylist import gPodderDevicePlaylist
2009-08-24 20:24:57 +02:00
from gpodder . gtkui . interface . dependencymanager import gPodderDependencyManager
2009-08-24 20:41:16 +02:00
from gpodder . gtkui . interface . welcome import gPodderWelcome
2009-08-24 19:31:58 +02:00
2008-04-06 02:19:03 +02:00
if gpodder . interface == gpodder . GUI :
WEB_BROWSER_ICON = ' web-browser '
elif gpodder . interface == gpodder . MAEMO :
import hildon
WEB_BROWSER_ICON = ' qgn_toolb_browser_web '
Sat, 29 Mar 2008 17:13:26 +0100 <thp@perli.net>
Project management updates (authors, contributors and copyright)
* AUTHORS: Removed (was outdated); content now in gui.py (AboutDialog)
* bin/gpodder, data/po/Makefile, doc/dev/copyright_notice,
doc/dev/win32/setup-win32.py, INSTALL, Makefile, README,
setup.py: Updated Copyright and old website URL to include 2008, the
gPodder team and www.gpodder.org
* src/gpodder/*.py: Updated Copyright years
* src/gpodder/gui.py: Add list of contributors from AUTHORS file and
from the content on the website's news page (please mail me if I
forgot to mention you as a contributor, I surely have missed a few);
make the AboutDialog's application name "gPodder" (from gpodder) and
add an URL hook function to the AboutDialog, so the website is opened
in the user's default web browser
git-svn-id: svn://svn.berlios.de/gpodder/trunk@648 b0d088ad-0a06-0410-aad2-9ed5178a7e87
2008-03-29 17:16:55 +01:00
app_authors = [
_ ( ' Current maintainer: ' ) , ' Thomas Perl <thpinfo.com> ' ,
' ' ,
_ ( ' Patches, bug reports and donations by: ' ) , ' Adrien Beaucreux ' ,
2009-02-01 21:35:07 +01:00
' Alain Tauch ' , ' Alex Ghitza ' , ' Alistair Sutton ' , ' Anders Kvist ' , ' Andrei Dolganov ' , ' Andrew Bennett ' , ' Andy Busch ' ,
2008-07-10 23:55:13 +02:00
' Antonio Roversi ' , ' Aravind Seshadri ' , ' Atte André Jensen ' , ' audioworld ' ,
2009-02-01 21:35:07 +01:00
' Bastian Staeck ' , ' Bernd Schlapsi ' , ' Bill Barnard ' , ' Bill Peters ' , ' Bjørn Rasmussen ' , ' Camille Moncelier ' , ' Casey Watson ' ,
2009-07-12 17:25:53 +02:00
' Carlos Moffat ' , ' Chris Arnold ' , ' Chris Moffitt ' , ' Clark Burbidge ' , ' Corey Goldberg ' , ' corq ' , ' Cory Albrecht ' , ' daggpod ' , ' Daniel Ramos ' ,
2009-03-31 13:03:38 +02:00
' David Spreen ' , ' Doug Hellmann ' , ' Edouard Pellerin ' , ' Fabio Fiorentini ' , ' FFranci72 ' , ' Florian Richter ' , ' Frank Harper ' ,
2008-12-05 13:54:55 +01:00
' Franz Seidl ' , ' FriedBunny ' , ' Gerrit Sangel ' , ' Gilles Lehoux ' , ' Götz Waschk ' ,
2009-02-09 16:50:00 +01:00
' Haim Roitgrund ' , ' Heinz Erhard ' , ' Hex ' , ' Holger Bauer ' , ' Holger Leskien ' , ' Iwan van der Kleijn ' , ' Jens Thiele ' ,
2008-05-05 15:55:27 +02:00
' Jérôme Chabod ' , ' Jerry Moss ' ,
2009-04-11 11:52:19 +02:00
' Jessica Henline ' , ' Jim Nygård ' , ' João Trindade ' , ' Joel Calado ' , ' John Ferguson ' ,
2008-04-23 22:33:27 +02:00
' José Luis Fustel ' , ' Joseph Bleau ' , ' Julio Acuña ' , ' Junio C Hamano ' ,
2008-06-13 14:30:42 +02:00
' Jürgen Schinker ' , ' Justin Forest ' ,
2009-03-11 03:51:32 +01:00
' Konstantin Ryabitsev ' , ' Leonid Ponomarev ' , ' Marco Antonio Villegas Vega ' , ' Marcos Hernández ' , ' Mark Alford ' , ' Markus Golser ' , ' Mehmet Nur Olcay ' , ' Michael Salim ' ,
2009-02-01 21:35:07 +01:00
' Mika Leppinen ' , ' Mike Coulson ' , ' Mikolaj Laczynski ' , ' Morten Juhl-Johansen Zölde-Fejér ' , ' Mykola Nikishov ' , ' narf ' ,
2008-03-30 10:50:34 +02:00
' Nick L. ' , ' Nicolas Quienot ' , ' Ondrej Vesely ' ,
Sat, 29 Mar 2008 17:13:26 +0100 <thp@perli.net>
Project management updates (authors, contributors and copyright)
* AUTHORS: Removed (was outdated); content now in gui.py (AboutDialog)
* bin/gpodder, data/po/Makefile, doc/dev/copyright_notice,
doc/dev/win32/setup-win32.py, INSTALL, Makefile, README,
setup.py: Updated Copyright and old website URL to include 2008, the
gPodder team and www.gpodder.org
* src/gpodder/*.py: Updated Copyright years
* src/gpodder/gui.py: Add list of contributors from AUTHORS file and
from the content on the website's news page (please mail me if I
forgot to mention you as a contributor, I surely have missed a few);
make the AboutDialog's application name "gPodder" (from gpodder) and
add an URL hook function to the AboutDialog, so the website is opened
in the user's default web browser
git-svn-id: svn://svn.berlios.de/gpodder/trunk@648 b0d088ad-0a06-0410-aad2-9ed5178a7e87
2008-03-29 17:16:55 +01:00
' Ortwin Forster ' , ' Paul Elliot ' , ' Paul Rudkin ' ,
2008-07-06 23:11:55 +02:00
' Pavel Mlčoch ' , ' Peter Hoffmann ' , ' PhilF ' , ' Philippe Gouaillier ' , ' Pieter de Decker ' ,
2009-02-01 21:35:07 +01:00
' Preben Randhol ' , ' Rafael Proença ' , ' R.Bell ' , ' red26wings ' , ' Richard Voigt ' ,
2009-02-14 16:31:53 +01:00
' Robert Young ' , ' Roel Groeneveld ' , ' Romain Janvier ' ,
2008-09-27 14:07:06 +02:00
' Scott Wegner ' , ' Sebastian Krause ' , ' Seth Remington ' , ' Shane Donohoe ' , ' Silvio Sisto ' , ' SPGoetze ' ,
2009-02-01 21:35:07 +01:00
' S. Rust ' ,
2008-12-05 13:54:55 +01:00
' Stefan Lohmaier ' , ' Stephan Buys ' , ' Steve McCarthy ' , ' Stylianos Papanastasiou ' , ' Teo Ramirez ' ,
Sat, 29 Mar 2008 17:13:26 +0100 <thp@perli.net>
Project management updates (authors, contributors and copyright)
* AUTHORS: Removed (was outdated); content now in gui.py (AboutDialog)
* bin/gpodder, data/po/Makefile, doc/dev/copyright_notice,
doc/dev/win32/setup-win32.py, INSTALL, Makefile, README,
setup.py: Updated Copyright and old website URL to include 2008, the
gPodder team and www.gpodder.org
* src/gpodder/*.py: Updated Copyright years
* src/gpodder/gui.py: Add list of contributors from AUTHORS file and
from the content on the website's news page (please mail me if I
forgot to mention you as a contributor, I surely have missed a few);
make the AboutDialog's application name "gPodder" (from gpodder) and
add an URL hook function to the AboutDialog, so the website is opened
in the user's default web browser
git-svn-id: svn://svn.berlios.de/gpodder/trunk@648 b0d088ad-0a06-0410-aad2-9ed5178a7e87
2008-03-29 17:16:55 +01:00
' Thomas Matthijs ' , ' Thomas Mills Hinkle ' , ' Thomas Nilsson ' ,
2008-12-05 13:54:55 +01:00
' Tim Michelsen ' , ' Tim Preetz ' , ' Todd Zullinger ' , ' Tomas Matheson ' , ' Ville-Pekka Vainio ' , ' Vitaliy Bondar ' , ' VladDrac ' ,
Sat, 29 Mar 2008 17:13:26 +0100 <thp@perli.net>
Project management updates (authors, contributors and copyright)
* AUTHORS: Removed (was outdated); content now in gui.py (AboutDialog)
* bin/gpodder, data/po/Makefile, doc/dev/copyright_notice,
doc/dev/win32/setup-win32.py, INSTALL, Makefile, README,
setup.py: Updated Copyright and old website URL to include 2008, the
gPodder team and www.gpodder.org
* src/gpodder/*.py: Updated Copyright years
* src/gpodder/gui.py: Add list of contributors from AUTHORS file and
from the content on the website's news page (please mail me if I
forgot to mention you as a contributor, I surely have missed a few);
make the AboutDialog's application name "gPodder" (from gpodder) and
add an URL hook function to the AboutDialog, so the website is opened
in the user's default web browser
git-svn-id: svn://svn.berlios.de/gpodder/trunk@648 b0d088ad-0a06-0410-aad2-9ed5178a7e87
2008-03-29 17:16:55 +01:00
' Vladimir Zemlyakov ' , ' Wilfred van Rooijen ' ,
' ' ,
' List may be incomplete - please contact me. '
]
2006-03-29 13:33:56 +02:00
2005-11-21 19:21:25 +01:00
2009-05-08 14:28:53 +02:00
class gPodder ( BuilderWidget , dbus . service . Object ) :
2009-05-26 13:47:47 +02:00
finger_friendly_widgets = [ ' btnCancelFeedUpdate ' , ' label2 ' , ' labelDownloads ' , ' btnCleanUpDownloads ' ]
2008-05-02 17:36:43 +02:00
ENTER_URL_TEXT = _ ( ' Enter podcast URL... ' )
2009-05-30 13:14:15 +02:00
APPMENU_ACTIONS = ( ' itemUpdate ' , ' itemDownloadAllNew ' , ' itemPreferences ' )
2009-05-30 13:28:16 +02:00
TREEVIEW_WIDGETS = ( ' treeAvailable ' , ' treeChannels ' , ' treeDownloads ' )
2009-02-25 14:57:45 +01:00
2009-08-24 18:11:58 +02:00
def __init__ ( self , bus_name , config ) :
2009-02-25 14:57:45 +01:00
dbus . service . Object . __init__ ( self , object_path = gpodder . dbus_gui_object_path , bus_name = bus_name )
2009-08-24 18:11:58 +02:00
self . db = Database ( gpodder . database_file )
self . config = config
2009-08-24 18:37:04 +02:00
BuilderWidget . __init__ ( self , None )
2008-04-24 18:28:39 +02:00
2005-11-21 19:21:25 +01:00
def new ( self ) :
2008-04-06 02:19:03 +02:00
if gpodder . interface == gpodder . MAEMO :
# Maemo-specific changes to the UI
2009-05-07 16:26:07 +02:00
gpodder . icon_file = gpodder . icon_file . replace ( ' .svg ' , ' .png ' )
2008-04-06 02:19:03 +02:00
self . app = hildon . Program ( )
gtk . set_application_name ( ' gPodder ' )
self . window = hildon . Window ( )
self . window . connect ( ' delete-event ' , self . on_gPodder_delete_event )
self . window . connect ( ' window-state-event ' , self . window_state_event )
2008-08-30 19:23:04 +02:00
2009-05-11 21:35:33 +02:00
self . itemUpdateChannel . set_visible ( True )
2008-04-06 02:19:03 +02:00
2009-01-19 19:00:24 +01:00
# Remove old toolbar from its parent widget
self . toolbar . get_parent ( ) . remove ( self . toolbar )
toolbar = gtk . Toolbar ( )
toolbar . set_style ( gtk . TOOLBAR_BOTH_HORIZ )
self . btnUpdateFeeds . get_parent ( ) . remove ( self . btnUpdateFeeds )
self . btnUpdateFeeds = gtk . ToolButton ( gtk . image_new_from_stock ( gtk . STOCK_REFRESH , gtk . ICON_SIZE_SMALL_TOOLBAR ) , _ ( ' Update all ' ) )
self . btnUpdateFeeds . set_is_important ( True )
self . btnUpdateFeeds . connect ( ' clicked ' , self . on_itemUpdate_activate )
toolbar . insert ( self . btnUpdateFeeds , - 1 )
self . btnUpdateFeeds . show_all ( )
self . btnUpdateSelectedFeed = gtk . ToolButton ( gtk . image_new_from_stock ( gtk . STOCK_REFRESH , gtk . ICON_SIZE_SMALL_TOOLBAR ) , _ ( ' Update selected ' ) )
self . btnUpdateSelectedFeed . set_is_important ( True )
self . btnUpdateSelectedFeed . connect ( ' clicked ' , self . on_itemUpdateChannel_activate )
toolbar . insert ( self . btnUpdateSelectedFeed , - 1 )
self . btnUpdateSelectedFeed . show_all ( )
self . toolFeedUpdateProgress = gtk . ToolItem ( )
self . pbFeedUpdate . reparent ( self . toolFeedUpdateProgress )
self . toolFeedUpdateProgress . set_expand ( True )
toolbar . insert ( self . toolFeedUpdateProgress , - 1 )
self . toolFeedUpdateProgress . hide ( )
self . btnCancelFeedUpdate = gtk . ToolButton ( gtk . STOCK_CLOSE )
self . btnCancelFeedUpdate . connect ( ' clicked ' , self . on_btnCancelFeedUpdate_clicked )
toolbar . insert ( self . btnCancelFeedUpdate , - 1 )
self . btnCancelFeedUpdate . hide ( )
self . toolbarSpacer = gtk . SeparatorToolItem ( )
self . toolbarSpacer . set_draw ( False )
self . toolbarSpacer . set_expand ( True )
toolbar . insert ( self . toolbarSpacer , - 1 )
self . toolbarSpacer . show ( )
2009-05-11 23:10:56 +02:00
self . wNotebook . set_show_tabs ( False )
self . tool_downloads = gtk . ToggleToolButton ( gtk . STOCK_GO_DOWN )
self . tool_downloads . connect ( ' toggled ' , self . on_tool_downloads_toggled )
self . tool_downloads . set_label ( _ ( ' Downloads ' ) )
self . tool_downloads . set_is_important ( True )
toolbar . insert ( self . tool_downloads , - 1 )
self . tool_downloads . show_all ( )
2009-01-19 19:00:24 +01:00
self . toolPreferences = gtk . ToolButton ( gtk . STOCK_PREFERENCES )
self . toolPreferences . connect ( ' clicked ' , self . on_itemPreferences_activate )
toolbar . insert ( self . toolPreferences , - 1 )
self . toolPreferences . show ( )
self . toolQuit = gtk . ToolButton ( gtk . STOCK_QUIT )
self . toolQuit . connect ( ' clicked ' , self . on_gPodder_delete_event )
toolbar . insert ( self . toolQuit , - 1 )
self . toolQuit . show ( )
# Add and replace toolbar with our new one
toolbar . show ( )
self . window . add_toolbar ( toolbar )
self . toolbar = toolbar
2008-04-06 02:19:03 +02:00
self . app . add_window ( self . window )
self . vMain . reparent ( self . window )
self . gPodder = self . window
# Reparent the main menu
menu = gtk . Menu ( )
for child in self . mainMenu . get_children ( ) :
2009-05-11 23:10:56 +02:00
child . get_parent ( ) . remove ( child )
menu . append ( self . set_finger_friendly ( child ) )
menu . append ( self . set_finger_friendly ( self . itemQuit . create_menu_item ( ) ) )
2009-05-30 13:14:15 +02:00
if hasattr ( hildon , ' AppMenu ' ) :
# Maemo 5 - use the new AppMenu with Buttons
self . appmenu = hildon . AppMenu ( )
for action_name in self . APPMENU_ACTIONS :
action = getattr ( self , action_name )
b = gtk . Button ( ' ' )
action . connect_proxy ( b )
self . appmenu . append ( b )
b = gtk . Button ( _ ( ' Classic menu ' ) )
b . connect ( ' clicked ' , lambda b : menu . popup ( None , None , None , 1 , 0 ) )
self . appmenu . append ( b )
self . window . set_app_menu ( self . appmenu )
else :
# Maemo 4 - just "reparent" the menu to the hildon window
self . window . set_menu ( menu )
2008-04-06 02:19:03 +02:00
self . mainMenu . destroy ( )
2008-04-22 20:34:41 +02:00
self . window . show ( )
2008-04-06 02:19:03 +02:00
# do some widget hiding
2009-05-11 21:35:33 +02:00
self . itemTransferSelected . set_visible ( False )
self . item_email_subscriptions . set_visible ( False )
self . menuView . set_visible ( False )
2008-04-24 18:28:39 +02:00
# get screen real estate
self . hboxContainer . set_border_width ( 0 )
2008-05-02 17:36:43 +02:00
2008-12-24 13:18:22 +01:00
# Offer importing of videocenter podcasts
if os . path . exists ( os . path . expanduser ( ' ~/videocenter ' ) ) :
2009-06-02 08:36:40 +02:00
self . item_upgrade_from_videocenter . set_visible ( True )
2008-12-24 13:18:22 +01:00
2008-09-27 14:20:43 +02:00
self . gPodder . connect ( ' key-press-event ' , self . on_key_press )
2009-08-11 00:36:38 +02:00
self . bluetooth_available = util . bluetooth_available ( )
2008-05-02 17:36:43 +02:00
2009-05-09 15:21:04 +02:00
if gpodder . win32 :
# FIXME: Implement e-mail sending of list in win32
self . item_email_subscriptions . set_sensitive ( False )
2009-08-24 18:11:58 +02:00
if self . config . show_url_entry_in_podcast_list :
2008-11-06 16:06:13 +01:00
self . hboxAddChannel . show ( )
2008-05-02 17:36:43 +02:00
2009-08-24 18:11:58 +02:00
if not gpodder . interface == gpodder . MAEMO and not self . config . show_toolbar :
2008-12-14 17:39:03 +01:00
self . toolbar . hide ( )
2008-05-02 17:36:43 +02:00
2009-08-24 18:11:58 +02:00
self . config . add_observer ( self . on_config_changed )
2008-05-02 17:36:43 +02:00
self . default_entry_text_color = self . entryAddChannel . get_style ( ) . text [ gtk . STATE_NORMAL ]
self . entryAddChannel . connect ( ' focus-in-event ' , self . entry_add_channel_focus )
self . entryAddChannel . connect ( ' focus-out-event ' , self . entry_add_channel_unfocus )
self . entry_add_channel_unfocus ( self . entryAddChannel , None )
2008-04-06 02:19:03 +02:00
2007-08-28 00:18:01 +02:00
self . uar = None
2008-03-20 11:24:26 +01:00
self . tray_icon = None
2009-08-24 19:43:49 +02:00
self . episode_shownotes_window = None
2009-04-01 13:34:19 +02:00
2009-08-24 16:17:32 +02:00
self . download_status_model = DownloadStatusModel ( )
2009-08-24 18:11:58 +02:00
self . download_queue_manager = download . DownloadQueueManager ( self . config )
2007-08-28 00:18:01 +02:00
2008-04-06 02:19:03 +02:00
self . fullscreen = False
2008-01-09 23:46:17 +01:00
self . minimized = False
self . gPodder . connect ( ' window-state-event ' , self . window_state_event )
2008-04-15 20:59:22 +02:00
self . show_hide_tray_icon ( )
2008-02-06 10:29:56 +01:00
2009-08-24 18:11:58 +02:00
self . itemShowToolbar . set_active ( self . config . show_toolbar )
self . itemShowDescription . set_active ( self . config . episode_list_descriptions )
2008-01-09 23:46:17 +01:00
2009-08-24 18:11:58 +02:00
self . config . connect_gtk_window ( self . gPodder , ' main_window ' )
self . config . connect_gtk_paned ( ' paned_position ' , self . channelPaned )
2007-11-02 17:37:14 +01:00
2009-08-24 18:11:58 +02:00
self . config . connect_gtk_spinbutton ( ' max_downloads ' , self . spinMaxDownloads )
self . config . connect_gtk_togglebutton ( ' max_downloads_enabled ' , self . cbMaxDownloads )
self . config . connect_gtk_spinbutton ( ' limit_rate_value ' , self . spinLimitDownloads )
self . config . connect_gtk_togglebutton ( ' limit_rate ' , self . cbLimitDownloads )
2008-04-17 17:45:29 +02:00
2009-04-01 01:12:17 +02:00
# Then the amount of maximum downloads changes, notify the queue manager
changed_cb = lambda spinbutton : self . download_queue_manager . spawn_and_retire_threads ( )
2008-04-17 17:45:29 +02:00
self . spinMaxDownloads . connect ( ' value-changed ' , changed_cb )
2008-04-06 02:19:03 +02:00
self . default_title = None
2009-05-07 16:26:07 +02:00
if gpodder . __version__ . rfind ( ' git ' ) != - 1 :
self . set_title ( ' gPodder %s ' % gpodder . __version__ )
2008-04-06 02:19:03 +02:00
else :
2008-08-11 18:00:10 +02:00
title = self . gPodder . get_title ( )
if title is not None :
self . set_title ( title )
else :
self . set_title ( _ ( ' gPodder ' ) )
2007-09-02 15:02:28 +02:00
Sat, 29 Mar 2008 17:13:26 +0100 <thp@perli.net>
Project management updates (authors, contributors and copyright)
* AUTHORS: Removed (was outdated); content now in gui.py (AboutDialog)
* bin/gpodder, data/po/Makefile, doc/dev/copyright_notice,
doc/dev/win32/setup-win32.py, INSTALL, Makefile, README,
setup.py: Updated Copyright and old website URL to include 2008, the
gPodder team and www.gpodder.org
* src/gpodder/*.py: Updated Copyright years
* src/gpodder/gui.py: Add list of contributors from AUTHORS file and
from the content on the website's news page (please mail me if I
forgot to mention you as a contributor, I surely have missed a few);
make the AboutDialog's application name "gPodder" (from gpodder) and
add an URL hook function to the AboutDialog, so the website is opened
in the user's default web browser
git-svn-id: svn://svn.berlios.de/gpodder/trunk@648 b0d088ad-0a06-0410-aad2-9ed5178a7e87
2008-03-29 17:16:55 +01:00
gtk . about_dialog_set_url_hook ( lambda dlg , link , data : util . open_website ( link ) , None )
2007-03-07 15:53:05 +01:00
2009-08-13 22:17:41 +02:00
# Set up podcast channel tree view widget
self . treeChannels . set_enable_search ( True )
self . treeChannels . set_search_column ( PodcastListModel . C_TITLE )
self . treeChannels . set_headers_visible ( False )
2007-07-05 23:07:16 +02:00
2009-08-13 22:17:41 +02:00
iconcolumn = gtk . TreeViewColumn ( ' ' )
2007-07-05 23:07:16 +02:00
iconcell = gtk . CellRendererPixbuf ( )
2009-08-13 22:17:41 +02:00
iconcolumn . pack_start ( iconcell , False )
iconcolumn . add_attribute ( iconcell , ' pixbuf ' , PodcastListModel . C_COVER )
self . treeChannels . append_column ( iconcolumn )
2007-07-05 23:07:16 +02:00
2008-11-19 19:29:55 +01:00
namecolumn = gtk . TreeViewColumn ( ' ' )
2007-07-05 23:07:16 +02:00
namecell = gtk . CellRendererText ( )
namecell . set_property ( ' ellipsize ' , pango . ELLIPSIZE_END )
2009-08-13 22:17:41 +02:00
namecolumn . pack_start ( namecell , True )
namecolumn . add_attribute ( namecell , ' markup ' , PodcastListModel . C_DESCRIPTION )
2007-07-05 23:07:16 +02:00
2007-11-27 23:04:15 +01:00
iconcell = gtk . CellRendererPixbuf ( )
2008-06-19 09:47:03 +02:00
iconcell . set_property ( ' xalign ' , 1.0 )
2009-08-13 22:17:41 +02:00
namecolumn . pack_start ( iconcell , False )
namecolumn . add_attribute ( iconcell , ' pixbuf ' , PodcastListModel . C_PILL )
namecolumn . add_attribute ( iconcell , ' visible ' , PodcastListModel . C_PILL_VISIBLE )
2008-11-19 19:29:55 +01:00
self . treeChannels . append_column ( namecolumn )
2009-08-13 22:17:41 +02:00
2009-08-24 16:47:59 +02:00
self . cover_downloader = CoverDownloader ( )
2009-08-13 23:19:12 +02:00
# Generate list models for podcasts and their episodes
2009-08-24 18:11:58 +02:00
self . podcast_list_model = PodcastListModel ( self . config . podcast_list_icon_size , self . cover_downloader )
2009-08-13 22:08:47 +02:00
self . treeChannels . set_model ( self . podcast_list_model )
2005-11-21 19:21:25 +01:00
2009-08-13 23:19:12 +02:00
self . episode_list_model = EpisodeListModel ( )
self . treeAvailable . set_model ( self . episode_list_model )
2007-04-03 08:27:46 +02:00
# enable alternating colors hint
self . treeAvailable . set_rules_hint ( True )
2007-09-19 17:04:42 +02:00
self . treeChannels . set_rules_hint ( True )
2007-04-03 08:27:46 +02:00
2007-11-14 21:57:31 +01:00
# connect to tooltip signals
2007-11-17 18:43:23 +01:00
try :
self . treeChannels . set_property ( ' has-tooltip ' , True )
self . treeChannels . connect ( ' query-tooltip ' , self . treeview_channels_query_tooltip )
2008-06-15 14:28:24 +02:00
self . treeAvailable . set_property ( ' has-tooltip ' , True )
self . treeAvailable . connect ( ' query-tooltip ' , self . treeview_episodes_query_tooltip )
2007-11-17 18:43:23 +01:00
except :
2008-06-15 14:28:24 +02:00
log ( ' I cannot set has-tooltip/query-tooltip (need at least PyGTK 2.12) ' , sender = self )
2007-11-14 21:57:31 +01:00
self . last_tooltip_channel = None
2008-06-15 14:28:24 +02:00
self . last_tooltip_episode = None
self . podcast_list_can_tooltip = True
self . episode_list_can_tooltip = True
2007-11-14 21:57:31 +01:00
2009-01-19 19:00:24 +01:00
self . currently_updating = False
2007-08-26 17:20:46 +02:00
# Add our context menu to treeAvailable
2008-04-06 02:19:03 +02:00
if gpodder . interface == gpodder . MAEMO :
2009-01-19 19:00:24 +01:00
self . treeview_available_buttonpress = ( 0 , 0 )
self . treeAvailable . connect ( ' button-press-event ' , self . treeview_button_savepos )
2008-04-06 02:19:03 +02:00
self . treeAvailable . connect ( ' button-release-event ' , self . treeview_button_pressed )
2009-01-19 19:00:24 +01:00
self . treeview_channels_buttonpress = ( 0 , 0 )
self . treeChannels . connect ( ' button-press-event ' , self . treeview_channels_button_pressed )
self . treeChannels . connect ( ' button-release-event ' , self . treeview_channels_button_released )
2008-04-06 02:19:03 +02:00
else :
self . treeAvailable . connect ( ' button-press-event ' , self . treeview_button_pressed )
2009-01-19 19:00:24 +01:00
self . treeChannels . connect ( ' button-press-event ' , self . treeview_channels_button_pressed )
2007-08-26 17:20:46 +02:00
2009-04-01 01:12:17 +02:00
self . treeDownloads . connect ( ' button-press-event ' , self . treeview_downloads_button_pressed )
2007-04-03 08:27:46 +02:00
iconcell = gtk . CellRendererPixbuf ( )
2008-04-06 02:19:03 +02:00
if gpodder . interface == gpodder . MAEMO :
2009-01-19 19:00:24 +01:00
iconcell . set_fixed_size ( - 1 , 52 )
2008-04-06 02:19:03 +02:00
status_column_label = ' '
else :
status_column_label = _ ( ' Status ' )
2009-08-13 23:19:12 +02:00
iconcolumn = gtk . TreeViewColumn ( status_column_label , iconcell , pixbuf = EpisodeListModel . C_STATUS_ICON )
2007-04-03 13:21:12 +02:00
2005-11-22 14:30:28 +01:00
namecell = gtk . CellRendererText ( )
2008-02-06 10:29:56 +01:00
namecell . set_property ( ' ellipsize ' , pango . ELLIPSIZE_END )
2009-08-13 23:19:12 +02:00
namecolumn = gtk . TreeViewColumn ( _ ( ' Episode ' ) , namecell , markup = EpisodeListModel . C_DESCRIPTION )
2008-02-06 10:29:56 +01:00
namecolumn . set_sizing ( gtk . TREE_VIEW_COLUMN_AUTOSIZE )
2009-03-22 14:52:59 +01:00
namecolumn . set_resizable ( True )
2008-02-06 10:29:56 +01:00
namecolumn . set_expand ( True )
2005-11-22 14:30:28 +01:00
sizecell = gtk . CellRendererText ( )
2009-08-13 23:19:12 +02:00
sizecolumn = gtk . TreeViewColumn ( _ ( ' Size ' ) , sizecell , text = EpisodeListModel . C_FILESIZE_TEXT )
2006-04-10 18:46:50 +02:00
releasecell = gtk . CellRendererText ( )
2009-08-13 23:19:12 +02:00
releasecolumn = gtk . TreeViewColumn ( _ ( ' Released ' ) , releasecell , text = EpisodeListModel . C_PUBLISHED_TEXT )
2005-11-22 14:30:28 +01:00
2008-02-06 10:29:56 +01:00
for itemcolumn in ( iconcolumn , namecolumn , sizecolumn , releasecolumn ) :
2009-01-19 19:00:24 +01:00
itemcolumn . set_reorderable ( gpodder . interface != gpodder . MAEMO )
2008-02-06 10:29:56 +01:00
self . treeAvailable . append_column ( itemcolumn )
2006-06-22 23:41:32 +02:00
2008-04-06 02:19:03 +02:00
if gpodder . interface == gpodder . MAEMO :
# Due to screen space contraints, we
# hide these columns here by default
self . column_size = sizecolumn
self . column_released = releasecolumn
self . column_released . set_visible ( False )
self . column_size . set_visible ( False )
2007-04-03 08:27:46 +02:00
# enable search in treeavailable
self . treeAvailable . set_search_equal_func ( self . treeAvailable_search_equal )
2009-05-30 13:28:16 +02:00
# on Maemo 5, we need to set hildon-ui-mode of TreeView widgets to 1
if gpodder . interface == gpodder . MAEMO :
HUIM = ' hildon-ui-mode '
if HUIM in [ p . name for p in gobject . list_properties ( gtk . TreeView ) ] :
for treeview_name in self . TREEVIEW_WIDGETS :
treeview = getattr ( self , treeview_name )
treeview . set_property ( HUIM , 1 )
2006-06-22 23:41:32 +02:00
# enable multiple selection support
2009-01-19 19:00:24 +01:00
if gpodder . interface == gpodder . MAEMO :
self . treeAvailable . get_selection ( ) . set_mode ( gtk . SELECTION_SINGLE )
else :
self . treeAvailable . get_selection ( ) . set_mode ( gtk . SELECTION_MULTIPLE )
2009-04-01 01:12:17 +02:00
self . treeDownloads . get_selection ( ) . set_mode ( gtk . SELECTION_MULTIPLE )
if hasattr ( self . treeDownloads , ' set_rubber_banding ' ) :
# Available in PyGTK 2.10 and above
self . treeDownloads . set_rubber_banding ( True )
2006-02-04 18:29:17 +01:00
2005-11-22 23:26:51 +01:00
# columns and renderers for "download progress" tab
2009-04-01 01:12:17 +02:00
# First column: [ICON] Episodename
column = gtk . TreeViewColumn ( _ ( ' Episode ' ) )
cell = gtk . CellRendererPixbuf ( )
2009-04-02 02:22:09 +02:00
if gpodder . interface == gpodder . MAEMO :
cell . set_property ( ' stock-size ' , gtk . ICON_SIZE_DIALOG )
else :
cell . set_property ( ' stock-size ' , gtk . ICON_SIZE_MENU )
2009-04-01 01:12:17 +02:00
column . pack_start ( cell , expand = False )
2009-04-01 13:34:19 +02:00
column . add_attribute ( cell , ' stock-id ' , \
2009-08-24 16:17:32 +02:00
DownloadStatusModel . C_ICON_NAME )
2009-04-01 01:12:17 +02:00
cell = gtk . CellRendererText ( )
cell . set_property ( ' ellipsize ' , pango . ELLIPSIZE_END )
column . pack_start ( cell , expand = True )
2009-08-24 16:17:32 +02:00
column . add_attribute ( cell , ' text ' , DownloadStatusModel . C_NAME )
2009-04-01 01:12:17 +02:00
column . set_sizing ( gtk . TREE_VIEW_COLUMN_AUTOSIZE )
column . set_resizable ( True )
column . set_expand ( True )
self . treeDownloads . append_column ( column )
# Second column: Progress
column = gtk . TreeViewColumn ( _ ( ' Progress ' ) , gtk . CellRendererProgress ( ) ,
2009-08-24 16:17:32 +02:00
value = DownloadStatusModel . C_PROGRESS , \
text = DownloadStatusModel . C_PROGRESS_TEXT )
2009-04-01 01:12:17 +02:00
self . treeDownloads . append_column ( column )
# Third column: Size
2009-04-02 02:22:09 +02:00
if gpodder . interface != gpodder . MAEMO :
column = gtk . TreeViewColumn ( _ ( ' Size ' ) , gtk . CellRendererText ( ) ,
2009-08-24 16:17:32 +02:00
text = DownloadStatusModel . C_SIZE_TEXT )
2009-04-02 02:22:09 +02:00
self . treeDownloads . append_column ( column )
2009-04-01 01:12:17 +02:00
# Fourth column: Speed
column = gtk . TreeViewColumn ( _ ( ' Speed ' ) , gtk . CellRendererText ( ) ,
2009-08-24 16:17:32 +02:00
text = DownloadStatusModel . C_SPEED_TEXT )
2009-04-01 01:12:17 +02:00
self . treeDownloads . append_column ( column )
# Fifth column: Status
column = gtk . TreeViewColumn ( _ ( ' Status ' ) , gtk . CellRendererText ( ) ,
2009-08-24 16:17:32 +02:00
text = DownloadStatusModel . C_STATUS_TEXT )
2009-04-01 01:12:17 +02:00
self . treeDownloads . append_column ( column )
2007-08-24 16:49:41 +02:00
2008-05-10 13:35:06 +02:00
# After we've set up most of the window, show it :)
if not gpodder . interface == gpodder . MAEMO :
self . gPodder . show ( )
2009-08-24 18:11:58 +02:00
if self . config . start_iconified :
2009-05-02 07:22:52 +02:00
self . iconify_main_window ( )
2009-08-24 18:11:58 +02:00
if self . tray_icon and self . config . minimize_to_tray :
2008-06-08 20:10:09 +02:00
self . tray_icon . set_visible ( False )
2009-08-24 16:47:59 +02:00
self . cover_downloader . register ( ' cover-available ' , self . cover_download_finished )
self . cover_downloader . register ( ' cover-removed ' , self . cover_file_removed )
2007-08-24 16:49:41 +02:00
2009-08-24 16:17:32 +02:00
self . treeDownloads . set_model ( self . download_status_model )
2009-04-01 01:12:17 +02:00
self . download_tasks_seen = set ( )
2009-08-24 00:43:55 +02:00
self . download_list_update_enabled = False
2009-04-02 01:40:53 +02:00
self . last_download_count = 0
2005-11-22 23:26:51 +01:00
2005-11-22 14:23:50 +01:00
#Add Drag and Drop Support
2007-09-19 17:04:42 +02:00
flags = gtk . DEST_DEFAULT_ALL
targets = [ ( ' text/plain ' , 0 , 2 ) , ( ' STRING ' , 0 , 3 ) , ( ' TEXT ' , 0 , 4 ) ]
actions = gtk . gdk . ACTION_DEFAULT | gtk . gdk . ACTION_COPY
self . treeChannels . drag_dest_set ( flags , targets , actions )
self . treeChannels . connect ( ' drag_data_received ' , self . drag_data_received )
2006-04-04 00:27:57 +02:00
2007-03-10 16:57:56 +01:00
# Subscribed channels
self . active_channel = None
2009-08-24 18:11:58 +02:00
self . channels = PodcastChannel . load_from_db ( self . db , self . config . download_dir )
2008-12-13 13:29:45 +01:00
self . channel_list_changed = True
2008-07-11 19:13:04 +02:00
self . update_podcasts_tab ( )
2006-12-03 19:11:14 +01:00
2008-01-21 09:57:03 +01:00
# load list of user applications for audio playback
2008-03-20 11:24:26 +01:00
self . user_apps_reader = UserAppsReader ( [ ' audio ' , ' video ' ] )
Thread ( target = self . read_apps ) . start ( )
2006-12-09 03:04:32 +01:00
2007-11-08 20:11:57 +01:00
# Set the "Device" menu item for the first time
self . update_item_device ( )
2008-10-28 10:36:23 +01:00
# Last folder used for saving episodes
self . folder_for_saving_episodes = None
2007-05-25 12:06:43 +02:00
# Now, update the feed cache, when everything's in place
2009-01-19 19:00:24 +01:00
self . btnUpdateFeeds . show ( )
2008-07-14 18:28:34 +02:00
self . updating_feed_cache = False
2008-04-22 20:34:41 +02:00
self . feed_cache_update_cancelled = False
2009-08-24 18:11:58 +02:00
self . update_feed_cache ( force_update = self . config . update_on_startup )
2008-01-09 23:46:17 +01:00
2009-08-11 00:36:38 +02:00
# Look for partial file downloads
2009-08-24 18:11:58 +02:00
partial_files = glob . glob ( os . path . join ( self . config . download_dir , ' * ' , ' *.partial ' ) )
2009-02-09 23:26:47 +01:00
2009-04-01 01:12:17 +02:00
# Message area
self . message_area = None
2009-02-09 23:26:47 +01:00
resumable_episodes = [ ]
if len ( partial_files ) > 0 :
for f in partial_files :
2009-05-12 12:13:03 +02:00
correct_name = f [ : - len ( ' .partial ' ) ] # strip ".partial"
2009-02-09 23:26:47 +01:00
log ( ' Searching episode for file: %s ' , correct_name , sender = self )
found_episode = False
for c in self . channels :
for e in c . get_all_episodes ( ) :
2009-05-12 12:13:03 +02:00
if e . local_filename ( create = False , check_only = True ) == correct_name :
2009-02-09 23:26:47 +01:00
log ( ' Found episode: %s ' , e . title , sender = self )
resumable_episodes . append ( e )
found_episode = True
if found_episode :
break
if found_episode :
break
2009-05-12 12:13:03 +02:00
if not found_episode :
log ( ' Partial file without episode: %s ' , f , sender = self )
util . delete_file ( f )
2009-02-09 23:26:47 +01:00
if len ( resumable_episodes ) :
2009-04-01 01:12:17 +02:00
self . download_episode_list_paused ( resumable_episodes )
2009-08-24 16:50:06 +02:00
self . message_area = SimpleMessageArea ( _ ( ' There are unfinished downloads from your last session. \n Pick the ones you want to continue downloading. ' ) )
2009-04-01 01:12:17 +02:00
self . vboxDownloadStatusWidgets . pack_start ( self . message_area , expand = False )
self . vboxDownloadStatusWidgets . reorder_child ( self . message_area , 0 )
self . message_area . show_all ( )
self . wNotebook . set_current_page ( 1 )
2009-02-09 23:26:47 +01:00
2009-08-24 18:11:58 +02:00
self . clean_up_downloads ( delete_partial = False )
2009-02-09 23:26:47 +01:00
else :
2009-08-24 18:11:58 +02:00
self . clean_up_downloads ( delete_partial = True )
2009-02-06 15:54:28 +01:00
2008-01-09 23:46:17 +01:00
# Start the auto-update procedure
self . auto_update_procedure ( first_run = True )
2005-11-21 19:21:25 +01:00
2007-12-12 20:57:50 +01:00
# Delete old episodes if the user wishes to
2009-08-24 18:11:58 +02:00
if self . config . auto_remove_old_episodes :
2007-12-12 20:57:50 +01:00
old_episodes = self . get_old_episodes ( )
if len ( old_episodes ) > 0 :
self . delete_episode_list ( old_episodes , confirm = False )
self . updateComboBox ( )
2008-05-02 17:36:43 +02:00
# First-time users should be asked if they want to see the OPML
if len ( self . channels ) == 0 :
2009-06-11 18:01:01 +02:00
util . idle_add ( self . on_itemUpdate_activate )
2008-05-02 17:36:43 +02:00
2009-08-17 21:46:17 +02:00
def enable_download_list_update ( self ) :
if not self . download_list_update_enabled :
gobject . timeout_add ( 1500 , self . update_downloads_list )
self . download_list_update_enabled = True
2009-05-26 13:47:47 +02:00
def on_btnCleanUpDownloads_clicked ( self , button ) :
2009-08-24 16:17:32 +02:00
model = self . download_status_model
2009-05-26 13:47:47 +02:00
all_tasks = [ ( gtk . TreeRowReference ( model , row . path ) , row [ 0 ] ) for row in model ]
changed_episode_urls = [ ]
for row_reference , task in all_tasks :
if task . status in ( task . DONE , task . CANCELLED , task . FAILED ) :
model . remove ( model . get_iter ( row_reference . get_path ( ) ) )
try :
# We don't "see" this task anymore - remove it;
# this is needed, so update_episode_list_icons()
# below gets the correct list of "seen" tasks
self . download_tasks_seen . remove ( task )
except KeyError , key_error :
log ( ' Cannot remove task from " seen " list: %s ' , task , sender = self )
changed_episode_urls . append ( task . url )
# Tell the task that it has been removed (so it can clean up)
task . removed_from_list ( )
# Tell the podcasts tab to update icons for our removed podcasts
self . update_episode_list_icons ( changed_episode_urls )
2009-08-24 00:43:55 +02:00
# Update the tab title and downloads list
self . update_downloads_list ( )
2009-05-26 13:47:47 +02:00
2009-05-11 23:10:56 +02:00
def on_tool_downloads_toggled ( self , toolbutton ) :
if toolbutton . get_active ( ) :
self . wNotebook . set_current_page ( 1 )
else :
self . wNotebook . set_current_page ( 0 )
2009-04-01 01:12:17 +02:00
def update_downloads_list ( self ) :
2009-06-05 11:46:20 +02:00
try :
2009-08-24 16:17:32 +02:00
model = self . download_status_model
2009-04-01 01:12:17 +02:00
2009-06-05 11:46:20 +02:00
downloading , failed , finished , queued , others = 0 , 0 , 0 , 0 , 0
total_speed , total_size , done_size = 0 , 0 , 0
2009-04-01 01:12:17 +02:00
2009-06-05 11:46:20 +02:00
# Keep a list of all download tasks that we've seen
download_tasks_seen = set ( )
2009-04-01 01:12:17 +02:00
2009-06-05 11:46:20 +02:00
# Remember the progress and speed for the episode that
# has been opened in the episode shownotes dialog (if any)
2009-08-24 19:43:49 +02:00
if self . episode_shownotes_window is not None :
episode_window_episode = self . episode_shownotes_window . episode
2009-06-05 11:46:20 +02:00
episode_window_progress = 0.0
episode_window_speed = 0.0
else :
episode_window_episode = None
2009-04-01 18:46:19 +02:00
2009-06-05 11:46:20 +02:00
# Do not go through the list of the model is not (yet) available
if model is None :
model = ( )
2009-05-30 12:34:36 +02:00
2009-06-05 11:46:20 +02:00
for row in model :
2009-08-24 16:17:32 +02:00
self . download_status_model . request_update ( row . iter )
2009-04-01 01:12:17 +02:00
2009-08-24 16:17:32 +02:00
task = row [ self . download_status_model . C_TASK ]
2009-06-05 11:46:20 +02:00
speed , size , status , progress = task . speed , task . total_size , task . status , task . progress
2009-04-01 01:12:17 +02:00
2009-06-05 11:46:20 +02:00
total_size + = size
done_size + = size * progress
2009-04-01 01:12:17 +02:00
2009-06-05 11:46:20 +02:00
if episode_window_episode is not None and \
episode_window_episode . url == task . url :
episode_window_progress = progress
episode_window_speed = speed
2009-04-01 18:46:19 +02:00
2009-06-05 11:46:20 +02:00
download_tasks_seen . add ( task )
2009-04-01 01:12:17 +02:00
2009-06-05 11:46:20 +02:00
if status == download . DownloadTask . DOWNLOADING :
downloading + = 1
total_speed + = speed
elif status == download . DownloadTask . FAILED :
failed + = 1
elif status == download . DownloadTask . DONE :
finished + = 1
elif status == download . DownloadTask . QUEUED :
queued + = 1
else :
others + = 1
# Remember which tasks we have seen after this run
self . download_tasks_seen = download_tasks_seen
text = [ _ ( ' Downloads ' ) ]
if downloading + failed + finished + queued > 0 :
s = [ ]
if downloading > 0 :
s . append ( _ ( ' %d downloading ' ) % downloading )
if failed > 0 :
s . append ( _ ( ' %d failed ' ) % failed )
if finished > 0 :
s . append ( _ ( ' %d done ' ) % finished )
if queued > 0 :
s . append ( _ ( ' %d queued ' ) % queued )
text . append ( ' ( ' + ' , ' . join ( s ) + ' ) ' )
self . labelDownloads . set_text ( ' ' . join ( text ) )
2009-04-01 01:12:17 +02:00
2009-05-11 23:10:56 +02:00
if gpodder . interface == gpodder . MAEMO :
2009-06-05 11:46:20 +02:00
sum = downloading + failed + finished + queued + others
if sum :
self . tool_downloads . set_label ( _ ( ' Downloads ( %d ) ' ) % sum )
else :
self . tool_downloads . set_label ( _ ( ' Downloads ' ) )
title = [ self . default_title ]
# We have to update all episodes/channels for which the status has
# changed. Accessing task.status_changed has the side effect of
# re-setting the changed flag, so we need to get the "changed" list
# of tuples first and split it into two lists afterwards
changed = [ ( task . url , task . podcast_url ) for task in \
self . download_tasks_seen if task . status_changed ]
episode_urls = [ episode_url for episode_url , channel_url in changed ]
channel_urls = [ channel_url for episode_url , channel_url in changed ]
count = downloading + queued
if count > 0 :
if count == 1 :
title . append ( _ ( ' downloading one file ' ) )
elif count > 1 :
title . append ( _ ( ' downloading %d files ' ) % count )
if total_size > 0 :
percentage = 100.0 * done_size / total_size
else :
percentage = 0.0
2009-07-06 16:05:59 +02:00
total_speed = util . format_filesize ( total_speed )
2009-06-05 11:46:20 +02:00
title [ 1 ] + = ' ( %d %% , %s /s) ' % ( percentage , total_speed )
if self . tray_icon is not None :
# Update the tray icon status and progress bar
self . tray_icon . set_status ( self . tray_icon . STATUS_DOWNLOAD_IN_PROGRESS , title [ 1 ] )
self . tray_icon . draw_progress_bar ( percentage / 100. )
elif self . last_download_count > 0 :
if self . tray_icon is not None :
# Update the tray icon status
self . tray_icon . set_status ( )
self . tray_icon . downloads_finished ( self . download_tasks_seen )
if gpodder . interface == gpodder . MAEMO :
hildon . hildon_banner_show_information ( self . gPodder , None , ' gPodder: %s ' % _ ( ' All downloads finished ' ) )
log ( ' All downloads have finished. ' , sender = self )
2009-08-24 18:11:58 +02:00
if self . config . cmd_all_downloads_complete :
util . run_external_command ( self . config . cmd_all_downloads_complete )
2009-06-05 11:46:20 +02:00
self . last_download_count = count
self . gPodder . set_title ( ' - ' . join ( title ) )
self . update_episode_list_icons ( episode_urls )
2009-08-24 19:43:49 +02:00
if self . episode_shownotes_window is not None and \
self . episode_shownotes_window . gPodderShownotes . get_property ( ' visible ' ) :
self . episode_shownotes_window . download_status_changed ( episode_urls )
self . episode_shownotes_window . download_status_progress ( episode_window_progress , episode_window_speed )
2009-06-05 11:46:20 +02:00
self . play_or_download ( )
if channel_urls :
self . updateComboBox ( only_these_urls = channel_urls )
2009-08-17 21:46:17 +02:00
if not self . download_queue_manager . are_queued_or_active_tasks ( ) :
self . download_list_update_enabled = False
2009-08-24 00:43:55 +02:00
return self . download_list_update_enabled
2009-06-05 11:46:20 +02:00
except Exception , e :
log ( ' Exception happened while updating download list. ' , sender = self , traceback = True )
self . show_message ( ' %s \n \n %s ' % ( _ ( ' Please report this problem and restart gPodder: ' ) , str ( e ) ) , _ ( ' Unhandled exception ' ) )
# We return False here, so the update loop won't be called again,
# that's why we require the restart of gPodder in the message.
return False
2009-04-01 01:12:17 +02:00
2008-05-02 17:36:43 +02:00
def entry_add_channel_focus ( self , widget , event ) :
widget . modify_text ( gtk . STATE_NORMAL , self . default_entry_text_color )
if widget . get_text ( ) == self . ENTER_URL_TEXT :
widget . set_text ( ' ' )
def entry_add_channel_unfocus ( self , widget , event ) :
if widget . get_text ( ) == ' ' :
widget . set_text ( self . ENTER_URL_TEXT )
widget . modify_text ( gtk . STATE_NORMAL , gtk . gdk . color_parse ( ' #aaaaaa ' ) )
def on_config_changed ( self , name , old_value , new_value ) :
2009-01-19 19:00:24 +01:00
if name == ' show_toolbar ' and gpodder . interface != gpodder . MAEMO :
2008-05-02 17:36:43 +02:00
if new_value :
2008-12-12 14:59:16 +01:00
self . toolbar . show ( )
2008-05-02 17:36:43 +02:00
else :
2008-12-12 14:59:16 +01:00
self . toolbar . hide ( )
2009-01-19 19:00:24 +01:00
elif name == ' episode_list_descriptions ' and gpodder . interface != gpodder . MAEMO :
2008-05-02 17:36:43 +02:00
self . updateTreeView ( )
2008-11-06 16:06:13 +01:00
elif name == ' show_url_entry_in_podcast_list ' :
2008-05-02 17:36:43 +02:00
if new_value :
2008-11-06 16:06:13 +01:00
self . hboxAddChannel . show ( )
2008-05-02 17:36:43 +02:00
else :
2008-11-06 16:06:13 +01:00
self . hboxAddChannel . hide ( )
2008-04-24 19:42:57 +02:00
2008-03-20 11:24:26 +01:00
def read_apps ( self ) :
time . sleep ( 3 ) # give other parts of gpodder a chance to start up
self . user_apps_reader . read ( )
util . idle_add ( self . user_apps_reader . get_applications_as_model , ' audio ' , False )
util . idle_add ( self . user_apps_reader . get_applications_as_model , ' video ' , False )
2007-12-12 20:57:50 +01:00
2008-06-15 14:28:24 +02:00
def treeview_episodes_query_tooltip ( self , treeview , x , y , keyboard_tooltip , tooltip ) :
# With get_bin_window, we get the window that contains the rows without
# the header. The Y coordinate of this window will be the height of the
# treeview header. This is the amount we have to subtract from the
# event's Y coordinate to get the coordinate to pass to get_path_at_pos
( x_bin , y_bin ) = treeview . get_bin_window ( ) . get_position ( )
y - = x_bin
y - = y_bin
( path , column , rx , ry ) = treeview . get_path_at_pos ( x , y ) or ( None , ) * 4
2008-11-19 19:29:55 +01:00
if not self . episode_list_can_tooltip or ( column is not None and column != treeview . get_columns ( ) [ 0 ] ) :
2008-06-15 14:28:24 +02:00
self . last_tooltip_episode = None
return False
if path is not None :
model = treeview . get_model ( )
iter = model . get_iter ( path )
2009-08-13 23:19:12 +02:00
url = model . get_value ( iter , EpisodeListModel . C_URL )
description = model . get_value ( iter , EpisodeListModel . C_DESCRIPTION_STRIPPED )
2008-06-15 14:28:24 +02:00
if self . last_tooltip_episode is not None and self . last_tooltip_episode != url :
self . last_tooltip_episode = None
return False
self . last_tooltip_episode = url
2008-11-19 19:29:55 +01:00
if len ( description ) > 400 :
description = description [ : 398 ] + ' [...] '
2008-06-15 14:28:24 +02:00
tooltip . set_text ( description )
return True
self . last_tooltip_episode = None
return False
def podcast_list_allow_tooltips ( self ) :
self . podcast_list_can_tooltip = True
def episode_list_allow_tooltips ( self ) :
self . episode_list_can_tooltip = True
2007-11-14 21:57:31 +01:00
def treeview_channels_query_tooltip ( self , treeview , x , y , keyboard_tooltip , tooltip ) :
2008-04-06 17:25:17 +02:00
( path , column , rx , ry ) = treeview . get_path_at_pos ( x , y ) or ( None , ) * 4
2007-11-14 21:57:31 +01:00
2008-11-19 19:29:55 +01:00
if not self . podcast_list_can_tooltip or ( column is not None and column != treeview . get_columns ( ) [ 0 ] ) :
2008-06-15 14:28:24 +02:00
self . last_tooltip_channel = None
return False
2007-11-14 21:57:31 +01:00
if path is not None :
model = treeview . get_model ( )
iter = model . get_iter ( path )
2009-08-13 23:19:12 +02:00
url = model . get_value ( iter , PodcastListModel . C_URL )
channel = model . get_value ( iter , PodcastListModel . C_CHANNEL )
2008-06-05 18:17:09 +02:00
2009-08-13 23:19:12 +02:00
if self . last_tooltip_channel is not None and self . last_tooltip_channel != channel :
self . last_tooltip_channel = None
return False
self . last_tooltip_channel = channel
channel . request_save_dir_size ( )
diskspace_str = util . format_filesize ( channel . save_dir_size , 0 )
error_str = model . get_value ( iter , PodcastListModel . C_ERROR )
if error_str :
error_str = _ ( ' Feedparser error: %s ' ) % saxutils . escape ( error_str . strip ( ) )
error_str = ' <span foreground= " #ff0000 " > %s </span> ' % error_str
table = gtk . Table ( rows = 3 , columns = 3 )
table . set_row_spacings ( 5 )
table . set_col_spacings ( 5 )
table . set_border_width ( 5 )
heading = gtk . Label ( )
heading . set_alignment ( 0 , 1 )
heading . set_markup ( ' <b><big> %s </big></b> \n <small> %s </small> ' % ( saxutils . escape ( channel . title ) , saxutils . escape ( channel . url ) ) )
table . attach ( heading , 0 , 1 , 0 , 1 )
size_info = gtk . Label ( )
size_info . set_alignment ( 1 , 1 )
size_info . set_justify ( gtk . JUSTIFY_RIGHT )
size_info . set_markup ( ' <b> %s </b> \n <small> %s </small> ' % ( diskspace_str , _ ( ' disk usage ' ) ) )
table . attach ( size_info , 2 , 3 , 0 , 1 )
table . attach ( gtk . HSeparator ( ) , 0 , 3 , 1 , 2 )
if len ( channel . description ) < 500 :
description = channel . description
else :
pos = channel . description . find ( ' \n \n ' )
if pos == - 1 or pos > 500 :
description = channel . description [ : 498 ] + ' [...] '
else :
description = channel . description [ : pos ]
2008-06-05 18:17:09 +02:00
2009-08-13 23:19:12 +02:00
description = gtk . Label ( description )
if error_str :
description . set_markup ( error_str )
description . set_alignment ( 0 , 0 )
description . set_line_wrap ( True )
table . attach ( description , 0 , 3 , 2 , 3 )
2008-05-14 15:38:06 +02:00
2009-08-13 23:19:12 +02:00
table . show_all ( )
tooltip . set_custom ( table )
return True
2007-11-14 21:57:31 +01:00
self . last_tooltip_channel = None
return False
2008-03-10 16:50:12 +01:00
def update_m3u_playlist_clicked ( self , widget ) :
self . active_channel . update_m3u_playlist ( )
self . show_message ( _ ( ' Updated M3U playlist in download folder. ' ) , _ ( ' Updated playlist ' ) )
2009-04-01 01:12:17 +02:00
def treeview_downloads_button_pressed ( self , treeview , event ) :
if event . button == 1 :
# Catch left mouse button presses, and if we there is no
# path at the given position, deselect all items
( x , y ) = ( int ( event . x ) , int ( event . y ) )
( path , column , rx , ry ) = treeview . get_path_at_pos ( x , y ) or ( None , ) * 4
if path is None :
treeview . get_selection ( ) . unselect_all ( )
2009-04-02 02:22:09 +02:00
# Use right-click for the Desktop version and left-click for Maemo
if ( event . button == 1 and gpodder . interface == gpodder . MAEMO ) or \
( event . button == 3 and gpodder . interface == gpodder . GUI ) :
2009-04-01 01:12:17 +02:00
( x , y ) = ( int ( event . x ) , int ( event . y ) )
( path , column , rx , ry ) = treeview . get_path_at_pos ( x , y ) or ( None , ) * 4
paths = [ ]
# Did the user right-click into a selection?
selection = treeview . get_selection ( )
if selection . count_selected_rows ( ) and path :
( model , paths ) = selection . get_selected_rows ( )
if path not in paths :
# We have right-clicked, but not into the
# selection, assume we don't want to operate
# on the selection
paths = [ ]
# No selection or right click not in selection:
# Select the single item where we clicked
if not paths and path :
treeview . grab_focus ( )
treeview . set_cursor ( path , column , 0 )
( model , paths ) = ( treeview . get_model ( ) , [ path ] )
# We did not find a selection, and the user didn't
# click on an item to select -- don't show the menu
if not paths :
return True
selected_tasks = [ ( gtk . TreeRowReference ( model , path ) , model . get_value ( model . get_iter ( path ) , 0 ) ) for path in paths ]
def make_menu_item ( label , stock_id , tasks , status ) :
# This creates a menu item for selection-wide actions
def for_each_task_set_status ( tasks , status ) :
2009-04-02 00:02:07 +02:00
changed_episode_urls = [ ]
2009-04-01 01:12:17 +02:00
for row_reference , task in tasks :
if status is not None :
if status == download . DownloadTask . QUEUED :
# Only queue task when its paused/failed/cancelled
if task . status in ( task . PAUSED , task . FAILED , task . CANCELLED ) :
self . download_queue_manager . add_task ( task )
2009-08-17 21:46:17 +02:00
self . enable_download_list_update ( )
2009-04-01 01:12:17 +02:00
elif status == download . DownloadTask . CANCELLED :
# Cancelling a download only allows when paused/downloading/queued
if task . status in ( task . QUEUED , task . DOWNLOADING , task . PAUSED ) :
task . status = status
elif status == download . DownloadTask . PAUSED :
# Pausing a download only when queued/downloading
if task . status in ( task . DOWNLOADING , task . QUEUED ) :
task . status = status
else :
# We (hopefully) can simply set the task status here
task . status = status
else :
# Remove the selected task - cancel downloading/queued tasks
if task . status in ( task . QUEUED , task . DOWNLOADING ) :
task . status = task . CANCELLED
model . remove ( model . get_iter ( row_reference . get_path ( ) ) )
2009-04-02 00:02:07 +02:00
# Remember the URL, so we can tell the UI to update
try :
# We don't "see" this task anymore - remove it;
# this is needed, so update_episode_list_icons()
# below gets the correct list of "seen" tasks
self . download_tasks_seen . remove ( task )
except KeyError , key_error :
log ( ' Cannot remove task from " seen " list: %s ' , task , sender = self )
changed_episode_urls . append ( task . url )
2009-04-01 01:12:17 +02:00
# Tell the task that it has been removed (so it can clean up)
task . removed_from_list ( )
2009-04-02 00:02:07 +02:00
# Tell the podcasts tab to update icons for our removed podcasts
self . update_episode_list_icons ( changed_episode_urls )
2009-08-24 00:43:55 +02:00
# Update the tab title and downloads list
self . update_downloads_list ( )
2009-04-01 01:12:17 +02:00
return True
item = gtk . ImageMenuItem ( label )
item . set_image ( gtk . image_new_from_stock ( stock_id , gtk . ICON_SIZE_MENU ) )
item . connect ( ' activate ' , lambda item : for_each_task_set_status ( tasks , status ) )
2009-04-02 00:21:08 +02:00
# Determine if we should disable this menu item
for row_reference , task in tasks :
if status == download . DownloadTask . QUEUED :
if task . status not in ( download . DownloadTask . PAUSED , \
download . DownloadTask . FAILED , \
download . DownloadTask . CANCELLED ) :
item . set_sensitive ( False )
break
elif status == download . DownloadTask . CANCELLED :
if task . status not in ( download . DownloadTask . PAUSED , \
download . DownloadTask . QUEUED , \
download . DownloadTask . DOWNLOADING ) :
item . set_sensitive ( False )
break
elif status == download . DownloadTask . PAUSED :
if task . status not in ( download . DownloadTask . QUEUED , \
download . DownloadTask . DOWNLOADING ) :
item . set_sensitive ( False )
2009-04-02 11:13:00 +02:00
break
elif status is None :
if task . status not in ( download . DownloadTask . CANCELLED , \
download . DownloadTask . FAILED , \
download . DownloadTask . DONE ) :
item . set_sensitive ( False )
2009-04-02 00:21:08 +02:00
break
2009-04-02 02:22:09 +02:00
return self . set_finger_friendly ( item )
2009-04-01 01:12:17 +02:00
menu = gtk . Menu ( )
2009-05-12 10:36:01 +02:00
item = gtk . ImageMenuItem ( _ ( ' Episode details ' ) )
item . set_image ( gtk . image_new_from_stock ( gtk . STOCK_INFO , gtk . ICON_SIZE_MENU ) )
if len ( selected_tasks ) == 1 :
row_reference , task = selected_tasks [ 0 ]
episode = task . episode
item . connect ( ' activate ' , lambda item : self . show_episode_shownotes ( episode ) )
else :
item . set_sensitive ( False )
menu . append ( item )
2009-04-01 01:12:17 +02:00
menu . append ( gtk . SeparatorMenuItem ( ) )
2009-05-12 10:36:01 +02:00
menu . append ( make_menu_item ( _ ( ' Download ' ) , gtk . STOCK_GO_DOWN , selected_tasks , download . DownloadTask . QUEUED ) )
2009-04-01 01:12:17 +02:00
menu . append ( make_menu_item ( _ ( ' Cancel ' ) , gtk . STOCK_CANCEL , selected_tasks , download . DownloadTask . CANCELLED ) )
menu . append ( make_menu_item ( _ ( ' Pause ' ) , gtk . STOCK_MEDIA_PAUSE , selected_tasks , download . DownloadTask . PAUSED ) )
menu . append ( gtk . SeparatorMenuItem ( ) )
menu . append ( make_menu_item ( _ ( ' Remove from list ' ) , gtk . STOCK_REMOVE , selected_tasks , None ) )
2009-04-02 02:22:09 +02:00
if gpodder . interface == gpodder . MAEMO :
# Because we open the popup on left-click for Maemo,
# we also include a non-action to close the menu
menu . append ( gtk . SeparatorMenuItem ( ) )
item = gtk . ImageMenuItem ( _ ( ' Close this menu ' ) )
item . set_image ( gtk . image_new_from_stock ( gtk . STOCK_CLOSE , gtk . ICON_SIZE_MENU ) )
menu . append ( self . set_finger_friendly ( item ) )
2009-04-01 01:12:17 +02:00
menu . show_all ( )
menu . popup ( None , None , None , event . button , event . time )
return True
2007-09-15 16:29:37 +02:00
def treeview_channels_button_pressed ( self , treeview , event ) :
2008-04-06 02:19:03 +02:00
global WEB_BROWSER_ICON
2009-01-19 19:00:24 +01:00
if gpodder . interface == gpodder . MAEMO :
self . treeview_channels_buttonpress = ( event . x , event . y )
return True
2007-09-15 16:29:37 +02:00
if event . button == 3 :
( x , y ) = ( int ( event . x ) , int ( event . y ) )
( path , column , rx , ry ) = treeview . get_path_at_pos ( x , y ) or ( None , ) * 4
paths = [ ]
# Did the user right-click into a selection?
selection = treeview . get_selection ( )
if selection . count_selected_rows ( ) and path :
( model , paths ) = selection . get_selected_rows ( )
if path not in paths :
# We have right-clicked, but not into the
# selection, assume we don't want to operate
# on the selection
paths = [ ]
# No selection or right click not in selection:
# Select the single item where we clicked
if not len ( paths ) and path :
treeview . grab_focus ( )
treeview . set_cursor ( path , column , 0 )
( model , paths ) = ( treeview . get_model ( ) , [ path ] )
# We did not find a selection, and the user didn't
# click on an item to select -- don't show the menu
if not len ( paths ) :
return True
menu = gtk . Menu ( )
2007-11-02 07:53:30 +01:00
item = gtk . ImageMenuItem ( _ ( ' Open download folder ' ) )
item . set_image ( gtk . image_new_from_icon_name ( ' folder-open ' , gtk . ICON_SIZE_MENU ) )
2008-03-02 14:04:37 +01:00
item . connect ( ' activate ' , lambda x : util . gui_open ( self . active_channel . save_dir ) )
2007-11-02 07:53:30 +01:00
menu . append ( item )
2008-08-30 19:23:04 +02:00
item = gtk . ImageMenuItem ( _ ( ' Update Feed ' ) )
item . set_image ( gtk . image_new_from_icon_name ( ' gtk-refresh ' , gtk . ICON_SIZE_MENU ) )
item . connect ( ' activate ' , self . on_itemUpdateChannel_activate )
item . set_sensitive ( not self . updating_feed_cache )
menu . append ( item )
2009-07-06 16:14:36 +02:00
item = gtk . ImageMenuItem ( _ ( ' Update M3U playlist ' ) )
item . set_image ( gtk . image_new_from_stock ( gtk . STOCK_REFRESH , gtk . ICON_SIZE_MENU ) )
item . connect ( ' activate ' , self . update_m3u_playlist_clicked )
menu . append ( item )
2008-03-10 16:50:12 +01:00
2008-03-22 20:43:20 +01:00
if self . active_channel . link :
item = gtk . ImageMenuItem ( _ ( ' Visit website ' ) )
2008-04-06 02:19:03 +02:00
item . set_image ( gtk . image_new_from_icon_name ( WEB_BROWSER_ICON , gtk . ICON_SIZE_MENU ) )
2008-03-22 20:43:20 +01:00
item . connect ( ' activate ' , lambda w : util . open_website ( self . active_channel . link ) )
menu . append ( item )
2008-11-19 17:05:19 +01:00
if self . active_channel . channel_is_locked :
item = gtk . ImageMenuItem ( _ ( ' Allow deletion of all episodes ' ) )
item . set_image ( gtk . image_new_from_stock ( gtk . STOCK_DIALOG_AUTHENTICATION , gtk . ICON_SIZE_MENU ) )
item . connect ( ' activate ' , self . on_channel_toggle_lock_activate )
menu . append ( self . set_finger_friendly ( item ) )
else :
item = gtk . ImageMenuItem ( _ ( ' Prohibit deletion of all episodes ' ) )
item . set_image ( gtk . image_new_from_stock ( gtk . STOCK_DIALOG_AUTHENTICATION , gtk . ICON_SIZE_MENU ) )
item . connect ( ' activate ' , self . on_channel_toggle_lock_activate )
menu . append ( self . set_finger_friendly ( item ) )
2007-11-02 07:53:30 +01:00
menu . append ( gtk . SeparatorMenuItem ( ) )
2008-03-22 20:43:20 +01:00
item = gtk . ImageMenuItem ( gtk . STOCK_EDIT )
2007-09-15 16:29:37 +02:00
item . connect ( ' activate ' , self . on_itemEditChannel_activate )
menu . append ( item )
2008-03-22 20:43:20 +01:00
item = gtk . ImageMenuItem ( gtk . STOCK_DELETE )
2007-09-15 16:29:37 +02:00
item . connect ( ' activate ' , self . on_itemRemoveChannel_activate )
menu . append ( item )
menu . show_all ( )
2008-06-15 14:28:24 +02:00
# Disable tooltips while we are showing the menu, so
# the tooltip will not appear over the menu
self . podcast_list_can_tooltip = False
menu . connect ( ' deactivate ' , lambda menushell : self . podcast_list_allow_tooltips ( ) )
2007-09-15 16:29:37 +02:00
menu . popup ( None , None , None , event . button , event . time )
return True
2008-04-11 10:29:13 +02:00
def on_itemClose_activate ( self , widget ) :
if self . tray_icon is not None :
2008-05-02 17:36:43 +02:00
if gpodder . interface == gpodder . MAEMO :
self . gPodder . set_property ( ' visible ' , False )
else :
self . iconify_main_window ( )
2008-04-11 10:29:13 +02:00
else :
self . on_gPodder_delete_event ( widget )
2008-06-14 13:43:53 +02:00
def cover_file_removed ( self , channel_url ) :
"""
The Cover Downloader calls this when a previously -
available cover has been removed from the disk . We
2009-08-13 22:08:47 +02:00
have to update our model to reflect this change .
2008-06-14 13:43:53 +02:00
"""
2009-08-13 22:08:47 +02:00
self . podcast_list_model . delete_cover_by_url ( channel_url )
2008-06-14 13:43:53 +02:00
def cover_download_finished ( self , channel_url , pixbuf ) :
"""
The Cover Downloader calls this when it has finished
downloading ( or registering , if already downloaded )
a new channel cover , which is ready for displaying .
"""
2009-08-13 22:08:47 +02:00
self . podcast_list_model . add_cover_by_url ( channel_url , pixbuf )
2008-06-14 13:43:53 +02:00
2009-08-13 23:19:12 +02:00
def save_episode_as_file ( self , episode ) :
2009-02-09 23:26:47 +01:00
if episode . was_downloaded ( and_exists = True ) :
folder = self . folder_for_saving_episodes
copy_from = episode . local_filename ( create = False )
assert copy_from is not None
2009-08-24 18:11:58 +02:00
copy_to = episode . sync_filename ( self . config . custom_sync_name_enabled , self . config . custom_sync_name )
2009-08-13 20:39:00 +02:00
( result , folder ) = self . show_copy_dialog ( src_filename = copy_from , dst_filename = copy_to , dst_directory = folder )
2009-02-09 23:26:47 +01:00
self . folder_for_saving_episodes = folder
2007-10-28 15:30:11 +01:00
2009-08-10 23:14:35 +02:00
def copy_episodes_bluetooth ( self , episodes ) :
episodes_to_copy = [ e for e in episodes if e . was_downloaded ( and_exists = True ) ]
2008-01-21 10:52:09 +01:00
2009-08-10 23:14:35 +02:00
def convert_and_send_thread ( episodes , notify ) :
for episode in episodes :
filename = episode . local_filename ( create = False )
assert filename is not None
destfile = os . path . join ( tempfile . gettempdir ( ) , \
2009-08-24 18:11:58 +02:00
util . sanitize_filename ( episode . sync_filename ( self . config . custom_sync_name_enabled , self . config . custom_sync_name ) ) )
2009-08-10 23:14:35 +02:00
( base , ext ) = os . path . splitext ( filename )
if not destfile . endswith ( ext ) :
destfile + = ext
2008-01-21 10:52:09 +01:00
2009-08-10 23:14:35 +02:00
try :
shutil . copyfile ( filename , destfile )
util . bluetooth_send_file ( destfile )
except :
log ( ' Cannot copy " %s " to " %s " . ' , filename , destfile , sender = self )
notify ( _ ( ' Error converting file. ' ) , _ ( ' Bluetooth file transfer ' ) )
2009-07-06 15:21:36 +02:00
2009-08-10 23:14:35 +02:00
util . delete_file ( destfile )
2008-02-27 09:42:38 +01:00
2009-08-10 23:14:35 +02:00
Thread ( target = convert_and_send_thread , args = [ episodes_to_copy , self . notification ] ) . start ( )
2008-01-21 10:52:09 +01:00
2009-01-19 19:00:24 +01:00
def treeview_button_savepos ( self , treeview , event ) :
if gpodder . interface == gpodder . MAEMO and event . button == 1 :
self . treeview_available_buttonpress = ( event . x , event . y )
return True
def treeview_channels_button_released ( self , treeview , event ) :
if gpodder . interface == gpodder . MAEMO and event . button == 1 :
selection = self . treeChannels . get_selection ( )
pathatpos = self . treeChannels . get_path_at_pos ( int ( event . x ) , int ( event . y ) )
if self . currently_updating :
log ( ' do not handle press while updating ' , sender = self )
return True
if pathatpos is None :
return False
else :
ydistance = int ( abs ( event . y - self . treeview_channels_buttonpress [ 1 ] ) )
xdistance = int ( event . x - self . treeview_channels_buttonpress [ 0 ] )
if ydistance < 30 :
( path , column , x , y ) = pathatpos
selection . select_path ( path )
self . treeChannels . set_cursor ( path )
self . treeChannels . grab_focus ( )
2009-05-30 12:48:56 +02:00
# Emulate the cursor changed signal to force an update
self . on_treeChannels_cursor_changed ( self . treeChannels )
2009-01-19 19:00:24 +01:00
return True
2009-08-10 18:32:08 +02:00
def get_device_name ( self ) :
2009-08-24 18:11:58 +02:00
if self . config . device_type == ' ipod ' :
2009-08-10 18:32:08 +02:00
return _ ( ' iPod ' )
2009-08-24 18:11:58 +02:00
elif self . config . device_type in ( ' filesystem ' , ' mtp ' ) :
2009-08-10 18:32:08 +02:00
return _ ( ' MP3 player ' )
else :
return ' (unknown device) '
2007-08-26 17:20:46 +02:00
def treeview_button_pressed ( self , treeview , event ) :
2008-04-06 02:19:03 +02:00
global WEB_BROWSER_ICON
2009-01-19 19:00:24 +01:00
if gpodder . interface == gpodder . MAEMO :
ydistance = int ( abs ( event . y - self . treeview_available_buttonpress [ 1 ] ) )
xdistance = int ( event . x - self . treeview_available_buttonpress [ 0 ] )
selection = self . treeAvailable . get_selection ( )
pathatpos = self . treeAvailable . get_path_at_pos ( int ( event . x ) , int ( event . y ) )
if pathatpos is None :
# No item at the current cursor position
return False
elif ydistance < 30 :
# Item under the cursor, and no scrolling done
( path , column , x , y ) = pathatpos
selection . select_path ( path )
self . treeAvailable . set_cursor ( path )
self . treeAvailable . grab_focus ( )
2009-08-24 18:11:58 +02:00
if self . config . maemo_enable_gestures and xdistance > 70 :
2009-08-05 12:18:03 +02:00
self . on_playback_selected_episodes ( None )
2009-01-19 19:00:24 +01:00
return True
2009-08-24 18:11:58 +02:00
elif self . config . maemo_enable_gestures and xdistance < - 70 :
2009-08-05 12:18:03 +02:00
self . on_shownotes_selected_episodes ( None )
2009-01-19 19:00:24 +01:00
return True
else :
# Scrolling has been done
return True
2008-04-06 02:19:03 +02:00
# Use right-click for the Desktop version and left-click for Maemo
if ( event . button == 1 and gpodder . interface == gpodder . MAEMO ) or \
( event . button == 3 and gpodder . interface == gpodder . GUI ) :
2007-08-26 17:20:46 +02:00
( x , y ) = ( int ( event . x ) , int ( event . y ) )
( path , column , rx , ry ) = treeview . get_path_at_pos ( x , y ) or ( None , ) * 4
paths = [ ]
# Did the user right-click into a selection?
selection = self . treeAvailable . get_selection ( )
if selection . count_selected_rows ( ) and path :
( model , paths ) = selection . get_selected_rows ( )
if path not in paths :
# We have right-clicked, but not into the
# selection, assume we don't want to operate
# on the selection
paths = [ ]
# No selection or right click not in selection:
# Select the single item where we clicked
if not len ( paths ) and path :
treeview . grab_focus ( )
treeview . set_cursor ( path , column , 0 )
( model , paths ) = ( treeview . get_model ( ) , [ path ] )
# We did not find a selection, and the user didn't
# click on an item to select -- don't show the menu
if not len ( paths ) :
return True
2009-08-10 23:14:35 +02:00
episodes = self . get_selected_episodes ( )
any_locked = any ( e . is_locked for e in episodes )
any_played = any ( e . is_played for e in episodes )
one_is_new = any ( e . state == gpodder . STATE_NORMAL and not e . is_played for e in episodes )
2007-08-30 20:54:18 +02:00
2007-08-26 17:20:46 +02:00
menu = gtk . Menu ( )
2008-08-04 23:26:51 +02:00
( can_play , can_download , can_transfer , can_cancel , can_delete , open_instead_of_play ) = self . play_or_download ( )
2007-08-26 17:20:46 +02:00
2009-04-01 21:54:17 +02:00
if open_instead_of_play :
item = gtk . ImageMenuItem ( gtk . STOCK_OPEN )
else :
item = gtk . ImageMenuItem ( gtk . STOCK_MEDIA_PLAY )
item . set_sensitive ( can_play )
2009-07-13 14:09:43 +02:00
item . connect ( ' activate ' , self . on_playback_selected_episodes )
2009-04-01 21:54:17 +02:00
menu . append ( self . set_finger_friendly ( item ) )
2008-06-30 03:10:18 +02:00
2009-04-02 02:22:09 +02:00
if not can_cancel :
item = gtk . ImageMenuItem ( _ ( ' Download ' ) )
item . set_image ( gtk . image_new_from_stock ( gtk . STOCK_GO_DOWN , gtk . ICON_SIZE_MENU ) )
item . set_sensitive ( can_download )
2009-07-13 14:09:43 +02:00
item . connect ( ' activate ' , self . on_download_selected_episodes )
2009-04-02 02:22:09 +02:00
menu . append ( self . set_finger_friendly ( item ) )
else :
item = gtk . ImageMenuItem ( gtk . STOCK_CANCEL )
item . connect ( ' activate ' , lambda w : self . on_treeDownloads_row_activated ( self . toolCancel ) )
menu . append ( self . set_finger_friendly ( item ) )
2007-08-26 17:20:46 +02:00
2009-04-01 21:54:17 +02:00
item = gtk . ImageMenuItem ( gtk . STOCK_DELETE )
2009-08-10 23:14:35 +02:00
item . set_sensitive ( can_delete )
2009-04-01 21:54:17 +02:00
item . connect ( ' activate ' , self . on_btnDownloadedDelete_clicked )
menu . append ( self . set_finger_friendly ( item ) )
2009-08-10 23:14:35 +02:00
if one_is_new :
2008-06-30 03:10:18 +02:00
item = gtk . ImageMenuItem ( _ ( ' Do not download ' ) )
item . set_image ( gtk . image_new_from_stock ( gtk . STOCK_DELETE , gtk . ICON_SIZE_MENU ) )
item . connect ( ' activate ' , lambda w : self . mark_selected_episodes_old ( ) )
menu . append ( self . set_finger_friendly ( item ) )
2009-08-10 23:14:35 +02:00
elif can_download :
2008-06-30 03:10:18 +02:00
item = gtk . ImageMenuItem ( _ ( ' Mark as new ' ) )
item . set_image ( gtk . image_new_from_stock ( gtk . STOCK_ABOUT , gtk . ICON_SIZE_MENU ) )
item . connect ( ' activate ' , lambda w : self . mark_selected_episodes_new ( ) )
menu . append ( self . set_finger_friendly ( item ) )
2007-08-30 20:54:18 +02:00
2009-04-01 21:54:17 +02:00
# Ok, this probably makes sense to only display for downloaded files
2008-11-19 16:25:27 +01:00
if can_play and not can_download :
2008-03-19 18:07:59 +01:00
menu . append ( gtk . SeparatorMenuItem ( ) )
item = gtk . ImageMenuItem ( _ ( ' Save to disk ' ) )
item . set_image ( gtk . image_new_from_stock ( gtk . STOCK_SAVE_AS , gtk . ICON_SIZE_MENU ) )
2009-08-13 23:19:12 +02:00
item . connect ( ' activate ' , lambda w : [ self . save_episode_as_file ( e ) for e in episodes ] )
2008-04-24 18:28:39 +02:00
menu . append ( self . set_finger_friendly ( item ) )
2009-08-11 00:36:38 +02:00
if self . bluetooth_available :
2008-03-19 18:07:59 +01:00
item = gtk . ImageMenuItem ( _ ( ' Send via bluetooth ' ) )
item . set_image ( gtk . image_new_from_icon_name ( ' bluetooth ' , gtk . ICON_SIZE_MENU ) )
2009-08-10 23:14:35 +02:00
item . connect ( ' activate ' , lambda w : self . copy_episodes_bluetooth ( episodes ) )
2008-04-24 18:28:39 +02:00
menu . append ( self . set_finger_friendly ( item ) )
2008-03-19 18:07:59 +01:00
if can_transfer :
2009-08-10 18:32:08 +02:00
item = gtk . ImageMenuItem ( _ ( ' Transfer to %s ' ) % self . get_device_name ( ) )
2008-03-19 18:07:59 +01:00
item . set_image ( gtk . image_new_from_icon_name ( ' multimedia-player ' , gtk . ICON_SIZE_MENU ) )
2009-08-10 23:14:35 +02:00
item . connect ( ' activate ' , lambda w : self . on_sync_to_ipod_activate ( w , episodes ) )
2008-04-24 18:28:39 +02:00
menu . append ( self . set_finger_friendly ( item ) )
2007-08-26 17:20:46 +02:00
2007-08-30 20:54:18 +02:00
if can_play :
menu . append ( gtk . SeparatorMenuItem ( ) )
2009-08-10 23:14:35 +02:00
if any_played :
2008-03-19 18:07:59 +01:00
item = gtk . ImageMenuItem ( _ ( ' Mark as unplayed ' ) )
2007-08-30 20:54:18 +02:00
item . set_image ( gtk . image_new_from_stock ( gtk . STOCK_CANCEL , gtk . ICON_SIZE_MENU ) )
item . connect ( ' activate ' , lambda w : self . on_item_toggle_played_activate ( w , False , False ) )
2008-04-24 18:28:39 +02:00
menu . append ( self . set_finger_friendly ( item ) )
2007-08-30 20:54:18 +02:00
else :
2008-03-19 18:07:59 +01:00
item = gtk . ImageMenuItem ( _ ( ' Mark as played ' ) )
2007-08-30 20:54:18 +02:00
item . set_image ( gtk . image_new_from_stock ( gtk . STOCK_APPLY , gtk . ICON_SIZE_MENU ) )
item . connect ( ' activate ' , lambda w : self . on_item_toggle_played_activate ( w , False , True ) )
2008-04-24 18:28:39 +02:00
menu . append ( self . set_finger_friendly ( item ) )
2007-08-30 20:54:18 +02:00
2009-08-10 23:14:35 +02:00
if any_locked :
2008-03-19 18:07:59 +01:00
item = gtk . ImageMenuItem ( _ ( ' Allow deletion ' ) )
2007-12-12 19:50:52 +01:00
item . set_image ( gtk . image_new_from_stock ( gtk . STOCK_DIALOG_AUTHENTICATION , gtk . ICON_SIZE_MENU ) )
2009-08-24 00:23:53 +02:00
item . connect ( ' activate ' , lambda w : self . on_item_toggle_lock_activate ( w , False , False ) )
2008-04-24 18:28:39 +02:00
menu . append ( self . set_finger_friendly ( item ) )
2007-12-12 19:50:52 +01:00
else :
2008-03-19 18:07:59 +01:00
item = gtk . ImageMenuItem ( _ ( ' Prohibit deletion ' ) )
2007-12-12 19:50:52 +01:00
item . set_image ( gtk . image_new_from_stock ( gtk . STOCK_DIALOG_AUTHENTICATION , gtk . ICON_SIZE_MENU ) )
2009-08-24 00:23:53 +02:00
item . connect ( ' activate ' , lambda w : self . on_item_toggle_lock_activate ( w , False , True ) )
2008-04-24 18:28:39 +02:00
menu . append ( self . set_finger_friendly ( item ) )
2008-03-19 18:07:59 +01:00
2009-04-01 21:54:17 +02:00
menu . append ( gtk . SeparatorMenuItem ( ) )
# Single item, add episode information menu item
item = gtk . ImageMenuItem ( _ ( ' Episode details ' ) )
item . set_image ( gtk . image_new_from_stock ( gtk . STOCK_INFO , gtk . ICON_SIZE_MENU ) )
2009-08-10 23:14:35 +02:00
item . connect ( ' activate ' , lambda w : self . show_episode_shownotes ( episodes [ 0 ] ) )
2009-04-01 21:54:17 +02:00
menu . append ( self . set_finger_friendly ( item ) )
# If we have it, also add episode website link
2009-08-10 23:14:35 +02:00
if episodes [ 0 ] . link and episodes [ 0 ] . link != episodes [ 0 ] . url :
2009-04-01 21:54:17 +02:00
item = gtk . ImageMenuItem ( _ ( ' Visit website ' ) )
item . set_image ( gtk . image_new_from_icon_name ( WEB_BROWSER_ICON , gtk . ICON_SIZE_MENU ) )
2009-08-10 23:14:35 +02:00
item . connect ( ' activate ' , lambda w : util . open_website ( episodes [ 0 ] . link ) )
2008-04-24 18:28:39 +02:00
menu . append ( self . set_finger_friendly ( item ) )
2008-04-06 02:19:03 +02:00
if gpodder . interface == gpodder . MAEMO :
# Because we open the popup on left-click for Maemo,
# we also include a non-action to close the menu
menu . append ( gtk . SeparatorMenuItem ( ) )
item = gtk . ImageMenuItem ( _ ( ' Close this menu ' ) )
item . set_image ( gtk . image_new_from_stock ( gtk . STOCK_CLOSE , gtk . ICON_SIZE_MENU ) )
2008-04-24 18:28:39 +02:00
menu . append ( self . set_finger_friendly ( item ) )
2007-08-26 17:20:46 +02:00
menu . show_all ( )
2008-06-15 14:28:24 +02:00
# Disable tooltips while we are showing the menu, so
# the tooltip will not appear over the menu
self . episode_list_can_tooltip = False
menu . connect ( ' deactivate ' , lambda menushell : self . episode_list_allow_tooltips ( ) )
2007-08-26 17:20:46 +02:00
menu . popup ( None , None , None , event . button , event . time )
return True
2008-04-06 02:19:03 +02:00
def set_title ( self , new_title ) :
self . default_title = new_title
self . gPodder . set_title ( new_title )
2008-12-13 13:29:45 +01:00
def update_selected_episode_list_icons ( self ) :
"""
Updates the status icons in the episode list
"""
selection = self . treeAvailable . get_selection ( )
( model , paths ) = selection . get_selected_rows ( )
for path in paths :
iter = model . get_iter ( path )
2009-08-13 23:19:12 +02:00
self . episode_list_model . update_by_iter ( iter , \
self . episode_is_downloading , \
2009-08-24 18:11:58 +02:00
self . config . episode_list_descriptions and gpodder . interface != gpodder . MAEMO )
2008-12-13 14:41:32 +01:00
def update_episode_list_icons ( self , urls ) :
"""
Updates the status icons in the episode list
Only update the episodes that have an URL in
the " urls " iterable object ( e . g . a list of URLs )
"""
2009-04-02 00:02:07 +02:00
if self . active_channel is None or not urls :
2008-12-13 14:41:32 +01:00
return
2009-08-13 23:19:12 +02:00
self . episode_list_model . update_by_urls ( urls , \
self . episode_is_downloading , \
2009-08-24 18:11:58 +02:00
self . config . episode_list_descriptions and gpodder . interface != gpodder . MAEMO )
2008-06-15 14:46:34 +02:00
2009-08-24 18:11:58 +02:00
def clean_up_downloads ( self , delete_partial = False ) :
# Clean up temporary files left behind by old gPodder versions
temporary_files = glob . glob ( ' %s /*/.tmp-* ' % self . config . download_dir )
if delete_partial :
temporary_files + = glob . glob ( ' %s /*/*.partial ' % self . config . download_dir )
for tempfile in temporary_files :
util . delete_file ( tempfile )
# Clean up empty download folders and abandoned download folders
download_dirs = glob . glob ( os . path . join ( self . config . download_dir , ' * ' ) )
for ddir in download_dirs :
if os . path . isdir ( ddir ) and False : # FIXME not db.channel_foldername_exists(os.path.basename(ddir)):
globr = glob . glob ( os . path . join ( ddir , ' * ' ) )
if len ( globr ) == 0 or ( len ( globr ) == 1 and globr [ 0 ] . endswith ( ' /cover ' ) ) :
log ( ' Stale download directory found: %s ' , os . path . basename ( ddir ) , sender = self )
shutil . rmtree ( ddir , ignore_errors = True )
def streaming_possible ( self ) :
return self . config . player and self . config . player != ' default '
def playback_episodes_for_real ( self , episodes ) :
groups = collections . defaultdict ( list )
for episode in episodes :
# Mark episode as played in the database
episode . mark ( is_played = True )
file_type = episode . file_type ( )
if file_type == ' video ' and self . config . videoplayer and \
self . config . videoplayer != ' default ' :
player = self . config . videoplayer
elif file_type == ' audio ' and self . config . player and \
self . config . player != ' default ' :
player = self . config . player
else :
player = ' default '
filename = episode . local_filename ( create = False )
if filename is None or not os . path . exists ( filename ) :
filename = episode . url
groups [ player ] . append ( filename )
# Open episodes with system default player
if ' default ' in groups :
for filename in groups [ ' default ' ] :
log ( ' Opening with system default: %s ' , filename , sender = self )
util . gui_open ( filename )
del groups [ ' default ' ]
# For each type now, go and create play commands
for group in groups :
for command in util . format_desktop_command ( group , groups [ group ] ) :
log ( ' Executing: %s ' , repr ( command ) , sender = self )
subprocess . Popen ( command )
2009-07-13 15:32:46 +02:00
def playback_episodes ( self , episodes ) :
2009-01-19 19:00:24 +01:00
if gpodder . interface == gpodder . MAEMO :
2009-08-05 12:18:03 +02:00
if len ( episodes ) == 1 :
text = _ ( ' Opening %s ' ) % saxutils . escape ( episodes [ 0 ] . title )
else :
text = _ ( ' Opening %d episodes ' ) % len ( episodes )
banner = hildon . hildon_banner_show_animation ( self . gPodder , None , text )
2009-01-19 19:00:24 +01:00
def destroy_banner_later ( banner ) :
banner . destroy ( )
return False
gobject . timeout_add ( 5000 , destroy_banner_later , banner )
2009-07-13 15:32:46 +02:00
episodes = [ e for e in episodes if \
2009-08-24 18:11:58 +02:00
e . was_downloaded ( and_exists = True ) or self . streaming_possible ( ) ]
2009-07-13 15:32:46 +02:00
try :
2009-08-24 18:11:58 +02:00
self . playback_episodes_for_real ( episodes )
2009-07-13 15:32:46 +02:00
except Exception , e :
log ( ' Error in playback! ' , sender = self , traceback = True )
self . show_message ( _ ( ' Please check your media player settings in the preferences dialog. ' ) , _ ( ' Error opening player ' ) )
2008-12-13 13:29:45 +01:00
self . update_selected_episode_list_icons ( )
2008-09-06 22:34:35 +02:00
self . updateComboBox ( only_selected_channel = True )
2007-04-03 13:21:12 +02:00
2007-04-03 08:27:46 +02:00
def treeAvailable_search_equal ( self , model , column , key , iter , data = None ) :
2008-02-26 16:49:59 +01:00
if model is None :
2007-04-03 08:27:46 +02:00
return True
key = key . lower ( )
2009-08-13 23:36:18 +02:00
for column in ( EpisodeListModel . C_TITLE , EpisodeListModel . C_DESCRIPTION_STRIPPED ) :
2007-04-03 08:27:46 +02:00
value = model . get_value ( iter , column ) . lower ( )
if value . find ( key ) != - 1 :
return False
return True
2008-05-02 17:36:43 +02:00
def change_menu_item ( self , menuitem , icon = None , label = None ) :
if icon is not None :
2009-05-08 14:28:53 +02:00
menuitem . set_property ( ' stock-id ' , icon )
2008-05-02 17:36:43 +02:00
if label is not None :
2009-05-08 14:28:53 +02:00
menuitem . label = label
2007-04-03 08:27:46 +02:00
2008-06-30 03:10:18 +02:00
def play_or_download ( self ) :
2007-03-31 04:00:30 +02:00
if self . wNotebook . get_current_page ( ) > 0 :
return
2008-06-30 03:10:18 +02:00
( can_play , can_download , can_transfer , can_cancel , can_delete ) = ( False , ) * 5
( is_played , is_locked ) = ( False , ) * 2
2006-12-13 00:11:34 +01:00
2008-08-04 23:26:51 +02:00
open_instead_of_play = False
2007-08-26 17:20:46 +02:00
selection = self . treeAvailable . get_selection ( )
if selection . count_selected_rows ( ) > 0 :
( model , paths ) = selection . get_selected_rows ( )
for path in paths :
2009-08-13 23:19:12 +02:00
episode = model . get_value ( model . get_iter ( path ) , EpisodeListModel . C_EPISODE )
2008-06-30 03:10:18 +02:00
2008-08-04 23:26:51 +02:00
if episode . file_type ( ) not in ( ' audio ' , ' video ' ) :
open_instead_of_play = True
2008-10-15 10:06:56 +02:00
if episode . was_downloaded ( ) :
can_play = episode . was_downloaded ( and_exists = True )
2008-06-30 03:10:18 +02:00
can_delete = True
is_played = episode . is_played
is_locked = episode . is_locked
2008-10-15 10:06:56 +02:00
if not can_play :
can_download = True
2007-08-26 17:20:46 +02:00
else :
2009-04-01 01:12:17 +02:00
if self . episode_is_downloading ( episode ) :
2007-08-26 17:20:46 +02:00
can_cancel = True
else :
can_download = True
2006-12-13 00:11:34 +01:00
2007-08-26 17:20:46 +02:00
can_download = can_download and not can_cancel
2009-08-24 18:11:58 +02:00
can_play = self . streaming_possible ( ) or ( can_play and not can_cancel and not can_download )
can_transfer = can_play and self . config . device_type != ' none ' and not can_cancel and not can_download
2007-08-26 17:20:46 +02:00
2008-08-04 23:26:51 +02:00
if open_instead_of_play :
2009-05-11 23:10:56 +02:00
if gpodder . interface != gpodder . MAEMO :
self . toolPlay . set_stock_id ( gtk . STOCK_OPEN )
2008-08-04 23:26:51 +02:00
can_transfer = False
else :
2009-05-11 23:10:56 +02:00
if gpodder . interface != gpodder . MAEMO :
self . toolPlay . set_stock_id ( gtk . STOCK_MEDIA_PLAY )
2008-08-04 23:26:51 +02:00
2007-08-26 17:20:46 +02:00
self . toolPlay . set_sensitive ( can_play )
self . toolDownload . set_sensitive ( can_download )
self . toolTransfer . set_sensitive ( can_transfer )
self . toolCancel . set_sensitive ( can_cancel )
2009-04-01 21:54:17 +02:00
self . item_cancel_download . set_sensitive ( can_cancel )
self . itemDownloadSelected . set_sensitive ( can_download )
self . itemOpenSelected . set_sensitive ( can_play )
self . itemPlaySelected . set_sensitive ( can_play )
self . itemDeleteSelected . set_sensitive ( can_play and not can_download )
self . item_toggle_played . set_sensitive ( can_play )
self . item_toggle_lock . set_sensitive ( can_play )
2009-05-08 14:28:53 +02:00
self . itemOpenSelected . set_visible ( open_instead_of_play )
self . itemPlaySelected . set_visible ( not open_instead_of_play )
2009-04-01 21:54:17 +02:00
2008-05-02 17:36:43 +02:00
if can_play :
if is_played :
self . change_menu_item ( self . item_toggle_played , gtk . STOCK_CANCEL , _ ( ' Mark as unplayed ' ) )
else :
self . change_menu_item ( self . item_toggle_played , gtk . STOCK_APPLY , _ ( ' Mark as played ' ) )
if is_locked :
self . change_menu_item ( self . item_toggle_lock , gtk . STOCK_DIALOG_AUTHENTICATION , _ ( ' Allow deletion ' ) )
else :
self . change_menu_item ( self . item_toggle_lock , gtk . STOCK_DIALOG_AUTHENTICATION , _ ( ' Prohibit deletion ' ) )
2008-08-04 23:26:51 +02:00
return ( can_play , can_download , can_transfer , can_cancel , can_delete , open_instead_of_play )
2006-12-13 00:11:34 +01:00
2008-04-17 17:45:29 +02:00
def on_cbMaxDownloads_toggled ( self , widget , * args ) :
self . spinMaxDownloads . set_sensitive ( self . cbMaxDownloads . get_active ( ) )
2008-09-06 22:34:35 +02:00
2008-04-17 17:45:29 +02:00
def on_cbLimitDownloads_toggled ( self , widget , * args ) :
2008-11-06 17:01:43 +01:00
self . spinLimitDownloads . set_sensitive ( self . cbLimitDownloads . get_active ( ) )
2008-12-13 14:41:32 +01:00
def episode_new_status_changed ( self , urls ) :
2008-11-06 17:01:43 +01:00
self . updateComboBox ( )
2008-12-13 14:41:32 +01:00
self . update_episode_list_icons ( urls )
2008-04-17 17:45:29 +02:00
2008-12-13 13:29:45 +01:00
def updateComboBox ( self , selected_url = None , only_selected_channel = False , only_these_urls = None ) :
selection = self . treeChannels . get_selection ( )
( model , iter ) = selection . get_selected ( )
2007-09-19 17:04:42 +02:00
2008-09-06 22:34:35 +02:00
if only_selected_channel :
2008-12-13 13:29:45 +01:00
# very cheap! only update selected channel
2008-09-06 22:34:35 +02:00
if iter and self . active_channel is not None :
2009-08-13 22:08:47 +02:00
model . update_by_iter ( iter )
2008-12-13 13:29:45 +01:00
elif not self . channel_list_changed :
# we can keep the model, but have to update some
if only_these_urls is None :
# still cheaper than reloading the whole list
iter = model . get_iter_first ( )
while iter is not None :
2009-08-13 22:08:47 +02:00
model . update_by_iter ( iter )
2008-12-13 13:29:45 +01:00
iter = model . iter_next ( iter )
else :
# ok, we got a bunch of urls to update
2009-08-13 22:08:47 +02:00
model . update_by_urls ( only_these_urls )
2008-09-06 22:34:35 +02:00
else :
if model and iter and selected_url is None :
# Get the URL of the currently-selected podcast
selected_url = model . get_value ( iter , 0 )
2008-06-30 03:10:18 +02:00
2009-08-13 22:08:47 +02:00
# Update the podcast list model with new channels
self . podcast_list_model . set_channels ( self . channels )
2008-05-14 15:38:06 +02:00
2008-09-06 22:34:35 +02:00
try :
selected_path = ( 0 , )
# Find the previously-selected URL in the new
# model if we have an URL (else select first)
if selected_url is not None :
pos = model . get_iter_first ( )
while pos is not None :
url = model . get_value ( pos , 0 )
if url == selected_url :
selected_path = model . get_path ( pos )
break
pos = model . iter_next ( pos )
self . treeChannels . get_selection ( ) . select_path ( selected_path )
except :
log ( ' Cannot set selection on treeChannels ' , sender = self )
2008-12-13 13:29:45 +01:00
self . on_treeChannels_cursor_changed ( self . treeChannels )
self . channel_list_changed = False
2009-04-01 01:12:17 +02:00
def episode_is_downloading ( self , episode ) :
""" Returns True if the given episode is being downloaded at the moment """
2009-06-02 08:39:08 +02:00
if episode is None :
return False
2009-04-01 01:12:17 +02:00
return episode . url in ( task . url for task in self . download_tasks_seen if task . status in ( task . DOWNLOADING , task . QUEUED , task . PAUSED ) )
2009-08-13 23:19:12 +02:00
def on_episode_list_model_updated ( self , banner = None ) :
if banner is not None :
banner . destroy ( )
self . treeAvailable . columns_autosize ( )
self . play_or_download ( )
self . currently_updating = False
2005-11-21 19:21:25 +01:00
2008-12-13 13:29:45 +01:00
def updateTreeView ( self ) :
2008-03-04 12:06:55 +01:00
if self . channels and self . active_channel is not None :
2009-01-19 19:00:24 +01:00
if gpodder . interface == gpodder . MAEMO :
banner = hildon . hildon_banner_show_animation ( self . gPodder , None , _ ( ' Loading episodes for %s ' ) % saxutils . escape ( self . active_channel . title ) )
else :
banner = None
2009-08-13 23:19:12 +02:00
2009-01-19 19:00:24 +01:00
self . currently_updating = True
2009-08-13 23:19:12 +02:00
self . episode_list_model . update_from_channel ( self . active_channel , \
self . episode_is_downloading , \
2009-08-24 18:11:58 +02:00
self . config . episode_list_descriptions and gpodder . interface != gpodder . MAEMO , \
2009-08-13 23:19:12 +02:00
lambda : self . on_episode_list_model_updated ( banner ) )
2006-12-06 21:25:26 +01:00
else :
2009-08-13 23:19:12 +02:00
self . episode_list_model . clear ( )
2005-11-21 19:21:25 +01:00
2005-11-22 14:23:50 +01:00
def drag_data_received ( self , widget , context , x , y , sel , ttype , time ) :
2008-06-14 13:43:53 +02:00
( path , column , rx , ry ) = self . treeChannels . get_path_at_pos ( x , y ) or ( None , ) * 4
dnd_channel = None
if path is not None :
model = self . treeChannels . get_model ( )
iter = model . get_iter ( path )
url = model . get_value ( iter , 0 )
for channel in self . channels :
if channel . url == url :
dnd_channel = channel
break
2005-11-22 14:23:50 +01:00
result = sel . data
2008-06-14 13:43:53 +02:00
rl = result . strip ( ) . lower ( )
if ( rl . endswith ( ' .jpg ' ) or rl . endswith ( ' .png ' ) or rl . endswith ( ' .gif ' ) or rl . endswith ( ' .svg ' ) ) and dnd_channel is not None :
2009-08-24 16:47:59 +02:00
self . cover_downloader . replace_cover ( dnd_channel , result )
2008-06-14 13:43:53 +02:00
else :
self . add_new_channel ( result )
2006-03-31 18:20:18 +02:00
2008-10-20 06:17:22 +02:00
def add_new_channel ( self , result = None , ask_download_new = True , quiet = False , block = False , authentication_tokens = None ) :
2008-12-23 20:17:52 +01:00
result = util . normalize_feed_url ( result )
2009-05-07 18:57:14 +02:00
( scheme , rest ) = result . split ( ' :// ' , 1 )
2008-11-06 19:08:05 +01:00
2008-09-23 03:48:59 +02:00
if not result :
2008-12-23 20:17:52 +01:00
cute_scheme = saxutils . escape ( scheme ) + ' :// '
title = _ ( ' %s URLs are not supported ' ) % cute_scheme
message = _ ( ' gPodder does not understand the URL you supplied. ' )
2008-09-23 03:48:59 +02:00
self . show_message ( message , title )
return
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 . treeChannels . get_selection ( ) . select_path ( ( i , ) )
self . on_treeChannels_cursor_changed ( self . treeChannels )
break
self . show_message ( _ ( ' You have already subscribed to this podcast: %s ' ) % (
saxutils . escape ( old_channel . title ) , ) , _ ( ' Already added ' ) )
return
2008-12-23 20:17:52 +01:00
waitdlg = gtk . MessageDialog ( self . gPodder , 0 , gtk . MESSAGE_INFO , gtk . BUTTONS_NONE )
waitdlg . add_button ( gtk . STOCK_CANCEL , gtk . RESPONSE_CANCEL )
waitdlg . set_title ( _ ( ' Downloading episode list ' ) )
waitdlg . set_markup ( ' <b><big> %s </big></b> ' % waitdlg . get_title ( ) )
2009-06-16 11:01:43 +02:00
waitdlg . format_secondary_text ( _ ( ' Downloading episode information for %s ' ) % result )
2008-12-23 20:17:52 +01:00
waitpb = gtk . ProgressBar ( )
if block :
waitdlg . vbox . add ( waitpb )
waitdlg . show_all ( )
waitdlg . set_response_sensitive ( gtk . RESPONSE_CANCEL , False )
2008-09-23 03:48:59 +02:00
self . entryAddChannel . set_text ( _ ( ' Downloading feed... ' ) )
self . entryAddChannel . set_sensitive ( False )
self . btnAddChannel . set_sensitive ( False )
2008-11-06 19:08:05 +01:00
args = ( result , self . add_new_channel_finish , authentication_tokens , ask_download_new , quiet , waitdlg )
2008-09-23 03:48:59 +02:00
thread = Thread ( target = self . add_new_channel_proc , args = args )
thread . start ( )
2008-11-06 19:08:05 +01:00
while block and thread . isAlive ( ) :
while gtk . events_pending ( ) :
gtk . main_iteration ( False )
waitpb . pulse ( )
2009-01-19 19:00:24 +01:00
time . sleep ( 0.1 )
2008-09-28 18:56:31 +02:00
2008-11-06 19:08:05 +01:00
2008-10-20 06:17:22 +02:00
def add_new_channel_proc ( self , url , callback , authentication_tokens , * callback_args ) :
2008-09-23 03:48:59 +02:00
log ( ' Adding new channel: %s ' , url )
2008-10-20 06:17:22 +02:00
channel = error = None
2008-09-23 03:48:59 +02:00
try :
2009-08-24 18:11:58 +02:00
channel = PodcastChannel . load ( self . db , url = url , create = True ,
2009-08-13 20:39:00 +02:00
authentication_tokens = authentication_tokens ,
2009-08-24 18:11:58 +02:00
max_episodes = self . config . max_episodes_per_feed ,
download_dir = self . config . download_dir )
2009-06-12 00:51:13 +02:00
except feedcore . AuthenticationRequired , e :
error = e
except feedcore . WifiLogin , e :
2008-10-20 06:17:22 +02:00
error = e
2008-09-23 03:48:59 +02:00
except Exception , e :
2009-08-13 20:39:00 +02:00
log ( ' Error adding channel: %s ' , e , traceback = True , sender = self )
2008-09-23 03:48:59 +02:00
2008-10-20 06:17:22 +02:00
util . idle_add ( callback , channel , url , error , * callback_args )
2008-09-23 03:48:59 +02:00
2009-08-13 20:39:00 +02:00
def save_channels_opml ( self ) :
exporter = opml . Exporter ( gpodder . subscription_file )
return exporter . write ( self . channels )
2008-11-06 19:08:05 +01:00
def add_new_channel_finish ( self , channel , url , error , ask_download_new , quiet , waitdlg ) :
2008-09-23 03:48:59 +02:00
if channel is not None :
self . channels . append ( channel )
2008-12-13 13:29:45 +01:00
self . channel_list_changed = True
2009-08-13 20:39:00 +02:00
self . save_channels_opml ( )
2008-09-23 03:48:59 +02:00
if not quiet :
# download changed channels and select the new episode in the UI afterwards
self . update_feed_cache ( force_update = False , select_url_afterwards = channel . url )
2009-03-24 20:44:37 +01:00
try :
( username , password ) = util . username_password_from_url ( url )
except ValueError , ve :
self . show_message ( _ ( ' The following error occured while trying to get authentication data from the URL: ' ) + ' \n \n ' + ve . message , _ ( ' Error getting authentication data ' ) )
( username , password ) = ( None , None )
log ( ' Error getting authentication data from URL: %s ' , url , traceback = True )
2008-09-23 03:48:59 +02:00
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 ( )
2008-10-20 06:17:22 +02:00
# We need to update the channel list otherwise the authentication
# data won't show up in the channel editor.
# TODO: Only updated the newly added feed to save some cpu cycles
2009-08-24 18:11:58 +02:00
self . channels = PodcastChannel . load_from_db ( self . db , self . config . download_dir )
2008-12-13 13:29:45 +01:00
self . channel_list_changed = True
2008-09-23 03:48:59 +02:00
if ask_download_new :
2009-04-01 13:34:19 +02:00
new_episodes = channel . get_new_episodes ( downloading = self . episode_is_downloading )
2008-09-23 03:48:59 +02:00
if len ( new_episodes ) :
self . new_episodes_show ( new_episodes )
2008-10-20 06:17:22 +02:00
2009-06-12 00:51:13 +02:00
elif isinstance ( error , feedcore . AuthenticationRequired ) :
2008-10-20 06:17:22 +02:00
response , auth_tokens = self . UsernamePasswordDialog (
_ ( ' Feed requires authentication ' ) , _ ( ' Please enter your username and password. ' ) )
if response :
self . add_new_channel ( url , authentication_tokens = auth_tokens )
2009-06-12 00:51:13 +02:00
elif isinstance ( error , feedcore . WifiLogin ) :
if self . show_confirmation ( _ ( ' The URL you are trying to add redirects to the website %s . Do you want to visit the website to login now? ' ) % saxutils . escape ( error . data ) , _ ( ' Website redirection detected ' ) ) :
util . open_website ( error . data )
if self . show_confirmation ( _ ( ' Please login to the website now. Should I retry subscribing to the podcast at %s ? ' ) % saxutils . escape ( url ) , _ ( ' Retry adding channel ' ) ) :
self . add_new_channel ( url )
2008-09-23 03:48:59 +02:00
else :
# Ok, the URL is not a channel, or there is some other
# error - let's see if it's a web page or OPML file...
2009-06-12 00:51:13 +02:00
handled = False
2007-08-20 15:45:46 +02:00
try :
2008-09-23 03:48:59 +02:00
data = urllib2 . urlopen ( url ) . read ( ) . lower ( )
if ' </opml> ' in data :
# This looks like an OPML feed
self . on_item_import_from_file_activate ( None , url )
2009-06-12 00:51:13 +02:00
handled = True
2008-10-20 06:17:22 +02:00
2008-09-23 03:48:59 +02:00
elif ' </html> ' in data :
# This looks like a web page
title = _ ( ' The URL is a website ' )
message = _ ( ' The URL you specified points to a web page. You need to find the " feed " URL of the podcast to add to gPodder. Do you want to visit this website now and look for the podcast feed URL? \n \n (Hint: Look for " XML feed " , " RSS feed " or " Podcast feed " if you are unsure for what to look. If there is only an iTunes URL, try adding this one.) ' )
if self . show_confirmation ( message , title ) :
util . open_website ( url )
2009-06-12 00:51:13 +02:00
handled = True
2008-10-20 06:17:22 +02:00
2008-06-30 03:10:18 +02:00
except Exception , e :
2008-09-23 03:48:59 +02:00
log ( ' Error trying to handle the URL as OPML or web page: %s ' , e , sender = self )
2009-06-12 00:51:13 +02:00
if not handled :
title = _ ( ' Error adding podcast ' )
message = _ ( ' The podcast could not be added. Please check the spelling of the URL or try again later. ' )
self . show_message ( message , title )
2008-09-23 03:48:59 +02:00
2008-10-20 06:17:22 +02:00
self . entryAddChannel . set_text ( self . ENTER_URL_TEXT )
2008-09-23 03:48:59 +02:00
self . entryAddChannel . set_sensitive ( True )
self . btnAddChannel . set_sensitive ( True )
2008-10-20 06:17:22 +02:00
self . update_podcasts_tab ( )
2009-08-24 18:11:58 +02:00
self . _update_cover ( channel )
2008-11-06 19:08:05 +01:00
waitdlg . destroy ( )
2006-12-09 02:59:53 +01:00
2007-08-20 15:45:46 +02:00
2009-06-11 18:01:01 +02:00
def update_feed_cache_finish_callback ( self , updated_urls = None , select_url_afterwards = None ) :
2009-08-24 18:11:58 +02:00
self . db . commit ( )
2008-07-14 18:28:34 +02:00
self . updating_feed_cache = False
2008-01-09 23:46:17 +01:00
2009-08-24 18:11:58 +02:00
self . channels = PodcastChannel . load_from_db ( self . db , self . config . download_dir )
2009-04-02 15:24:54 +02:00
self . channel_list_changed = True
2009-05-12 12:13:03 +02:00
self . updateComboBox ( selected_url = select_url_afterwards )
2009-04-02 15:24:54 +02:00
2009-06-05 11:57:01 +02:00
# Only search for new episodes in podcasts that have been
# updated, not in other podcasts (for single-feed updates)
episodes = self . get_new_episodes ( [ c for c in self . channels if c . url in updated_urls ] )
2009-04-02 15:24:54 +02:00
2008-01-09 23:46:17 +01:00
if self . tray_icon :
2009-06-11 18:01:01 +02:00
self . tray_icon . set_status ( )
2009-04-02 15:24:54 +02:00
2009-06-11 18:01:01 +02:00
if self . feed_cache_update_cancelled :
# The user decided to abort the feed update
self . show_update_feeds_buttons ( )
elif not episodes :
# Nothing new here - but inform the user
2009-04-02 15:24:54 +02:00
self . pbFeedUpdate . set_fraction ( 1.0 )
2009-06-11 18:01:01 +02:00
self . pbFeedUpdate . set_text ( _ ( ' No new episodes ' ) )
2009-04-02 15:24:54 +02:00
self . feed_cache_update_cancelled = True
self . btnCancelFeedUpdate . show ( )
self . btnCancelFeedUpdate . set_sensitive ( True )
if gpodder . interface == gpodder . MAEMO :
# btnCancelFeedUpdate is a ToolButton on Maemo
self . btnCancelFeedUpdate . set_stock_id ( gtk . STOCK_APPLY )
else :
# btnCancelFeedUpdate is a normal gtk.Button
self . btnCancelFeedUpdate . set_image ( gtk . image_new_from_stock ( gtk . STOCK_APPLY , gtk . ICON_SIZE_BUTTON ) )
2009-07-12 21:10:11 +02:00
else :
# New episodes are available
self . pbFeedUpdate . set_fraction ( 1.0 )
# Are we minimized and should we auto download?
2009-08-24 18:11:58 +02:00
if ( self . minimized and ( self . config . auto_download == ' minimized ' ) ) or ( self . config . auto_download == ' always ' ) :
2009-06-11 18:01:01 +02:00
self . download_episode_list ( episodes )
if len ( episodes ) == 1 :
title = _ ( ' Downloading one new episode ' )
else :
title = _ ( ' Downloading %d new episodes ' ) % len ( episodes )
2009-07-12 21:10:11 +02:00
if self . tray_icon :
message = self . tray_icon . format_episode_list ( [ e . title for e in episodes ] )
self . tray_icon . send_notification ( message , title )
self . show_update_feeds_buttons ( )
2009-05-02 07:22:52 +02:00
else :
2009-07-12 21:10:11 +02:00
self . show_update_feeds_buttons ( )
# New episodes are available and we are not minimized
2009-08-24 18:11:58 +02:00
if not self . config . do_not_show_new_episodes_dialog :
2009-07-12 21:10:11 +02:00
self . new_episodes_show ( episodes )
2009-06-11 18:01:01 +02:00
else :
2009-07-12 21:10:11 +02:00
if len ( episodes ) == 1 :
message = _ ( ' One new episode is available for download ' )
else :
message = _ ( ' %i new episodes are available for download ' % len ( episodes ) )
self . pbFeedUpdate . set_text ( message )
2008-08-30 19:23:04 +02:00
2009-08-24 16:47:59 +02:00
def _update_cover ( self , channel ) :
if not os . path . exists ( channel . cover_file ) and channel . image :
self . cover_downloader . request_cover ( channel )
2009-06-11 18:01:01 +02:00
def update_feed_cache_proc ( self , channels , select_url_afterwards ) :
total = len ( channels )
2008-06-30 03:10:18 +02:00
2009-06-11 18:01:01 +02:00
for updated , channel in enumerate ( channels ) :
if not self . feed_cache_update_cancelled :
2009-06-12 00:51:13 +02:00
try :
2009-08-24 18:11:58 +02:00
channel . update ( max_episodes = self . config . max_episodes_per_feed )
2009-08-24 16:47:59 +02:00
self . _update_cover ( channel )
2009-08-24 13:34:51 +02:00
# except feedcore.Offline:
# self.feed_cache_update_cancelled = True
# if not self.minimized:
# util.idle_add(self.show_message, _('The feed update has been cancelled because you appear to be offline.'), _('Cannot connect to server'))
# break
2009-06-12 00:51:13 +02:00
except Exception , e :
util . idle_add ( self . show_message , _ ( ' There has been an error updating %s : %s ' ) % ( saxutils . escape ( channel . url ) , saxutils . escape ( str ( e ) ) ) , _ ( ' Error while updating feed ' ) )
2009-06-12 02:44:04 +02:00
log ( ' Error: %s ' , str ( e ) , sender = self , traceback = True )
2008-08-30 19:23:04 +02:00
2009-06-11 18:01:01 +02:00
# By the time we get here the update may have already been cancelled
if not self . feed_cache_update_cancelled :
def update_progress ( ) :
progression = _ ( ' Updated %s ( %d / %d ) ' ) % ( channel . title , updated , total )
self . pbFeedUpdate . set_text ( progression )
if self . tray_icon :
self . tray_icon . set_status ( self . tray_icon . STATUS_UPDATING_FEED_CACHE , progression )
self . pbFeedUpdate . set_fraction ( float ( updated ) / float ( total ) )
util . idle_add ( update_progress )
if self . feed_cache_update_cancelled :
break
2008-08-30 19:23:04 +02:00
2009-06-11 18:01:01 +02:00
updated_urls = [ c . url for c in channels ]
util . idle_add ( self . update_feed_cache_finish_callback , updated_urls , select_url_afterwards )
2008-08-30 19:23:04 +02:00
2009-06-11 18:01:01 +02:00
def show_update_feeds_buttons ( self ) :
# Make sure that the buttons for updating feeds
# appear - this should happen after a feed update
if gpodder . interface == gpodder . MAEMO :
self . btnUpdateSelectedFeed . show ( )
self . toolFeedUpdateProgress . hide ( )
self . btnCancelFeedUpdate . hide ( )
self . btnCancelFeedUpdate . set_is_important ( False )
self . btnCancelFeedUpdate . set_stock_id ( gtk . STOCK_CLOSE )
self . toolbarSpacer . set_expand ( True )
self . toolbarSpacer . set_draw ( False )
else :
self . hboxUpdateFeeds . hide ( )
self . btnUpdateFeeds . show ( )
self . itemUpdate . set_sensitive ( True )
self . itemUpdateChannel . set_sensitive ( True )
2007-03-10 16:57:56 +01:00
2008-04-22 20:34:41 +02:00
def on_btnCancelFeedUpdate_clicked ( self , widget ) :
2009-06-11 18:01:01 +02:00
if not self . feed_cache_update_cancelled :
self . pbFeedUpdate . set_text ( _ ( ' Cancelling... ' ) )
2009-01-19 19:00:24 +01:00
self . feed_cache_update_cancelled = True
self . btnCancelFeedUpdate . set_sensitive ( False )
2009-06-11 18:01:01 +02:00
else :
self . show_update_feeds_buttons ( )
2008-04-22 20:34:41 +02:00
2009-06-11 18:01:01 +02:00
def update_feed_cache ( self , channels = None , force_update = True , select_url_afterwards = None ) :
2008-07-14 18:28:34 +02:00
if self . updating_feed_cache :
return
2008-08-30 19:23:04 +02:00
if not force_update :
2009-08-24 18:11:58 +02:00
self . channels = PodcastChannel . load_from_db ( self . db , self . config . download_dir )
2008-12-13 13:29:45 +01:00
self . channel_list_changed = True
2009-05-12 12:13:03 +02:00
self . updateComboBox ( selected_url = select_url_afterwards )
2008-08-30 19:23:04 +02:00
return
2008-07-14 18:28:34 +02:00
self . updating_feed_cache = True
self . itemUpdate . set_sensitive ( False )
2008-08-30 19:23:04 +02:00
self . itemUpdateChannel . set_sensitive ( False )
2008-01-09 23:46:17 +01:00
if self . tray_icon :
self . tray_icon . set_status ( self . tray_icon . STATUS_UPDATING_FEED_CACHE )
2008-08-30 19:23:04 +02:00
if channels is None :
channels = self . channels
if len ( channels ) == 1 :
2009-01-19 19:00:24 +01:00
text = _ ( ' Updating " %s " ... ' ) % channels [ 0 ] . title
2008-08-30 19:23:04 +02:00
else :
2009-01-19 19:00:24 +01:00
text = _ ( ' Updating %d feeds... ' ) % len ( channels )
self . pbFeedUpdate . set_text ( text )
2008-08-30 19:23:04 +02:00
self . pbFeedUpdate . set_fraction ( 0 )
2008-01-09 23:46:17 +01:00
2008-04-22 20:34:41 +02:00
self . feed_cache_update_cancelled = False
2009-01-19 19:00:24 +01:00
self . btnCancelFeedUpdate . show ( )
self . btnCancelFeedUpdate . set_sensitive ( True )
if gpodder . interface == gpodder . MAEMO :
self . toolbarSpacer . set_expand ( False )
self . toolbarSpacer . set_draw ( True )
self . btnUpdateSelectedFeed . hide ( )
self . toolFeedUpdateProgress . show_all ( )
else :
2009-04-02 15:24:54 +02:00
self . btnCancelFeedUpdate . set_image ( gtk . image_new_from_stock ( gtk . STOCK_STOP , gtk . ICON_SIZE_BUTTON ) )
2009-01-19 19:00:24 +01:00
self . hboxUpdateFeeds . show_all ( )
self . btnUpdateFeeds . hide ( )
2009-06-11 18:01:01 +02:00
args = ( channels , select_url_afterwards )
Thread ( target = self . update_feed_cache_proc , args = args ) . start ( )
2007-03-10 16:57:56 +01:00
2008-01-09 23:46:17 +01:00
def on_gPodder_delete_event ( self , widget , * args ) :
""" Called when the GUI wants to close the window
Displays a confirmation dialog ( and closes / hides gPodder )
"""
2005-11-21 19:21:25 +01:00
2009-08-24 16:17:32 +02:00
downloading = self . download_status_model . are_downloads_in_progress ( )
2008-01-09 23:46:17 +01:00
2008-03-30 10:50:34 +02:00
# Only iconify if we are using the window's "X" button,
# but not when we are using "Quit" in the menu or toolbar
2009-08-24 18:11:58 +02:00
if not self . config . on_quit_ask and self . config . on_quit_systray and self . tray_icon and widget . get_name ( ) not in ( ' toolQuit ' , ' itemQuit ' ) :
2008-02-06 10:45:40 +01:00
self . iconify_main_window ( )
2009-08-24 18:11:58 +02:00
elif self . config . on_quit_ask or downloading :
2008-04-06 02:19:03 +02:00
if gpodder . interface == gpodder . MAEMO :
result = self . show_confirmation ( _ ( ' Do you really want to quit gPodder now? ' ) )
if result :
self . close_gpodder ( )
else :
return True
2008-01-09 23:46:17 +01:00
dialog = gtk . MessageDialog ( self . gPodder , gtk . DIALOG_MODAL , gtk . MESSAGE_QUESTION , gtk . BUTTONS_NONE )
dialog . add_button ( gtk . STOCK_CANCEL , gtk . RESPONSE_CANCEL )
dialog . add_button ( gtk . STOCK_QUIT , gtk . RESPONSE_CLOSE )
title = _ ( ' Quit gPodder ' )
if downloading :
2009-02-09 23:26:47 +01:00
message = _ ( ' You are downloading episodes. You can resume downloads the next time you start gPodder. Do you want to quit now? ' )
2008-01-09 23:46:17 +01:00
else :
message = _ ( ' Do you really want to quit gPodder now? ' )
dialog . set_title ( title )
dialog . set_markup ( ' <span weight= " bold " size= " larger " > %s </span> \n \n %s ' % ( title , message ) )
if not downloading :
cb_ask = gtk . CheckButton ( _ ( " Don ' t ask me again " ) )
dialog . vbox . pack_start ( cb_ask )
cb_ask . show_all ( )
result = dialog . run ( )
dialog . destroy ( )
if result == gtk . RESPONSE_CLOSE :
if not downloading and cb_ask . get_active ( ) == True :
2009-08-24 18:11:58 +02:00
self . config . on_quit_ask = False
2008-01-09 23:46:17 +01:00
self . close_gpodder ( )
else :
self . close_gpodder ( )
return True
def close_gpodder ( self ) :
""" clean everything and exit properly
"""
2006-12-09 02:59:53 +01:00
if self . channels :
2009-08-13 20:39:00 +02:00
if self . save_channels_opml ( ) :
2009-08-24 18:11:58 +02:00
if self . config . my_gpodder_autoupload :
2008-12-10 23:07:10 +01:00
log ( ' Uploading to my.gpodder.org on close ' , sender = self )
2008-12-11 14:24:29 +01:00
util . idle_add ( self . on_upload_to_mygpo , None )
2008-12-10 23:07:10 +01:00
else :
2008-05-02 17:36:43 +02:00
self . show_message ( _ ( ' Please check your permissions and free disk space. ' ) , _ ( ' Error saving podcast list ' ) )
2005-11-21 19:21:25 +01:00
2008-11-19 18:55:59 +01:00
self . gPodder . hide ( )
2009-04-02 00:33:49 +02:00
2009-05-09 15:21:04 +02:00
if self . tray_icon is not None :
self . tray_icon . set_visible ( False )
2009-04-02 00:33:49 +02:00
# Notify all tasks to to carry out any clean-up actions
2009-08-24 16:17:32 +02:00
self . download_status_model . tell_all_tasks_to_quit ( )
2009-04-02 00:33:49 +02:00
2008-11-19 18:55:59 +01:00
while gtk . events_pending ( ) :
gtk . main_iteration ( False )
2009-08-24 18:11:58 +02:00
self . db . close ( )
2005-11-23 20:53:18 +01:00
2009-05-08 14:28:53 +02:00
self . quit ( )
2009-05-11 20:09:21 +02:00
sys . exit ( 0 )
2005-11-21 19:21:25 +01:00
2007-12-12 20:57:50 +01:00
def get_old_episodes ( self ) :
episodes = [ ]
for channel in self . channels :
2008-06-30 03:10:18 +02:00
for episode in channel . get_downloaded_episodes ( ) :
2009-08-24 18:11:58 +02:00
if episode . age_in_days ( ) > self . config . episode_old_age and \
2009-08-13 20:39:00 +02:00
not episode . is_locked and episode . is_played :
2007-12-12 20:57:50 +01:00
episodes . append ( episode )
return episodes
2007-11-08 20:11:57 +01:00
def delete_episode_list ( self , episodes , confirm = True ) :
if len ( episodes ) == 0 :
return
2008-09-06 22:34:35 +02:00
2007-11-08 20:11:57 +01:00
if len ( episodes ) == 1 :
message = _ ( ' Do you really want to delete this episode? ' )
else :
message = _ ( ' Do you really want to delete %d episodes? ' ) % len ( episodes )
if confirm and self . show_confirmation ( message , _ ( ' Delete episodes ' ) ) == False :
return
2008-12-14 17:30:17 +01:00
episode_urls = set ( )
channel_urls = set ( )
2007-11-08 20:11:57 +01:00
for episode in episodes :
log ( ' Deleting episode: %s ' , episode . title , sender = self )
episode . delete_from_disk ( )
2008-12-14 17:30:17 +01:00
episode_urls . add ( episode . url )
channel_urls . add ( episode . channel . url )
2007-11-08 20:11:57 +01:00
2009-04-27 14:54:02 +02:00
# Episodes have been deleted - persist the database
2009-08-24 18:11:58 +02:00
self . db . commit ( )
2009-04-27 14:54:02 +02:00
2009-08-24 01:06:22 +02:00
self . update_episode_list_icons ( episode_urls )
self . updateComboBox ( only_these_urls = channel_urls )
2007-11-08 20:11:57 +01:00
def on_itemRemoveOldEpisodes_activate ( self , widget ) :
columns = (
2009-08-13 20:39:00 +02:00
( ' title_markup ' , None , None , _ ( ' Episode ' ) ) ,
2008-08-13 03:47:01 +02:00
( ' channel_prop ' , None , None , _ ( ' Podcast ' ) ) ,
( ' filesize_prop ' , ' length ' , gobject . TYPE_INT , _ ( ' Size ' ) ) ,
( ' pubdate_prop ' , ' pubDate ' , gobject . TYPE_INT , _ ( ' Released ' ) ) ,
( ' played_prop ' , None , None , _ ( ' Status ' ) ) ,
( ' age_prop ' , None , None , _ ( ' Downloaded ' ) ) ,
2007-11-08 20:11:57 +01:00
)
selection_buttons = {
2008-06-30 03:10:18 +02:00
_ ( ' Select played ' ) : lambda episode : episode . is_played ,
2009-08-24 18:11:58 +02:00
_ ( ' Select older than %d days ' ) % self . config . episode_old_age : lambda episode : episode . age_in_days ( ) > self . config . episode_old_age ,
2007-11-08 20:11:57 +01:00
}
instructions = _ ( ' Select the episodes you want to delete from your hard disk. ' )
episodes = [ ]
selected = [ ]
for channel in self . channels :
2008-06-30 03:10:18 +02:00
for episode in channel . get_downloaded_episodes ( ) :
if not episode . is_locked :
episodes . append ( episode )
selected . append ( episode . is_played )
2007-11-08 20:11:57 +01:00
2009-08-24 18:37:04 +02:00
gPodderEpisodeSelector ( self . gPodder , title = _ ( ' Remove old episodes ' ) , instructions = instructions , \
2007-11-08 20:11:57 +01:00
episodes = episodes , selected = selected , columns = columns , \
stock_ok_button = gtk . STOCK_DELETE , callback = self . delete_episode_list , \
2009-08-24 18:11:58 +02:00
selection_buttons = selection_buttons , _config = self . config )
2007-11-08 20:11:57 +01:00
2009-08-10 23:14:35 +02:00
def on_selected_episodes_status_changed ( self ) :
self . update_selected_episode_list_icons ( )
self . updateComboBox ( only_selected_channel = True )
2009-08-24 18:11:58 +02:00
self . db . commit ( )
2009-08-10 23:14:35 +02:00
2008-06-30 03:10:18 +02:00
def mark_selected_episodes_new ( self ) :
2009-08-10 23:14:35 +02:00
for episode in self . get_selected_episodes ( ) :
episode . mark_new ( )
self . on_selected_episodes_status_changed ( )
2007-08-30 20:54:18 +02:00
2008-06-30 03:10:18 +02:00
def mark_selected_episodes_old ( self ) :
2009-08-10 23:14:35 +02:00
for episode in self . get_selected_episodes ( ) :
episode . mark_old ( )
self . on_selected_episodes_status_changed ( )
2008-06-13 14:30:42 +02:00
2007-08-30 20:54:18 +02:00
def on_item_toggle_played_activate ( self , widget , toggle = True , new_value = False ) :
2009-08-10 23:14:35 +02:00
for episode in self . get_selected_episodes ( ) :
if toggle :
episode . mark ( is_played = not episode . is_played )
else :
episode . mark ( is_played = new_value )
self . on_selected_episodes_status_changed ( )
2007-08-30 20:54:18 +02:00
2007-12-12 19:50:52 +01:00
def on_item_toggle_lock_activate ( self , widget , toggle = True , new_value = False ) :
2009-08-10 23:14:35 +02:00
for episode in self . get_selected_episodes ( ) :
if toggle :
episode . mark ( is_locked = not episode . is_locked )
else :
episode . mark ( is_locked = new_value )
self . on_selected_episodes_status_changed ( )
2007-12-12 19:50:52 +01:00
2008-11-19 17:05:19 +01:00
def on_channel_toggle_lock_activate ( self , widget , toggle = True , new_value = False ) :
self . active_channel . channel_is_locked = not self . active_channel . channel_is_locked
2009-08-10 23:14:35 +02:00
self . active_channel . update_channel_lock ( )
2008-11-19 17:05:19 +01:00
if self . active_channel . channel_is_locked :
self . change_menu_item ( self . channel_toggle_lock , gtk . STOCK_DIALOG_AUTHENTICATION , _ ( ' Allow deletion of all episodes ' ) )
else :
self . change_menu_item ( self . channel_toggle_lock , gtk . STOCK_DIALOG_AUTHENTICATION , _ ( ' Prohibit deletion of all episodes ' ) )
for episode in self . active_channel . get_all_episodes ( ) :
2009-08-10 23:14:35 +02:00
episode . mark ( is_locked = self . active_channel . channel_is_locked )
2008-11-19 17:05:19 +01:00
2008-12-13 13:29:45 +01:00
self . updateComboBox ( only_selected_channel = True )
2009-08-10 23:14:35 +02:00
self . update_episode_list_icons ( [ e . url for e in self . active_channel . get_all_episodes ( ) ] )
2008-11-19 17:05:19 +01:00
2009-08-10 18:32:08 +02:00
def send_subscriptions ( self ) :
try :
subprocess . Popen ( [ ' xdg-email ' , ' --subject ' , _ ( ' My podcast subscriptions ' ) ,
2009-08-11 00:36:38 +02:00
' --attach ' , gpodder . subscription_file ] )
2009-08-10 18:32:08 +02:00
except :
return False
return True
2008-05-02 17:36:43 +02:00
def on_item_email_subscriptions_activate ( self , widget ) :
if not self . channels :
self . show_message ( _ ( ' Your subscription list is empty. ' ) , _ ( ' Could not send list ' ) )
2009-08-10 18:32:08 +02:00
elif not self . send_subscriptions ( ) :
2008-05-02 17:36:43 +02:00
self . show_message ( _ ( ' There was an error sending your subscription list via e-mail. ' ) , _ ( ' Could not send list ' ) )
2008-08-30 19:23:04 +02:00
def on_itemUpdateChannel_activate ( self , widget = None ) :
self . update_feed_cache ( channels = [ self . active_channel , ] )
2009-06-11 18:01:01 +02:00
def on_itemUpdate_activate ( self , widget = None ) :
2006-12-06 21:25:26 +01:00
if self . channels :
2009-06-11 18:01:01 +02:00
self . update_feed_cache ( )
2006-12-06 21:25:26 +01:00
else :
2009-08-24 18:37:04 +02:00
gPodderWelcome ( self . gPodder , center_on_widget = self . gPodder , show_example_podcasts_callback = self . on_itemImportChannels_activate , setup_my_gpodder_callback = self . on_download_from_mygpo )
2005-11-21 19:21:25 +01:00
2009-04-01 01:12:17 +02:00
def download_episode_list_paused ( self , episodes ) :
self . download_episode_list ( episodes , True )
def download_episode_list ( self , episodes , add_paused = False ) :
2007-11-08 20:11:57 +01:00
for episode in episodes :
log ( ' Downloading episode: %s ' , episode . title , sender = self )
2009-04-01 01:12:17 +02:00
if not episode . was_downloaded ( and_exists = True ) :
task_exists = False
for task in self . download_tasks_seen :
if episode . url == task . url and task . status not in ( task . DOWNLOADING , task . QUEUED ) :
self . download_queue_manager . add_task ( task )
2009-08-17 21:46:17 +02:00
self . enable_download_list_update ( )
2009-04-01 01:12:17 +02:00
task_exists = True
continue
if task_exists :
continue
2009-06-04 12:35:18 +02:00
try :
2009-08-24 18:11:58 +02:00
task = download . DownloadTask ( episode , self . config )
2009-06-04 12:35:18 +02:00
except Exception , e :
self . show_message ( _ ( ' Download error while downloading %s : \n \n %s ' ) % ( episode . title , str ( e ) ) , _ ( ' Download error ' ) )
log ( ' Download error while downloading %s ' , episode . title , sender = self , traceback = True )
continue
2009-04-01 01:12:17 +02:00
if add_paused :
task . status = task . PAUSED
else :
self . download_queue_manager . add_task ( task )
2009-08-24 16:17:32 +02:00
self . download_status_model . register_task ( task )
2009-08-17 21:46:17 +02:00
self . enable_download_list_update ( )
2006-12-20 20:44:29 +01:00
2008-01-22 10:10:08 +01:00
def new_episodes_show ( self , episodes ) :
2007-11-08 20:11:57 +01:00
columns = (
2009-08-13 20:39:00 +02:00
( ' title_markup ' , None , None , _ ( ' Episode ' ) ) ,
2008-08-13 03:47:01 +02:00
( ' channel_prop ' , None , None , _ ( ' Podcast ' ) ) ,
( ' filesize_prop ' , ' length ' , gobject . TYPE_INT , _ ( ' Size ' ) ) ,
( ' pubdate_prop ' , ' pubDate ' , gobject . TYPE_INT , _ ( ' Released ' ) ) ,
2007-11-08 20:11:57 +01:00
)
2009-04-02 15:24:54 +02:00
instructions = _ ( ' Select the episodes you want to download now. ' )
2007-03-10 18:42:32 +01:00
2009-08-24 18:37:04 +02:00
gPodderEpisodeSelector ( self . gPodder , title = _ ( ' New episodes available ' ) , instructions = instructions , \
2009-04-02 15:24:54 +02:00
episodes = episodes , columns = columns , selected_default = True , \
stock_ok_button = ' gpodder-download ' , \
callback = self . download_episode_list , \
remove_callback = lambda e : e . mark_old ( ) , \
remove_action = _ ( ' Never download ' ) , \
2009-08-24 18:11:58 +02:00
remove_finished = self . episode_new_status_changed , \
_config = self . config )
2008-01-22 10:10:08 +01:00
def on_itemDownloadAllNew_activate ( self , widget , * args ) :
2009-04-02 15:24:54 +02:00
new_episodes = self . get_new_episodes ( )
if len ( new_episodes ) :
self . new_episodes_show ( new_episodes )
else :
msg = _ ( ' No new episodes available for download ' )
if self . tray_icon is not None and self . minimized :
self . tray_icon . send_notification ( msg )
else :
self . show_message ( msg , _ ( ' No new episodes ' ) )
2008-08-30 19:23:04 +02:00
2009-04-02 15:24:54 +02:00
def get_new_episodes ( self , channels = None ) :
2008-08-30 19:23:04 +02:00
if channels is None :
channels = self . channels
2008-01-22 10:10:08 +01:00
episodes = [ ]
2008-08-30 19:23:04 +02:00
for channel in channels :
2009-04-01 13:34:19 +02:00
for episode in channel . get_new_episodes ( downloading = self . episode_is_downloading ) :
2008-01-22 10:10:08 +01:00
episodes . append ( episode )
2009-04-02 15:24:54 +02:00
return episodes
2006-12-20 20:44:29 +01:00
2008-06-13 09:41:36 +02:00
def get_all_episodes ( self , exclude_nonsignificant = True ) :
""" ' exclude_nonsignificant ' will exclude non-downloaded episodes
and all episodes from channels that are set to skip when syncing """
episode_list = [ ]
for channel in self . channels :
if not channel . sync_to_devices and exclude_nonsignificant :
log ( ' Skipping channel: %s ' , channel . title , sender = self )
continue
for episode in channel . get_all_episodes ( ) :
2008-06-30 03:10:18 +02:00
if episode . was_downloaded ( and_exists = True ) or not exclude_nonsignificant :
2008-06-13 09:41:36 +02:00
episode_list . append ( episode )
return episode_list
def ipod_delete_played ( self , device ) :
all_episodes = self . get_all_episodes ( exclude_nonsignificant = False )
episodes_on_device = device . get_all_tracks ( )
for local_episode in all_episodes :
2008-07-11 20:17:48 +02:00
device_episode = device . episode_on_device ( local_episode )
if device_episode and ( local_episode . is_played and not local_episode . is_locked
2009-08-10 23:14:35 +02:00
or local_episode . state == gpodder . STATE_DELETED ) :
2008-07-09 03:43:01 +02:00
log ( " mp3_player_delete_played: removing %s " % device_episode . title )
device . remove_track ( device_episode )
2008-06-13 09:41:36 +02:00
2008-01-28 12:38:53 +01:00
def on_sync_to_ipod_activate ( self , widget , episodes = None ) :
2008-07-04 02:30:28 +02:00
# make sure gpod is available before even trying to sync
2009-08-24 18:11:58 +02:00
if self . config . device_type == ' ipod ' and not sync . gpod_available :
2008-07-04 02:30:28 +02:00
title = _ ( ' Cannot Sync To iPod ' )
message = _ ( ' Please install the libgpod python bindings (python-gpod) and restart gPodder to continue. ' )
self . notification ( message , title )
2008-07-20 04:55:30 +02:00
return
2009-08-24 18:11:58 +02:00
elif self . config . device_type == ' mtp ' and not sync . pymtp_available :
2008-09-01 21:26:35 +02:00
title = _ ( ' Cannot sync to MTP device ' )
message = _ ( ' Please install the libmtp python bindings (python-pymtp) and restart gPodder to continue. ' )
self . notification ( message , title )
return
2008-07-20 04:55:30 +02:00
2009-08-24 18:11:58 +02:00
device = sync . open_device ( self . config )
2008-07-20 04:55:30 +02:00
device . register ( ' post-done ' , self . sync_to_ipod_completed )
2008-01-28 12:38:53 +01:00
if device is None :
2007-03-10 18:42:32 +01:00
title = _ ( ' No device configured ' )
message = _ ( ' To use the synchronization feature, please configure your device in the preferences dialog first. ' )
2008-01-28 12:38:53 +01:00
self . notification ( message , title )
2007-03-18 19:28:17 +01:00
return
2008-01-28 12:38:53 +01:00
if not device . open ( ) :
title = _ ( ' Cannot open device ' )
message = _ ( ' There has been an error opening your device. ' )
self . notification ( message , title )
2008-01-09 23:56:03 +01:00
return
2007-03-18 19:28:17 +01:00
2009-08-24 18:11:58 +02:00
if self . config . device_type == ' ipod ' :
2009-04-21 15:32:45 +02:00
#update played episodes and delete if requested
for channel in self . channels :
if channel . sync_to_devices :
allepisodes = [ episode for episode in channel . get_all_episodes ( ) if episode . was_downloaded ( and_exists = True ) ]
2009-08-24 18:11:58 +02:00
device . update_played_or_delete ( channel , allepisodes , self . config . ipod_delete_played_from_db )
2009-04-21 15:32:45 +02:00
2009-08-24 18:11:58 +02:00
if self . config . ipod_purge_old_episodes :
2009-05-07 23:55:51 +02:00
device . purge ( )
2008-11-08 22:27:36 +01:00
2008-07-20 04:55:30 +02:00
sync_all_episodes = not bool ( episodes )
2007-03-18 19:28:17 +01:00
2008-01-28 12:38:53 +01:00
if episodes is None :
2008-07-09 03:43:01 +02:00
episodes = self . get_all_episodes ( )
# make sure we have enough space on the device
2009-08-24 01:22:36 +02:00
can_sync = True
2008-07-09 03:43:01 +02:00
total_size = 0
2009-08-24 01:22:36 +02:00
free_space = max ( device . get_free_space ( ) , 0 )
2008-07-09 03:43:01 +02:00
for episode in episodes :
2009-08-24 18:11:58 +02:00
if not device . episode_on_device ( episode ) and not ( sync_all_episodes and self . config . only_sync_not_played and episode . is_played ) :
2009-02-09 23:26:47 +01:00
filename = episode . local_filename ( create = False )
if filename is not None :
total_size + = util . calculate_size ( str ( filename ) )
2008-07-09 03:43:01 +02:00
if total_size > free_space :
2009-08-24 01:22:36 +02:00
title = _ ( ' Not enough space left on device ' )
message = _ ( ' You need to free up %s . \n Do you want to continue? ' ) % ( util . format_filesize ( total_size - free_space ) , )
can_sync = self . show_confirmation ( message , title )
if self . tray_icon :
self . tray_icon . set_synchronisation_device ( device )
if can_sync :
2009-08-24 19:50:13 +02:00
gPodderSyncProgress ( self . gPodder , device = device , gPodder = self )
2008-07-20 04:55:30 +02:00
Thread ( target = self . sync_to_ipod_thread , args = ( widget , device , sync_all_episodes , episodes ) ) . start ( )
2009-08-24 01:22:36 +02:00
else :
device . close ( )
2008-07-09 03:43:01 +02:00
2009-04-27 14:54:02 +02:00
# The sync process might have updated the status of episodes,
# therefore persist the database here to avoid losing data
2009-08-24 18:11:58 +02:00
self . db . commit ( )
2009-04-27 14:54:02 +02:00
2008-07-20 04:55:30 +02:00
def sync_to_ipod_completed ( self , device , successful_sync ) :
device . unregister ( ' post-done ' , self . sync_to_ipod_completed )
2008-07-09 03:43:01 +02:00
2008-07-20 04:55:30 +02:00
if self . tray_icon :
self . tray_icon . release_synchronisation_device ( )
2008-01-28 12:38:53 +01:00
2008-07-20 04:55:30 +02:00
if not successful_sync :
2008-01-28 12:38:53 +01:00
title = _ ( ' Error closing device ' )
message = _ ( ' There has been an error closing your device. ' )
self . notification ( message , title )
2007-03-18 19:28:17 +01:00
2008-01-28 12:38:53 +01:00
# update model for played state updates after sync
util . idle_add ( self . updateComboBox )
2008-07-20 04:55:30 +02:00
def sync_to_ipod_thread ( self , widget , device , sync_all_episodes , episodes = None ) :
if sync_all_episodes :
device . add_tracks ( episodes )
# 'only_sync_not_played' must be used or else all the played
# tracks will be copied then immediately deleted
2009-08-24 18:11:58 +02:00
if self . config . mp3_player_delete_played and self . config . only_sync_not_played :
2008-07-20 04:55:30 +02:00
self . ipod_delete_played ( device )
else :
device . add_tracks ( episodes , force_played = True )
device . close ( )
2009-04-11 11:17:52 +02:00
self . update_selected_episode_list_icons ( )
2008-07-20 04:55:30 +02:00
2008-01-28 12:38:53 +01:00
def ipod_cleanup_callback ( self , device , tracks ) :
title = _ ( ' Delete podcasts from device? ' )
2008-09-10 16:11:44 +02:00
message = _ ( ' The selected episodes will be removed from your device. This cannot be undone. Files in your gPodder library will be unaffected. Do you really want to delete these episodes from your device? ' )
2008-01-28 12:38:53 +01:00
if len ( tracks ) > 0 and self . show_confirmation ( message , title ) :
2009-08-24 19:50:13 +02:00
gPodderSyncProgress ( self . gPodder , device = device , gPodder = self )
2009-01-05 14:07:38 +01:00
Thread ( target = self . ipod_cleanup_thread , args = [ device , tracks ] ) . start ( )
def ipod_cleanup_thread ( self , device , tracks ) :
device . remove_tracks ( tracks )
2008-01-28 12:38:53 +01:00
if not device . close ( ) :
title = _ ( ' Error closing device ' )
message = _ ( ' There has been an error closing your device. ' )
2009-01-05 14:07:38 +01:00
gobject . idle_add ( self . show_message , message , title )
2006-04-07 03:43:06 +02:00
2006-04-06 16:11:03 +02:00
def on_cleanup_ipod_activate ( self , widget , * args ) :
2008-01-28 12:38:53 +01:00
columns = (
2008-08-13 03:47:01 +02:00
( ' title ' , None , None , _ ( ' Episode ' ) ) ,
( ' podcast ' , None , None , _ ( ' Podcast ' ) ) ,
( ' filesize ' , None , None , _ ( ' Size ' ) ) ,
2009-05-26 13:56:49 +02:00
( ' modified ' , ' modified_sort ' , gobject . TYPE_INT , _ ( ' Copied ' ) ) ,
2008-08-13 03:47:01 +02:00
( ' playcount ' , None , None , _ ( ' Play count ' ) ) ,
( ' released ' , None , None , _ ( ' Released ' ) ) ,
2008-01-28 12:38:53 +01:00
)
2009-08-24 18:11:58 +02:00
device = sync . open_device ( self . config )
2008-01-28 12:38:53 +01:00
if device is None :
2007-03-10 18:42:32 +01:00
title = _ ( ' No device configured ' )
message = _ ( ' To use the synchronization feature, please configure your device in the preferences dialog first. ' )
2008-01-28 12:38:53 +01:00
self . show_message ( message , title )
2007-03-18 19:28:17 +01:00
return
2008-01-28 12:38:53 +01:00
if not device . open ( ) :
title = _ ( ' Cannot open device ' )
message = _ ( ' There has been an error opening your device. ' )
self . show_message ( message , title )
2007-03-18 19:28:17 +01:00
return
2008-01-28 12:38:53 +01:00
tracks = device . get_all_tracks ( )
if len ( tracks ) > 0 :
remove_tracks_callback = lambda tracks : self . ipod_cleanup_callback ( device , tracks )
2008-04-19 19:01:09 +02:00
wanted_columns = [ ]
2008-08-13 03:47:01 +02:00
for key , sort_name , sort_type , caption in columns :
2008-04-19 19:01:09 +02:00
want_this_column = False
for track in tracks :
if getattr ( track , key ) is not None :
want_this_column = True
break
if want_this_column :
2008-08-13 03:47:01 +02:00
wanted_columns . append ( ( key , sort_name , sort_type , caption ) )
2008-01-28 12:38:53 +01:00
title = _ ( ' Remove podcasts from device ' )
instructions = _ ( ' Select the podcast episodes you want to remove from your device. ' )
2009-08-24 18:37:04 +02:00
gPodderEpisodeSelector ( self . gPodder , title = title , instructions = instructions , episodes = tracks , columns = wanted_columns , \
2009-08-24 18:11:58 +02:00
stock_ok_button = gtk . STOCK_DELETE , callback = remove_tracks_callback , tooltip_attribute = None , \
_config = self . config )
2008-01-28 12:38:53 +01:00
else :
title = _ ( ' No files on device ' )
message = _ ( ' The devices contains no files to be removed. ' )
self . show_message ( message , title )
2008-09-10 16:11:44 +02:00
device . close ( )
2006-04-06 16:11:03 +02:00
2008-11-05 08:15:23 +01:00
def on_manage_device_playlist ( self , widget ) :
# make sure gpod is available before even trying to sync
2009-08-24 18:11:58 +02:00
if self . config . device_type == ' ipod ' and not sync . gpod_available :
2008-11-05 08:15:23 +01:00
title = _ ( ' Cannot manage iPod playlist ' )
message = _ ( ' This feature is not available for iPods. ' )
self . notification ( message , title )
return
2009-08-24 18:11:58 +02:00
elif self . config . device_type == ' mtp ' and not sync . pymtp_available :
2008-11-05 08:15:23 +01:00
title = _ ( ' Cannot manage MTP device playlist ' )
message = _ ( ' This feature is not available for MTP devices. ' )
self . notification ( message , title )
return
2009-08-24 18:11:58 +02:00
device = sync . open_device ( self . config )
2008-11-05 08:15:23 +01:00
if device is None :
title = _ ( ' No device configured ' )
message = _ ( ' To use the playlist feature, please configure your Filesystem based MP3-Player in the preferences dialog first. ' )
self . notification ( message , title )
return
if not device . open ( ) :
title = _ ( ' Cannot open device ' )
message = _ ( ' There has been an error opening your device. ' )
self . notification ( message , title )
return
2009-08-24 20:20:51 +02:00
gPodderDevicePlaylist ( self . gPodder , device = device , gPodder = self , _config = self . config )
2008-11-05 08:15:23 +01:00
device . close ( )
2008-01-09 23:46:17 +01:00
def show_hide_tray_icon ( self ) :
2009-08-24 18:11:58 +02:00
if self . config . display_tray_icon and have_trayicon and self . tray_icon is None :
self . tray_icon = trayicon . GPodderStatusIcon ( self , gpodder . icon_file , self . config )
elif not self . config . display_tray_icon and self . tray_icon is not None :
2008-01-09 23:46:17 +01:00
self . tray_icon . set_visible ( False )
del self . tray_icon
self . tray_icon = None
2009-08-24 18:11:58 +02:00
if self . config . minimize_to_tray and self . tray_icon :
2008-01-09 23:46:17 +01:00
self . tray_icon . set_visible ( self . minimized )
elif self . tray_icon :
self . tray_icon . set_visible ( True )
2008-02-06 10:29:56 +01:00
def on_itemShowToolbar_activate ( self , widget ) :
2009-08-24 18:11:58 +02:00
self . config . show_toolbar = self . itemShowToolbar . get_active ( )
2008-02-06 10:29:56 +01:00
def on_itemShowDescription_activate ( self , widget ) :
2009-08-24 18:11:58 +02:00
self . config . episode_list_descriptions = self . itemShowDescription . get_active ( )
2008-02-06 10:29:56 +01:00
2007-11-08 20:11:57 +01:00
def update_item_device ( self ) :
2009-08-24 18:11:58 +02:00
if self . config . device_type != ' none ' :
2009-05-08 14:28:53 +02:00
self . itemDevice . set_visible ( True )
2009-08-10 23:14:35 +02:00
self . itemDevice . label = self . get_device_name ( )
2007-11-08 20:11:57 +01:00
else :
2009-05-08 14:28:53 +02:00
self . itemDevice . set_visible ( False )
2007-11-08 20:11:57 +01:00
def properties_closed ( self ) :
2008-01-09 23:46:17 +01:00
self . show_hide_tray_icon ( )
2007-11-08 20:11:57 +01:00
self . update_item_device ( )
self . updateComboBox ( )
2005-11-21 19:21:25 +01:00
def on_itemPreferences_activate ( self , widget , * args ) :
2009-08-24 19:31:58 +02:00
gPodderPreferences ( self . gPodder , _config = self . config , \
callback_finished = self . properties_closed , \
user_apps_reader = self . user_apps_reader )
2005-11-21 19:21:25 +01:00
2008-10-14 19:27:10 +02:00
def on_itemDependencies_activate ( self , widget ) :
2009-08-24 18:37:04 +02:00
gPodderDependencyManager ( self . gPodder )
2008-10-14 19:27:10 +02:00
2008-08-04 16:54:18 +02:00
def on_add_new_google_search ( self , widget , * args ) :
def add_google_video_search ( query ) :
self . add_new_channel ( ' http://video.google.com/videofeed?type=search&q= ' + urllib . quote ( query ) + ' &so=1&num=250&output=rss ' )
2009-08-24 19:10:52 +02:00
gPodderAddPodcast ( self . gPodder , url_callback = add_google_video_search , custom_title = _ ( ' Add Google Video search ' ) , custom_label = _ ( ' Search for: ' ) )
2008-08-04 16:54:18 +02:00
2008-12-24 13:18:22 +01:00
def on_upgrade_from_videocenter ( self , widget ) :
from gpodder import nokiavideocenter
vc = nokiavideocenter . UpgradeFromVideocenter ( )
if vc . db2opml ( ) :
2009-08-24 20:04:13 +02:00
gPodderPodcastDirectory ( self . gPodder , _config = self . config , custom_title = _ ( ' Import podcasts from Video Center ' ) , hide_url_entry = True ) . get_channels_from_url ( vc . opmlfile , lambda url : self . add_new_channel ( url , False , block = True ) , lambda : self . on_itemDownloadAllNew_activate ( self . gPodder ) )
2008-12-24 13:18:22 +01:00
else :
self . show_message ( _ ( ' Have you installed Video Center on your tablet? ' ) , _ ( ' Cannot find Video Center subscriptions ' ) )
2008-12-10 23:07:10 +01:00
def require_my_gpodder_authentication ( self ) :
2009-08-24 18:11:58 +02:00
if not self . config . my_gpodder_username or not self . config . my_gpodder_password :
success , authentication = self . UsernamePasswordDialog ( _ ( ' Login to my.gpodder.org ' ) , _ ( ' Please enter your e-mail address and your password. ' ) , username = self . config . my_gpodder_username , password = self . config . my_gpodder_password , username_prompt = _ ( ' E-Mail Address ' ) , register_callback = lambda : util . open_website ( ' http://my.gpodder.org/register ' ) )
2008-12-10 23:07:10 +01:00
if success and authentication [ 0 ] and authentication [ 1 ] :
2009-08-24 18:11:58 +02:00
self . config . my_gpodder_username , self . config . my_gpodder_password = authentication
2008-12-10 23:07:10 +01:00
return True
2008-12-08 17:10:53 +01:00
else :
2008-12-10 23:07:10 +01:00
return False
2008-12-08 17:10:53 +01:00
2008-12-10 23:07:10 +01:00
return True
def my_gpodder_offer_autoupload ( self ) :
2009-08-24 18:11:58 +02:00
if not self . config . my_gpodder_autoupload :
2008-12-10 23:07:10 +01:00
if self . show_confirmation ( _ ( ' gPodder can automatically upload your subscription list to my.gpodder.org when you close it. Do you want to enable this feature? ' ) , _ ( ' Upload subscriptions on quit ' ) ) :
2009-08-24 18:11:58 +02:00
self . config . my_gpodder_autoupload = True
2008-12-10 23:07:10 +01:00
2008-12-11 14:24:29 +01:00
def on_download_from_mygpo ( self , widget ) :
2008-12-10 23:07:10 +01:00
if self . require_my_gpodder_authentication ( ) :
2009-08-24 18:11:58 +02:00
client = my . MygPodderClient ( self . config . my_gpodder_username , self . config . my_gpodder_password )
2008-12-10 23:07:10 +01:00
opml_data = client . download_subscriptions ( )
if len ( opml_data ) > 0 :
2009-08-11 00:36:38 +02:00
fp = open ( gpodder . subscription_file , ' w ' )
2008-12-10 23:07:10 +01:00
fp . write ( opml_data )
fp . close ( )
2008-12-11 14:24:29 +01:00
( added , skipped ) = ( 0 , 0 )
2009-08-11 00:36:38 +02:00
i = opml . Importer ( gpodder . subscription_file )
2008-12-10 23:07:10 +01:00
for item in i . items :
url = item [ ' url ' ]
2008-12-11 14:24:29 +01:00
if url not in ( c . url for c in self . channels ) :
self . add_new_channel ( url , ask_download_new = False , block = True )
added + = 1
else :
log ( ' Already added: %s ' , url , sender = self )
skipped + = 1
2008-12-10 23:07:10 +01:00
self . updateComboBox ( )
2008-12-11 14:24:29 +01:00
if added > 0 :
self . show_message ( _ ( ' Added %d new subscriptions and skipped %d existing ones. ' ) % ( added , skipped ) , _ ( ' Result of subscription download ' ) )
elif widget is not None :
self . show_message ( _ ( ' Your local subscription list is up to date. ' ) , _ ( ' Result of subscription download ' ) )
2008-12-10 23:07:10 +01:00
self . my_gpodder_offer_autoupload ( )
else :
2009-08-24 18:11:58 +02:00
self . config . my_gpodder_password = ' '
2008-12-11 14:54:36 +01:00
self . on_download_from_mygpo ( widget )
2008-12-08 17:10:53 +01:00
else :
self . show_message ( _ ( ' Please set up your username and password first. ' ) , _ ( ' Username and password needed ' ) )
2008-12-10 23:07:10 +01:00
def on_upload_to_mygpo ( self , widget ) :
if self . require_my_gpodder_authentication ( ) :
2009-08-24 18:11:58 +02:00
client = my . MygPodderClient ( self . config . my_gpodder_username , self . config . my_gpodder_password )
2009-08-13 20:39:00 +02:00
self . save_channels_opml ( )
2009-08-11 00:36:38 +02:00
success , messages = client . upload_subscriptions ( gpodder . subscription_file )
2008-12-10 23:07:10 +01:00
if widget is not None :
self . show_message ( ' \n ' . join ( messages ) , _ ( ' Results of upload ' ) )
if not success :
2009-08-24 18:11:58 +02:00
self . config . my_gpodder_password = ' '
2008-12-10 23:07:10 +01:00
self . on_upload_to_mygpo ( widget )
else :
self . my_gpodder_offer_autoupload ( )
elif not success :
log ( ' Upload to my.gpodder.org failed, but widget is None! ' , sender = self )
elif widget is not None :
self . show_message ( _ ( ' Please set up your username and password first. ' ) , _ ( ' Username and password needed ' ) )
2005-11-21 19:21:25 +01:00
def on_itemAddChannel_activate ( self , widget , * args ) :
2009-08-24 19:10:52 +02:00
gPodderAddPodcast ( self . gPodder , url_callback = self . add_new_channel )
2005-11-21 19:21:25 +01:00
def on_itemEditChannel_activate ( self , widget , * args ) :
2008-02-26 16:49:59 +01:00
if self . active_channel is None :
2008-05-02 17:36:43 +02:00
title = _ ( ' No podcast selected ' )
message = _ ( ' Please select a podcast in the podcasts list to edit. ' )
2007-03-10 18:42:32 +01:00
self . show_message ( message , title )
2006-04-06 16:11:03 +02:00
return
2007-07-11 20:28:09 +02:00
2009-08-24 18:37:04 +02:00
gPodderChannel ( self . main_window , channel = self . active_channel , callback_closed = lambda : self . updateComboBox ( only_selected_channel = True ) , cover_downloader = self . cover_downloader )
2008-03-03 23:09:34 +01:00
2005-11-21 19:21:25 +01:00
def on_itemRemoveChannel_activate ( self , widget , * args ) :
try :
2008-04-06 02:19:03 +02:00
if gpodder . interface == gpodder . GUI :
dialog = gtk . MessageDialog ( self . gPodder , gtk . DIALOG_MODAL , gtk . MESSAGE_QUESTION , gtk . BUTTONS_NONE )
dialog . add_button ( gtk . STOCK_NO , gtk . RESPONSE_NO )
dialog . add_button ( gtk . STOCK_YES , gtk . RESPONSE_YES )
2008-05-02 17:36:43 +02:00
title = _ ( ' Remove podcast and episodes? ' )
2008-04-06 02:19:03 +02:00
message = _ ( ' Do you really want to remove <b> %s </b> and all downloaded episodes? ' ) % saxutils . escape ( self . active_channel . title )
dialog . set_title ( title )
dialog . set_markup ( ' <span weight= " bold " size= " larger " > %s </span> \n \n %s ' % ( title , message ) )
cb_ask = gtk . CheckButton ( _ ( ' Do not delete my downloaded episodes ' ) )
dialog . vbox . pack_start ( cb_ask )
cb_ask . show_all ( )
affirmative = gtk . RESPONSE_YES
elif gpodder . interface == gpodder . MAEMO :
cb_ask = gtk . CheckButton ( ' ' ) # dummy check button
2008-05-02 17:36:43 +02:00
dialog = hildon . Note ( ' confirmation ' , ( self . gPodder , _ ( ' Do you really want to remove this podcast and all downloaded episodes? ' ) ) )
2008-04-06 02:19:03 +02:00
affirmative = gtk . RESPONSE_OK
2008-01-14 20:06:26 +01:00
result = dialog . run ( )
dialog . destroy ( )
2008-04-06 02:19:03 +02:00
if result == affirmative :
2008-01-14 20:06:26 +01:00
# delete downloaded episodes only if checkbox is unchecked
if cb_ask . get_active ( ) == False :
self . active_channel . remove_downloaded ( )
else :
log ( ' Not removing downloaded episodes ' , sender = self )
2009-04-01 13:34:19 +02:00
# Clean up downloads and download directories
2009-08-24 18:11:58 +02:00
self . clean_up_downloads ( )
2009-04-01 13:34:19 +02:00
2008-07-22 21:09:48 +02:00
# cancel any active downloads from this channel
2009-04-01 13:34:19 +02:00
for episode in self . active_channel . get_all_episodes ( ) :
2009-08-24 16:17:32 +02:00
self . download_status_model . cancel_by_url ( episode . url )
2008-05-14 15:38:06 +02:00
# get the URL of the podcast we want to select next
position = self . channels . index ( self . active_channel )
if position == len ( self . channels ) - 1 :
# this is the last podcast, so select the URL
# of the item before this one (i.e. the "new last")
select_url = self . channels [ position - 1 ] . url
else :
# there is a podcast after the deleted one, so
# we simply select the one that comes after it
select_url = self . channels [ position + 1 ] . url
# Remove the channel
2008-06-30 03:10:18 +02:00
self . active_channel . delete ( )
2008-01-14 20:06:26 +01:00
self . channels . remove ( self . active_channel )
2008-12-13 13:29:45 +01:00
self . channel_list_changed = True
2009-08-13 20:39:00 +02:00
self . save_channels_opml ( )
2008-05-14 15:38:06 +02:00
# Re-load the channels and select the desired new channel
self . update_feed_cache ( force_update = False , select_url_afterwards = select_url )
2005-11-21 19:21:25 +01:00
except :
2008-04-22 22:24:19 +02:00
log ( ' There has been an error removing the channel. ' , traceback = True , sender = self )
2008-07-11 19:13:04 +02:00
self . update_podcasts_tab ( )
2005-11-21 19:21:25 +01:00
2008-05-02 17:36:43 +02:00
def get_opml_filter ( self ) :
filter = gtk . FileFilter ( )
filter . add_pattern ( ' *.opml ' )
filter . add_pattern ( ' *.xml ' )
filter . set_name ( _ ( ' OPML files ' ) + ' (*.opml, *.xml) ' )
return filter
2008-06-14 15:17:55 +02:00
def on_item_import_from_file_activate ( self , widget , filename = None ) :
if filename is None :
if gpodder . interface == gpodder . GUI :
dlg = gtk . FileChooserDialog ( title = _ ( ' Import from OPML ' ) , parent = None , action = gtk . FILE_CHOOSER_ACTION_OPEN )
dlg . add_button ( gtk . STOCK_CANCEL , gtk . RESPONSE_CANCEL )
dlg . add_button ( gtk . STOCK_OPEN , gtk . RESPONSE_OK )
elif gpodder . interface == gpodder . MAEMO :
dlg = hildon . FileChooserDialog ( self . gPodder , gtk . FILE_CHOOSER_ACTION_OPEN )
dlg . set_filter ( self . get_opml_filter ( ) )
response = dlg . run ( )
filename = None
if response == gtk . RESPONSE_OK :
filename = dlg . get_filename ( )
dlg . destroy ( )
2008-05-02 17:36:43 +02:00
if filename is not None :
2009-08-24 20:04:13 +02:00
gPodderPodcastDirectory ( self . gPodder , _config = self . config , custom_title = _ ( ' Import podcasts from OPML file ' ) , hide_url_entry = True ) . get_channels_from_url ( filename , lambda url : self . add_new_channel ( url , False , block = True ) , lambda : self . on_itemDownloadAllNew_activate ( self . gPodder ) )
2008-05-02 17:36:43 +02:00
2005-11-21 19:21:25 +01:00
def on_itemExportChannels_activate ( self , widget , * args ) :
2006-12-09 02:59:53 +01:00
if not self . channels :
2007-03-10 18:42:32 +01:00
title = _ ( ' Nothing to export ' )
2008-05-02 17:36:43 +02:00
message = _ ( ' Your list of podcast subscriptions is empty. Please subscribe to some podcasts first before trying to export your subscription list. ' )
2007-03-10 18:42:32 +01:00
self . show_message ( message , title )
2006-12-09 02:59:53 +01:00
return
2008-05-02 17:36:43 +02:00
if gpodder . interface == gpodder . GUI :
dlg = gtk . FileChooserDialog ( title = _ ( ' Export to OPML ' ) , parent = self . gPodder , action = gtk . FILE_CHOOSER_ACTION_SAVE )
dlg . add_button ( gtk . STOCK_CANCEL , gtk . RESPONSE_CANCEL )
dlg . add_button ( gtk . STOCK_SAVE , gtk . RESPONSE_OK )
elif gpodder . interface == gpodder . MAEMO :
dlg = hildon . FileChooserDialog ( self . gPodder , gtk . FILE_CHOOSER_ACTION_SAVE )
dlg . set_filter ( self . get_opml_filter ( ) )
2005-12-08 20:47:35 +01:00
response = dlg . run ( )
if response == gtk . RESPONSE_OK :
2007-08-19 16:28:24 +02:00
filename = dlg . get_filename ( )
2008-09-10 16:02:06 +02:00
dlg . destroy ( )
2007-08-19 16:28:24 +02:00
exporter = opml . Exporter ( filename )
2008-09-10 16:02:06 +02:00
if exporter . write ( self . channels ) :
if len ( self . channels ) == 1 :
title = _ ( ' One subscription exported ' )
else :
title = _ ( ' %d subscriptions exported ' ) % len ( self . channels )
self . show_message ( _ ( ' Your podcast list has been successfully exported. ' ) , title )
else :
2007-08-19 16:28:24 +02:00
self . show_message ( _ ( ' Could not export OPML to file. Please check your permissions. ' ) , _ ( ' OPML export failed ' ) )
2008-09-10 16:02:06 +02:00
else :
dlg . destroy ( )
2005-11-21 19:21:25 +01:00
2006-06-13 23:00:31 +02:00
def on_itemImportChannels_activate ( self , widget , * args ) :
2009-08-24 20:04:13 +02:00
gPodderPodcastDirectory ( self . gPodder , _config = self . config ) . get_channels_from_url ( self . config . opml_url , lambda url : self . add_new_channel ( url , False , block = True ) , lambda : self . on_itemDownloadAllNew_activate ( self . gPodder ) )
2006-06-13 23:00:31 +02:00
2006-12-04 14:06:42 +01:00
def on_homepage_activate ( self , widget , * args ) :
2009-05-07 16:26:07 +02:00
util . open_website ( gpodder . __url__ )
2006-12-04 14:06:42 +01:00
2007-12-13 08:05:25 +01:00
def on_wiki_activate ( self , widget , * args ) :
2008-03-03 20:38:22 +01:00
util . open_website ( ' http://wiki.gpodder.org/ ' )
2006-12-04 14:06:42 +01:00
2008-01-14 20:23:49 +01:00
def on_bug_tracker_activate ( self , widget , * args ) :
2009-05-31 13:20:43 +02:00
if gpodder . interface == gpodder . MAEMO :
util . open_website ( ' http://bugs.maemo.org/enter_bug.cgi?product=gPodder ' )
else :
util . open_website ( ' http://bugs.gpodder.org/ ' )
2008-01-14 20:23:49 +01:00
2009-05-31 11:45:54 +02:00
def on_shop_activate ( self , widget , * args ) :
util . open_website ( ' http://gpodder.org/shop ' )
def on_wishlist_activate ( self , widget , * args ) :
util . open_website ( ' http://www.amazon.de/gp/registry/2PD2MYGHE6857 ' )
2005-11-21 19:21:25 +01:00
def on_itemAbout_activate ( self , widget , * args ) :
dlg = gtk . AboutDialog ( )
2009-05-07 16:26:07 +02:00
dlg . set_name ( ' gPodder ' )
dlg . set_version ( gpodder . __version__ )
dlg . set_copyright ( gpodder . __copyright__ )
dlg . set_website ( gpodder . __url__ )
2006-04-06 16:11:03 +02:00
dlg . set_translator_credits ( _ ( ' translator-credits ' ) )
2007-08-28 00:18:01 +02:00
dlg . connect ( ' response ' , lambda dlg , response : dlg . destroy ( ) )
2006-12-04 14:06:42 +01:00
2008-04-06 02:19:03 +02:00
if gpodder . interface == gpodder . GUI :
# For the "GUI" version, we add some more
# items to the about dialog (credits and logo)
dlg . set_authors ( app_authors )
try :
2009-05-07 16:26:07 +02:00
dlg . set_logo ( gtk . gdk . pixbuf_new_from_file ( gpodder . icon_file ) )
2008-04-06 02:19:03 +02:00
except :
2009-05-07 16:26:07 +02:00
dlg . set_logo_icon_name ( ' gpodder ' )
2006-12-04 14:06:42 +01:00
2005-11-21 19:21:25 +01:00
dlg . run ( )
2006-11-29 00:23:16 +01:00
2006-02-04 18:29:17 +01:00
def on_wNotebook_switch_page ( self , widget , * args ) :
2007-03-31 04:00:30 +02:00
page_num = args [ 1 ]
2008-04-06 02:19:03 +02:00
if gpodder . interface == gpodder . MAEMO :
2009-05-11 23:10:56 +02:00
self . tool_downloads . set_active ( page_num == 1 )
2008-04-06 02:19:03 +02:00
page = self . wNotebook . get_nth_page ( page_num )
2008-04-24 18:28:39 +02:00
tab_label = self . wNotebook . get_tab_label ( page ) . get_text ( )
if page_num == 0 and self . active_channel is not None :
self . set_title ( self . active_channel . title )
else :
self . set_title ( tab_label )
2007-03-31 04:00:30 +02:00
if page_num == 0 :
self . play_or_download ( )
2009-04-01 01:12:17 +02:00
self . menuChannels . set_sensitive ( True )
self . menuSubscriptions . set_sensitive ( True )
# The message area in the downloads tab should be hidden
# when the user switches away from the downloads tab
if self . message_area is not None :
self . message_area . hide ( )
self . message_area = None
2007-03-31 04:00:30 +02:00
else :
2009-04-01 01:12:17 +02:00
self . menuChannels . set_sensitive ( False )
self . menuSubscriptions . set_sensitive ( False )
2007-03-31 04:00:30 +02:00
self . toolDownload . set_sensitive ( False )
self . toolPlay . set_sensitive ( False )
self . toolTransfer . set_sensitive ( False )
2009-08-24 16:17:32 +02:00
self . toolCancel . set_sensitive ( False ) #services.download_status_model.has_items())
2006-02-04 18:29:17 +01:00
2009-03-13 20:08:57 +01:00
def on_treeChannels_row_activated ( self , widget , path , * args ) :
# double-click action of the podcast list or enter
self . treeChannels . set_cursor ( path )
2007-07-05 23:07:16 +02:00
def on_treeChannels_cursor_changed ( self , widget , * args ) :
2007-09-19 17:04:42 +02:00
( model , iter ) = self . treeChannels . get_selection ( ) . get_selected ( )
2007-07-11 13:05:02 +02:00
2008-12-13 13:29:45 +01:00
if model is not None and iter is not None :
old_active_channel = self . active_channel
2009-08-13 23:19:12 +02:00
self . active_channel = model . get_value ( iter , PodcastListModel . C_CHANNEL )
2006-12-09 02:59:53 +01:00
2008-12-13 13:29:45 +01:00
if self . active_channel == old_active_channel :
return
2008-04-06 02:19:03 +02:00
if gpodder . interface == gpodder . MAEMO :
self . set_title ( self . active_channel . title )
2009-05-08 14:28:53 +02:00
self . itemEditChannel . set_visible ( True )
self . itemRemoveChannel . set_visible ( True )
self . channel_toggle_lock . set_visible ( True )
2008-11-19 17:05:19 +01:00
if self . active_channel . channel_is_locked :
self . change_menu_item ( self . channel_toggle_lock , gtk . STOCK_DIALOG_AUTHENTICATION , _ ( ' Allow deletion of all episodes ' ) )
else :
self . change_menu_item ( self . channel_toggle_lock , gtk . STOCK_DIALOG_AUTHENTICATION , _ ( ' Prohibit deletion of all episodes ' ) )
2006-12-16 18:44:13 +01:00
else :
2007-09-19 17:04:42 +02:00
self . active_channel = None
2009-05-08 14:28:53 +02:00
self . itemEditChannel . set_visible ( False )
self . itemRemoveChannel . set_visible ( False )
self . channel_toggle_lock . set_visible ( False )
2006-12-16 18:44:13 +01:00
2008-12-13 13:29:45 +01:00
self . updateTreeView ( )
2005-11-21 19:21:25 +01:00
2007-09-19 17:04:42 +02:00
def on_entryAddChannel_changed ( self , widget , * args ) :
2008-05-02 17:36:43 +02:00
active = self . entryAddChannel . get_text ( ) not in ( ' ' , self . ENTER_URL_TEXT )
2007-09-19 17:04:42 +02:00
self . btnAddChannel . set_sensitive ( active )
def on_btnAddChannel_clicked ( self , widget , * args ) :
url = self . entryAddChannel . get_text ( )
self . entryAddChannel . set_text ( ' ' )
self . add_new_channel ( url )
2006-03-04 21:45:01 +01:00
def on_btnEditChannel_clicked ( self , widget , * args ) :
self . on_itemEditChannel_activate ( widget , args )
2009-07-13 14:09:43 +02:00
def get_selected_episodes ( self ) :
""" Get a list of selected episodes from treeAvailable """
selection = self . treeAvailable . get_selection ( )
model , paths = selection . get_selected_rows ( )
2007-03-18 19:28:17 +01:00
2009-08-13 23:19:12 +02:00
episodes = [ model . get_value ( model . get_iter ( path ) , EpisodeListModel . C_EPISODE ) for path in paths ]
2009-07-13 14:09:43 +02:00
return episodes
2008-12-14 17:30:17 +01:00
2009-07-13 14:09:43 +02:00
def on_transfer_selected_episodes ( self , widget ) :
self . on_sync_to_ipod_activate ( widget , self . get_selected_episodes ( ) )
2007-03-18 19:28:17 +01:00
2009-07-13 14:09:43 +02:00
def on_playback_selected_episodes ( self , widget ) :
2009-07-13 15:32:46 +02:00
self . playback_episodes ( self . get_selected_episodes ( ) )
2007-03-18 19:28:17 +01:00
2009-07-13 14:09:43 +02:00
def on_shownotes_selected_episodes ( self , widget ) :
episodes = self . get_selected_episodes ( )
if episodes :
episode = episodes . pop ( 0 )
self . show_episode_shownotes ( episode )
else :
self . show_message ( _ ( ' No episode selected ' ) , _ ( ' Please select an episode ' ) )
def on_download_selected_episodes ( self , widget ) :
episodes = self . get_selected_episodes ( )
self . download_episode_list ( episodes )
self . update_episode_list_icons ( [ episode . url for episode in episodes ] )
self . play_or_download ( )
def on_treeAvailable_row_activated ( self , widget , path , view_column ) :
""" Double-click/enter action handler for treeAvailable """
2009-07-13 23:32:14 +02:00
# We should only have one one selected as it was double clicked!
e = self . get_selected_episodes ( ) [ 0 ]
2009-08-24 18:11:58 +02:00
if ( self . config . double_click_episode_action == ' download ' ) :
2009-07-13 23:32:14 +02:00
# If the episode has already been downloaded and exists then play it
if e . was_downloaded ( and_exists = True ) :
self . playback_episodes ( self . get_selected_episodes ( ) )
# else download it if it is not already downloading
elif not self . episode_is_downloading ( e ) :
self . download_episode_list ( [ e ] )
self . update_episode_list_icons ( [ e . url ] )
self . play_or_download ( )
2009-08-24 18:11:58 +02:00
elif ( self . config . double_click_episode_action == ' stream ' ) :
2009-07-13 23:32:14 +02:00
# If we happen to have downloaded this episode simple play it
if e . was_downloaded ( and_exists = True ) :
self . playback_episodes ( self . get_selected_episodes ( ) )
# else if streaming is possible stream it
2009-08-24 18:11:58 +02:00
elif self . streaming_possible ( ) :
2009-07-13 23:32:14 +02:00
self . playback_episodes ( self . get_selected_episodes ( ) )
else :
log ( ' Unable to stream episode - default media player selected! ' , sender = self , traceback = True )
self . show_message ( _ ( ' Please check your media player settings in the preferences dialog. ' ) , _ ( ' Unable to stream episode ' ) )
else :
# default action is to display show notes
self . on_shownotes_selected_episodes ( widget )
2005-11-21 19:21:25 +01:00
2009-05-12 10:36:01 +02:00
def show_episode_shownotes ( self , episode ) :
2009-07-13 15:32:46 +02:00
play_callback = lambda : self . playback_episodes ( [ episode ] )
2009-05-12 10:36:01 +02:00
def download_callback ( ) :
self . download_episode_list ( [ episode ] )
self . play_or_download ( )
2009-08-24 19:43:49 +02:00
if self . episode_shownotes_window is None :
2009-05-12 10:36:01 +02:00
log ( ' First-time use of episode window --- creating ' , sender = self )
2009-08-24 19:43:49 +02:00
self . episode_shownotes_window = gPodderShownotes ( self . gPodder , _config = self . config , \
2009-08-24 16:17:32 +02:00
download_status_model = self . download_status_model , \
2009-05-12 10:36:01 +02:00
episode_is_downloading = self . episode_is_downloading )
2009-08-24 19:43:49 +02:00
self . episode_shownotes_window . show ( episode = episode , download_callback = download_callback , play_callback = play_callback )
2009-05-12 10:36:01 +02:00
2006-12-13 00:11:34 +01:00
def on_treeAvailable_button_release_event ( self , widget , * args ) :
self . play_or_download ( )
2008-01-09 23:46:17 +01:00
def auto_update_procedure ( self , first_run = False ) :
log ( ' auto_update_procedure() got called ' , sender = self )
2009-08-24 18:11:58 +02:00
if not first_run and self . config . auto_update_feeds and self . minimized :
2008-06-30 03:10:18 +02:00
self . update_feed_cache ( force_update = True )
2008-01-09 23:46:17 +01:00
2009-08-24 18:11:58 +02:00
next_update = 60 * 1000 * self . config . auto_update_frequency
2008-01-09 23:46:17 +01:00
gobject . timeout_add ( next_update , self . auto_update_procedure )
2006-07-20 15:19:19 +02:00
2005-11-23 20:53:18 +01:00
def on_treeDownloads_row_activated ( self , widget , * args ) :
2009-04-01 01:12:17 +02:00
if self . wNotebook . get_current_page ( ) == 0 :
2007-08-25 18:05:03 +02:00
# Use the available podcasts treeview + model
2009-04-01 01:12:17 +02:00
selection = self . treeAvailable . get_selection ( )
( model , paths ) = selection . get_selected_rows ( )
urls = [ model . get_value ( model . get_iter ( path ) , 0 ) for path in paths ]
selected_tasks = [ task for task in self . download_tasks_seen if task . url in urls ]
for task in selected_tasks :
task . status = task . CANCELLED
self . update_selected_episode_list_icons ( )
self . play_or_download ( )
2007-08-25 18:05:03 +02:00
return
2009-04-01 01:12:17 +02:00
# Use the standard way of working on the treeview
selection = self . treeDownloads . get_selection ( )
( model , paths ) = selection . get_selected_rows ( )
selected_tasks = [ ( gtk . TreeRowReference ( model , path ) , model . get_value ( model . get_iter ( path ) , 0 ) ) for path in paths ]
for tree_row_reference , task in selected_tasks :
if task . status in ( task . DOWNLOADING , task . QUEUED ) :
task . status = task . PAUSED
elif task . status in ( task . CANCELLED , task . PAUSED , task . FAILED ) :
self . download_queue_manager . add_task ( task )
2009-08-17 21:46:17 +02:00
self . enable_download_list_update ( )
2009-04-01 01:12:17 +02:00
elif task . status == task . DONE :
model . remove ( model . get_iter ( tree_row_reference . get_path ( ) ) )
self . play_or_download ( )
2005-11-23 20:53:18 +01:00
2009-08-24 00:43:55 +02:00
# Update the tab title and downloads list
self . update_downloads_list ( )
2005-11-23 20:53:18 +01:00
def on_btnCancelDownloadStatus_clicked ( self , widget , * args ) :
self . on_treeDownloads_row_activated ( widget , None )
2006-07-20 15:19:19 +02:00
def on_btnCancelAll_clicked ( self , widget , * args ) :
self . treeDownloads . get_selection ( ) . select_all ( )
2007-03-31 04:00:30 +02:00
self . on_treeDownloads_row_activated ( self . toolCancel , None )
2006-07-20 15:19:19 +02:00
self . treeDownloads . get_selection ( ) . unselect_all ( )
2009-08-24 00:43:55 +02:00
# Update the tab title and downloads list
self . update_downloads_list ( )
2006-03-24 20:08:59 +01:00
def on_btnDownloadedDelete_clicked ( self , widget , * args ) :
2008-02-26 16:49:59 +01:00
if self . active_channel is None :
2007-08-26 17:20:46 +02:00
return
2009-07-13 14:34:43 +02:00
if self . wNotebook . get_current_page ( ) == 1 :
# Downloads tab visible - no action!
return
2009-08-10 23:14:35 +02:00
episodes = self . get_selected_episodes ( )
2008-06-30 03:10:18 +02:00
2009-08-10 23:14:35 +02:00
if not episodes :
log ( ' Nothing selected - will not remove any downloaded episode. ' )
2006-07-14 22:09:00 +02:00
return
2006-07-14 21:10:47 +02:00
2009-08-10 23:14:35 +02:00
if len ( episodes ) == 1 :
episode = episodes [ 0 ]
if episode . is_locked :
title = _ ( ' %s is locked ' ) % saxutils . escape ( episode . title )
2007-12-12 19:50:52 +01:00
message = _ ( ' You cannot delete this locked episode. You must unlock it before you can delete it. ' )
self . notification ( message , title )
return
2009-08-10 23:14:35 +02:00
title = _ ( ' Remove %s ? ' ) % saxutils . escape ( episode . title )
2007-03-10 18:42:32 +01:00
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. " )
2007-09-09 18:49:56 +02:00
else :
2009-08-10 23:14:35 +02:00
title = _ ( ' Remove %d episodes? ' ) % len ( episodes )
2007-09-09 18:49:56 +02:00
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. ' )
2007-12-12 19:50:52 +01:00
2009-08-24 13:22:23 +02:00
locked_count = sum ( int ( e . is_locked ) for e in episodes if e . is_locked is not None )
2007-12-12 19:50:52 +01:00
2009-08-10 23:14:35 +02:00
if len ( episodes ) == locked_count :
2007-12-12 19:50:52 +01:00
title = _ ( ' Episodes are locked ' )
message = _ ( ' The selected episodes are locked. Please unlock the episodes that you want to delete before trying to delete them. ' )
self . notification ( message , title )
return
elif locked_count > 0 :
2009-08-10 23:14:35 +02:00
title = _ ( ' Remove %d out of %d episodes? ' ) % ( len ( episodes ) - locked_count , len ( episodes ) )
2008-12-23 18:42:52 +01:00
message = _ ( ' The selection contains locked episodes that will not be deleted. If you want to listen to the deleted episodes, you will have to re-download them. ' )
2009-08-24 01:06:22 +02:00
2006-07-14 21:10:47 +02:00
# if user confirms deletion, let's remove some stuff ;)
2009-08-10 23:14:35 +02:00
if self . show_confirmation ( message , title ) :
for episode in episodes :
if not episode . is_locked :
episode . delete_from_disk ( )
self . updateComboBox ( only_selected_channel = True )
2007-09-09 18:49:56 +02:00
2007-02-03 11:48:32 +01:00
# only delete partial files if we do not have any downloads in progress
2009-08-24 18:11:58 +02:00
self . clean_up_downloads ( False )
2008-12-13 14:41:32 +01:00
self . update_selected_episode_list_icons ( )
self . play_or_download ( )
2006-03-24 20:08:59 +01:00
2008-04-06 02:19:03 +02:00
def on_key_press ( self , widget , event ) :
2008-09-27 14:20:43 +02:00
# Allow tab switching with Ctrl + PgUp/PgDown
if event . state & gtk . gdk . CONTROL_MASK :
if event . keyval == gtk . keysyms . Page_Up :
self . wNotebook . prev_page ( )
return True
elif event . keyval == gtk . keysyms . Page_Down :
self . wNotebook . next_page ( )
return True
# After this code we only handle Maemo hardware keys,
# so if we are not a Maemo app, we don't do anything
2008-04-06 02:19:03 +02:00
if gpodder . interface != gpodder . MAEMO :
2008-09-27 14:20:43 +02:00
return False
2008-04-06 02:19:03 +02:00
if event . keyval == gtk . keysyms . F6 :
if self . fullscreen :
self . window . unfullscreen ( )
else :
self . window . fullscreen ( )
if event . keyval == gtk . keysyms . Escape :
new_visibility = not self . vboxChannelNavigator . get_property ( ' visible ' )
self . vboxChannelNavigator . set_property ( ' visible ' , new_visibility )
self . column_size . set_visible ( not new_visibility )
self . column_released . set_visible ( not new_visibility )
diff = 0
if event . keyval == gtk . keysyms . F7 : #plus
diff = 1
elif event . keyval == gtk . keysyms . F8 : #minus
diff = - 1
2009-01-19 19:00:24 +01:00
if diff != 0 and not self . currently_updating :
2008-04-06 02:19:03 +02:00
selection = self . treeChannels . get_selection ( )
( model , iter ) = selection . get_selected ( )
2009-01-19 19:00:24 +01:00
new_path = ( ( model . get_path ( iter ) [ 0 ] + diff ) % len ( model ) , )
selection . select_path ( new_path )
self . treeChannels . set_cursor ( new_path )
return True
return False
2008-01-09 23:46:17 +01:00
def window_state_event ( self , widget , event ) :
2008-04-06 02:19:03 +02:00
if event . new_window_state & gtk . gdk . WINDOW_STATE_FULLSCREEN :
self . fullscreen = True
else :
self . fullscreen = False
2008-01-09 23:46:17 +01:00
old_minimized = self . minimized
2008-09-01 20:42:00 +02:00
self . minimized = bool ( event . new_window_state & gtk . gdk . WINDOW_STATE_ICONIFIED )
if gpodder . interface == gpodder . MAEMO :
self . minimized = bool ( event . new_window_state & gtk . gdk . WINDOW_STATE_WITHDRAWN )
2007-07-11 13:05:02 +02:00
2008-01-09 23:46:17 +01:00
if old_minimized != self . minimized and self . tray_icon :
self . gPodder . set_skip_taskbar_hint ( self . minimized )
elif not self . tray_icon :
self . gPodder . set_skip_taskbar_hint ( False )
2009-08-24 18:11:58 +02:00
if self . config . minimize_to_tray and self . tray_icon :
2008-01-09 23:46:17 +01:00
self . tray_icon . set_visible ( self . minimized )
def uniconify_main_window ( self ) :
if self . minimized :
self . gPodder . present ( )
def iconify_main_window ( self ) :
if not self . minimized :
2008-07-11 19:13:04 +02:00
self . gPodder . iconify ( )
def update_podcasts_tab ( self ) :
if len ( self . channels ) :
self . label2 . set_text ( _ ( ' Podcasts ( %d ) ' ) % len ( self . channels ) )
else :
self . label2 . set_text ( _ ( ' Podcasts ' ) )
2007-07-11 13:05:02 +02:00
2009-02-25 14:57:45 +01:00
@dbus.service.method ( gpodder . dbus_interface )
def show_gui_window ( self ) :
self . gPodder . present ( )
2005-11-21 19:21:25 +01:00
2007-08-28 00:18:01 +02:00
def main ( ) :
2006-04-03 22:59:10 +02:00
gobject . threads_init ( )
2007-03-25 21:55:28 +02:00
gtk . window_set_default_icon_name ( ' gpodder ' )
2005-11-21 19:21:25 +01:00
2009-05-30 11:31:02 +02:00
try :
session_bus = dbus . SessionBus ( mainloop = dbus . glib . DBusGMainLoop ( ) )
bus_name = dbus . service . BusName ( gpodder . dbus_bus_name , bus = session_bus )
except dbus . exceptions . DBusException , dbe :
log ( ' Warning: Cannot get " on the bus " . ' , traceback = True )
dlg = gtk . MessageDialog ( None , gtk . DIALOG_MODAL , gtk . MESSAGE_ERROR , \
gtk . BUTTONS_CLOSE , _ ( ' Cannot start gPodder ' ) )
dlg . format_secondary_markup ( _ ( ' D-Bus error: %s ' ) % ( str ( dbe ) , ) )
dlg . set_title ( ' gPodder ' )
dlg . run ( )
dlg . destroy ( )
sys . exit ( 0 )
2009-05-11 21:35:33 +02:00
2009-08-24 18:11:58 +02:00
util . make_directory ( gpodder . home )
config = UIConfig ( gpodder . config_file )
if gpodder . interface == gpodder . MAEMO :
# Detect changing of SD cards between mmc1/mmc2 if a gpodder
# folder exists there (allow moving "gpodder" between SD cards or USB)
# Also allow moving "gpodder" to home folder (e.g. rootfs on SD)
if not os . path . exists ( config . download_dir ) :
log ( ' Downloads might have been moved. Trying to locate them... ' )
for basedir in [ ' /media/mmc1 ' , ' /media/mmc2 ' ] + glob . glob ( ' /media/usb/* ' ) + [ ' /home/user ' ] :
dir = os . path . join ( basedir , ' gpodder ' )
if os . path . exists ( dir ) :
log ( ' Downloads found in: %s ' , dir )
config . download_dir = dir
break
else :
log ( ' Downloads NOT FOUND in %s ' , dir )
if not config . disable_fingerscroll :
GtkBuilderWidget . use_fingerscroll = True
2009-05-11 21:35:33 +02:00
2009-08-24 18:11:58 +02:00
gp = gPodder ( bus_name , config )
2009-02-25 14:57:45 +01:00
gp . run ( )
2005-11-21 19:21:25 +01:00
2008-02-20 13:46:51 +01:00