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
2010-01-02 17:35:42 +01:00
# Copyright (c) 2005-2010 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
2010-01-19 23:43:59 +01:00
import cgi
2005-11-21 19:21:25 +01:00
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
2009-08-10 18:32:08 +02:00
import tempfile
2009-08-24 18:11:58 +02:00
import collections
2009-08-24 20:59:21 +02:00
import threading
2006-03-04 21:45:01 +01:00
2007-07-05 23:07:16 +02:00
from xml . sax import saxutils
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-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-05-07 16:26:07 +02:00
_ = gpodder . gettext
2009-12-21 23:18:00 +01:00
N_ = gpodder . ngettext
2009-05-07 16:26:07 +02: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
from gpodder . gtkui . model import PodcastListModel
from gpodder . gtkui . model import EpisodeListModel
2009-08-24 18:11:58 +02:00
from gpodder . gtkui . config import UIConfig
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-09-01 23:38:21 +02:00
from gpodder . gtkui . draw import draw_text_box_centered
2009-08-24 18:54:45 +02:00
from gpodder . gtkui . interface . common import BuilderWidget
2009-09-05 00:12:53 +02:00
from gpodder . gtkui . interface . common import TreeViewHelper
2009-08-24 19:10:52 +02:00
from gpodder . gtkui . interface . addpodcast import gPodderAddPodcast
2010-01-19 23:43:59 +01:00
from gpodder . gtkui . mygpodder import MygPodderSettings
2005-11-21 19:21:25 +01:00
2009-09-15 19:49:28 +02:00
if gpodder . ui . desktop :
2009-11-16 15:21:57 +01:00
from gpodder . gtkui . download import DownloadStatusModel
2009-09-05 14:24:37 +02:00
from gpodder . gtkui . desktop . sync import gPodderSyncUI
2009-09-02 01:43:09 +02:00
2009-09-07 18:18:23 +02:00
from gpodder . gtkui . desktop . channel import gPodderChannel
2009-09-02 01:43:09 +02:00
from gpodder . gtkui . desktop . preferences import gPodderPreferences
from gpodder . gtkui . desktop . shownotes import gPodderShownotes
2009-09-08 21:35:36 +02:00
from gpodder . gtkui . desktop . episodeselector import gPodderEpisodeSelector
2009-09-09 00:20:24 +02:00
from gpodder . gtkui . desktop . podcastdirectory import gPodderPodcastDirectory
2009-09-23 13:20:44 +02:00
from gpodder . gtkui . desktop . dependencymanager import gPodderDependencyManager
2009-09-04 20:17:33 +02:00
try :
from gpodder . gtkui . desktop . trayicon import GPodderStatusIcon
have_trayicon = True
except Exception , exc :
log ( ' Warning: Could not import gpodder.trayicon. ' , traceback = True )
log ( ' Warning: This probably means your PyGTK installation is too old! ' )
have_trayicon = False
2009-09-15 22:35:46 +02:00
elif gpodder . ui . diablo :
2009-11-16 15:21:57 +01:00
from gpodder . gtkui . download import DownloadStatusModel
2009-09-07 18:18:23 +02:00
from gpodder . gtkui . maemo . channel import gPodderChannel
2009-09-02 00:58:55 +02:00
from gpodder . gtkui . maemo . preferences import gPodderPreferences
2009-09-02 01:43:09 +02:00
from gpodder . gtkui . maemo . shownotes import gPodderShownotes
2009-09-08 21:35:36 +02:00
from gpodder . gtkui . maemo . episodeselector import gPodderEpisodeSelector
2009-09-09 00:20:24 +02:00
from gpodder . gtkui . maemo . podcastdirectory import gPodderPodcastDirectory
2009-09-04 20:17:33 +02:00
have_trayicon = False
2009-09-15 22:35:46 +02:00
elif gpodder . ui . fremantle :
2009-11-16 15:21:57 +01:00
from gpodder . gtkui . frmntl . model import DownloadStatusModel
2009-12-22 00:24:26 +01:00
from gpodder . gtkui . frmntl . model import EpisodeListModel
from gpodder . gtkui . frmntl . model import PodcastListModel
2009-11-16 15:21:57 +01:00
2009-09-15 22:35:46 +02:00
from gpodder . gtkui . maemo . channel import gPodderChannel
2009-10-13 20:09:01 +02:00
from gpodder . gtkui . frmntl . preferences import gPodderPreferences
2009-09-16 23:51:18 +02:00
from gpodder . gtkui . frmntl . shownotes import gPodderShownotes
2009-09-15 22:35:46 +02:00
from gpodder . gtkui . frmntl . episodeselector import gPodderEpisodeSelector
from gpodder . gtkui . frmntl . podcastdirectory import gPodderPodcastDirectory
2009-09-16 23:51:18 +02:00
from gpodder . gtkui . frmntl . episodes import gPodderEpisodes
from gpodder . gtkui . frmntl . downloads import gPodderDownloads
2009-09-22 00:45:42 +02:00
from gpodder . gtkui . interface . common import Orientation
2009-09-15 22:35:46 +02:00
have_trayicon = False
2009-08-24 19:31:58 +02:00
2009-09-24 14:41:11 +02:00
from gpodder . gtkui . frmntl . portrait import FremantleRotation
2009-09-22 00:45:42 +02:00
2009-08-24 20:41:16 +02:00
from gpodder . gtkui . interface . welcome import gPodderWelcome
2009-09-12 15:32:10 +02:00
from gpodder . gtkui . interface . progress import ProgressIndicator
2009-08-24 19:31:58 +02:00
2009-09-15 19:49:28 +02:00
if gpodder . ui . maemo :
2008-04-06 02:19:03 +02:00
import hildon
2010-01-08 01:58:15 +01:00
from gpodder . dbusproxy import DBusPodcastsProxy
2009-05-08 14:28:53 +02:00
class gPodder ( BuilderWidget , dbus . service . Object ) :
2009-11-12 02:53:59 +01:00
finger_friendly_widgets = [ ' btnCleanUpDownloads ' , ' button_search_episodes_clear ' ]
2009-02-25 14:57:45 +01:00
2009-12-17 17:17:47 +01:00
ICON_GENERAL_ADD = ' general_add '
ICON_GENERAL_REFRESH = ' general_refresh '
ICON_GENERAL_CLOSE = ' general_close '
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 )
2010-01-08 01:58:15 +01:00
self . podcasts_proxy = DBusPodcastsProxy ( lambda : self . channels , \
self . on_itemUpdate_activate , \
self . playback_episodes , \
self . download_episode_list , \
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 ) :
2009-09-16 23:51:18 +02:00
if gpodder . ui . diablo :
import hildon
2008-04-06 02:19:03 +02:00
self . app = hildon . Program ( )
2009-09-05 15:42:55 +02:00
self . app . add_window ( self . main_window )
self . main_window . add_toolbar ( self . toolbar )
2008-04-06 02:19:03 +02:00
menu = gtk . Menu ( )
2009-09-05 15:42:55 +02:00
for child in self . main_menu . get_children ( ) :
child . reparent ( menu )
self . main_window . set_menu ( self . set_finger_friendly ( menu ) )
2009-09-16 23:51:18 +02:00
self . bluetooth_available = False
elif gpodder . ui . fremantle :
import hildon
self . app = hildon . Program ( )
self . app . add_window ( self . main_window )
appmenu = hildon . AppMenu ( )
2009-12-17 17:17:47 +01:00
for filter in ( self . item_view_podcasts_all , \
self . item_view_podcasts_downloaded , \
self . item_view_podcasts_unplayed ) :
button = gtk . ToggleButton ( )
filter . connect_proxy ( button )
appmenu . add_filter ( button )
for action in ( self . itemPreferences , \
self . item_downloads , \
2009-09-22 20:53:26 +02:00
self . itemRemoveOldEpisodes , \
2009-12-17 17:17:47 +01:00
self . item_unsubscribe , \
self . item_support , \
self . item_report_bug ) :
button = hildon . Button ( gtk . HILDON_SIZE_AUTO , \
hildon . BUTTON_ARRANGEMENT_HORIZONTAL )
2009-09-16 23:51:18 +02:00
action . connect_proxy ( button )
2009-12-17 17:17:47 +01:00
if action == self . item_downloads :
button . set_title ( _ ( ' Downloads ' ) )
button . set_value ( _ ( ' Idle ' ) )
self . button_downloads = button
2009-09-16 23:51:18 +02:00
appmenu . append ( button )
appmenu . show_all ( )
self . main_window . set_app_menu ( appmenu )
2009-09-24 14:41:11 +02:00
# Initialize portrait mode / rotation manager
2009-10-13 14:27:38 +02:00
self . _fremantle_rotation = FremantleRotation ( ' gPodder ' , \
2009-10-13 20:09:01 +02:00
self . main_window , \
gpodder . __version__ , \
self . config . rotation_mode )
2009-09-22 00:45:42 +02:00
2009-12-17 15:14:13 +01:00
if self . config . rotation_mode == FremantleRotation . ALWAYS :
2009-12-17 17:17:47 +01:00
util . idle_add ( self . on_window_orientation_changed , \
Orientation . PORTRAIT )
2009-12-17 15:14:13 +01:00
2009-09-05 15:42:55 +02:00
self . bluetooth_available = False
else :
self . bluetooth_available = util . bluetooth_available ( )
self . toolbar . set_property ( ' visible ' , self . config . show_toolbar )
2008-05-02 17:36:43 +02:00
2009-09-05 15:42:55 +02:00
self . config . connect_gtk_window ( self . gPodder , ' main_window ' )
2009-09-16 23:51:18 +02:00
if not gpodder . ui . fremantle :
self . config . connect_gtk_paned ( ' paned_position ' , self . channelPaned )
2009-09-05 15:42:55 +02:00
self . main_window . show ( )
2009-09-02 18:40:52 +02:00
2008-09-27 14:20:43 +02:00
self . gPodder . connect ( ' key-press-event ' , self . on_key_press )
2008-05-02 17:36:43 +02:00
2009-08-24 18:11:58 +02:00
self . config . add_observer ( self . on_config_changed )
2009-08-25 16:19:14 +02:00
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-09-28 14:23:21 +02:00
self . new_episodes_window = None
2009-04-01 13:34:19 +02:00
2009-09-15 19:49:28 +02:00
if gpodder . ui . desktop :
2009-09-05 14:24:37 +02:00
self . sync_ui = gPodderSyncUI ( self . config , self . notification , \
self . main_window , self . show_confirmation , \
self . update_episode_list_icons , \
self . update_podcast_list_model , self . toolPreferences , \
gPodderEpisodeSelector )
else :
self . sync_ui = None
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
2009-09-16 23:51:18 +02:00
if gpodder . ui . desktop :
self . show_hide_tray_icon ( )
self . itemShowToolbar . set_active ( self . config . show_toolbar )
self . itemShowDescription . set_active ( self . config . episode_list_descriptions )
2008-02-06 10:29:56 +01:00
2009-09-16 23:51:18 +02:00
if not gpodder . ui . fremantle :
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-09-16 23:51:18 +02:00
# When the amount of maximum downloads changes, notify the queue manager
2009-12-17 00:37:45 +01:00
changed_cb = lambda spinbutton : self . download_queue_manager . spawn_threads ( )
2009-09-16 23:51:18 +02:00
self . spinMaxDownloads . connect ( ' value-changed ' , changed_cb )
2008-04-17 17:45:29 +02:00
2009-09-16 23:51:18 +02:00
self . default_title = ' gPodder '
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
2009-09-05 00:12:53 +02:00
self . cover_downloader = CoverDownloader ( )
# Generate list models for podcasts and their episodes
self . podcast_list_model = PodcastListModel ( self . config . podcast_list_icon_size , self . cover_downloader )
2009-09-05 00:53:46 +02:00
self . cover_downloader . register ( ' cover-available ' , self . cover_download_finished )
self . cover_downloader . register ( ' cover-removed ' , self . cover_file_removed )
2009-09-15 19:49:28 +02:00
if gpodder . ui . fremantle :
2009-12-17 17:17:47 +01:00
# Work around Maemo bug #4718
self . button_refresh . set_name ( ' HildonButton-finger ' )
self . button_subscribe . set_name ( ' HildonButton-finger ' )
self . button_refresh . set_sensitive ( False )
self . button_subscribe . set_sensitive ( False )
self . button_subscribe . set_image ( gtk . image_new_from_icon_name ( \
self . ICON_GENERAL_ADD , gtk . ICON_SIZE_BUTTON ) )
self . button_refresh . set_image ( gtk . image_new_from_icon_name ( \
self . ICON_GENERAL_REFRESH , gtk . ICON_SIZE_BUTTON ) )
# Make the button scroll together with the TreeView contents
action_area_box = self . treeChannels . get_action_area_box ( )
for child in self . buttonbox :
child . reparent ( action_area_box )
self . vbox . remove ( self . buttonbox )
action_area_box . set_spacing ( 2 )
action_area_box . set_border_width ( 3 )
self . treeChannels . set_action_area_visible ( True )
2009-09-21 16:03:52 +02:00
2009-10-13 20:09:01 +02:00
from gpodder . gtkui . frmntl import style
sub_font = style . get_font_desc ( ' SmallSystemFont ' )
sub_color = style . get_color ( ' SecondaryTextColor ' )
sub = ( sub_font . to_string ( ) , sub_color . to_string ( ) )
sub = ' <span font_desc= " %s " foreground= " %s " > %% s</span> ' % sub
self . label_footer . set_markup ( sub % gpodder . __copyright__ )
2009-09-21 16:03:52 +02:00
hildon . hildon_gtk_window_set_progress_indicator ( self . main_window , True )
while gtk . events_pending ( ) :
gtk . main_iteration ( False )
2009-10-13 20:09:01 +02:00
try :
# Try to get the real package version from dpkg
p = subprocess . Popen ( [ ' dpkg-query ' , ' -W ' , ' -f=$ {Version} ' , ' gpodder ' ] , stdout = subprocess . PIPE )
version , _stderr = p . communicate ( )
del _stderr
del p
except :
version = gpodder . __version__
self . label_footer . set_markup ( sub % ( ' v %s ' % version ) )
2009-12-17 17:17:47 +01:00
self . label_footer . hide ( )
2009-10-13 20:09:01 +02:00
2009-09-16 23:51:18 +02:00
self . episodes_window = gPodderEpisodes ( self . main_window , \
on_treeview_expose_event = self . on_treeview_expose_event , \
show_episode_shownotes = self . show_episode_shownotes , \
update_podcast_list_model = self . update_podcast_list_model , \
2009-09-19 19:48:17 +02:00
on_itemRemoveChannel_activate = self . on_itemRemoveChannel_activate , \
item_view_episodes_all = self . item_view_episodes_all , \
item_view_episodes_unplayed = self . item_view_episodes_unplayed , \
item_view_episodes_downloaded = self . item_view_episodes_downloaded , \
2009-11-12 02:53:59 +01:00
item_view_episodes_undeleted = self . item_view_episodes_undeleted , \
on_entry_search_episodes_changed = self . on_entry_search_episodes_changed , \
on_entry_search_episodes_key_press = self . on_entry_search_episodes_key_press , \
2009-12-06 22:13:31 +01:00
hide_episode_search = self . hide_episode_search , \
2009-12-05 11:33:07 +01:00
on_itemUpdateChannel_activate = self . on_itemUpdateChannel_activate , \
2009-12-05 14:08:15 +01:00
playback_episodes = self . playback_episodes , \
delete_episode_list = self . delete_episode_list , \
2009-12-22 00:24:26 +01:00
episode_list_status_changed = self . episode_list_status_changed , \
download_episode_list = self . download_episode_list , \
2009-12-22 17:01:47 +01:00
episode_is_downloading = self . episode_is_downloading , \
show_episode_in_download_manager = self . show_episode_in_download_manager , \
add_download_task_monitor = self . add_download_task_monitor , \
remove_download_task_monitor = self . remove_download_task_monitor , \
for_each_episode_set_task_status = self . for_each_episode_set_task_status )
2009-11-12 02:53:59 +01:00
# Expose objects for episode list type-ahead find
self . hbox_search_episodes = self . episodes_window . hbox_search_episodes
self . entry_search_episodes = self . episodes_window . entry_search_episodes
self . button_search_episodes_clear = self . episodes_window . button_search_episodes_clear
2009-09-16 23:51:18 +02:00
self . downloads_window = gPodderDownloads ( self . main_window , \
on_treeview_expose_event = self . on_treeview_expose_event , \
2009-09-22 19:38:27 +02:00
on_btnCleanUpDownloads_clicked = self . on_btnCleanUpDownloads_clicked , \
_for_each_task_set_status = self . _for_each_task_set_status , \
2009-12-17 14:50:39 +01:00
downloads_list_get_selection = self . downloads_list_get_selection , \
_config = self . config )
2009-12-17 17:17:47 +01:00
2009-09-16 23:51:18 +02:00
self . treeAvailable = self . episodes_window . treeview
self . treeDownloads = self . downloads_window . treeview
2009-09-05 00:12:53 +02:00
# Init the treeviews that we use
self . init_podcast_list_treeview ( )
self . init_episode_list_treeview ( )
self . init_download_list_treeview ( )
2009-09-09 16:44:48 +02:00
if self . config . podcast_list_hide_boring :
self . item_view_hide_boring_podcasts . set_active ( True )
2009-09-05 00:12:53 +02:00
self . currently_updating = False
2009-09-15 19:49:28 +02:00
if gpodder . ui . maemo :
2009-09-05 00:12:53 +02:00
self . context_menu_mouse_button = 1
else :
self . context_menu_mouse_button = 3
if self . config . start_iconified :
self . iconify_main_window ( )
self . download_tasks_seen = set ( )
self . download_list_update_enabled = False
self . last_download_count = 0
2009-12-22 17:01:47 +01:00
self . download_task_monitors = set ( )
2009-09-05 00:12:53 +02:00
# Subscribed channels
self . active_channel = None
self . channels = PodcastChannel . load_from_db ( self . db , self . config . download_dir )
self . channel_list_changed = True
self . update_podcasts_tab ( )
# load list of user applications for audio playback
self . user_apps_reader = UserAppsReader ( [ ' audio ' , ' video ' ] )
2010-01-28 17:39:10 +01:00
threading . Thread ( target = self . user_apps_reader . read ) . start ( )
2009-09-05 00:12:53 +02:00
# Set the "Device" menu item for the first time
2009-09-16 23:51:18 +02:00
if gpodder . ui . desktop :
self . update_item_device ( )
2009-09-05 00:12:53 +02:00
2010-01-28 19:31:58 +01:00
# Set up the first instance of MygPoClient
self . mygpo_client = my . MygPoClient ( self . config )
2009-09-05 00:12:53 +02:00
# Now, update the feed cache, when everything's in place
2009-09-16 23:51:18 +02:00
if not gpodder . ui . fremantle :
self . btnUpdateFeeds . show ( )
2009-09-05 00:12:53 +02:00
self . updating_feed_cache = False
self . feed_cache_update_cancelled = False
self . update_feed_cache ( force_update = self . config . update_on_startup )
# Look for partial file downloads
partial_files = glob . glob ( os . path . join ( self . config . download_dir , ' * ' , ' *.partial ' ) )
# Message area
self . message_area = None
resumable_episodes = [ ]
if len ( partial_files ) > 0 :
for f in partial_files :
correct_name = f [ : - len ( ' .partial ' ) ] # strip ".partial"
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 ( ) :
if e . local_filename ( create = False , check_only = True ) == correct_name :
log ( ' Found episode: %s ' , e . title , sender = self )
resumable_episodes . append ( e )
found_episode = True
if found_episode :
break
if found_episode :
break
if not found_episode :
log ( ' Partial file without episode: %s ' , f , sender = self )
util . delete_file ( f )
if len ( resumable_episodes ) :
self . download_episode_list_paused ( resumable_episodes )
2009-09-16 23:51:18 +02:00
if not gpodder . ui . fremantle :
self . message_area = SimpleMessageArea ( _ ( ' There are unfinished downloads from your last session. \n Pick the ones you want to continue downloading. ' ) )
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-09-05 00:12:53 +02:00
self . clean_up_downloads ( delete_partial = False )
else :
self . clean_up_downloads ( delete_partial = True )
# Start the auto-update procedure
2009-10-13 22:48:12 +02:00
self . _auto_update_timer_source_id = None
if self . config . auto_update_feeds :
self . restart_auto_update_timer ( )
2009-09-05 00:12:53 +02:00
2009-12-17 14:50:39 +01:00
# Connect the auto cleanup button to the configuration
if gpodder . ui . desktop or gpodder . ui . diablo :
self . config . connect_gtk_togglebutton ( ' auto_cleanup_downloads ' , \
self . btnCleanUpDownloads )
2009-09-05 00:12:53 +02:00
# Delete old episodes if the user wishes to
if self . config . auto_remove_old_episodes :
old_episodes = self . get_old_episodes ( )
if len ( old_episodes ) > 0 :
self . delete_episode_list ( old_episodes , confirm = False )
self . update_podcast_list_model ( set ( e . channel . url for e in old_episodes ) )
2009-09-21 16:03:52 +02:00
if gpodder . ui . fremantle :
hildon . hildon_gtk_window_set_progress_indicator ( self . main_window , False )
2009-12-17 17:17:47 +01:00
self . button_refresh . set_sensitive ( True )
2009-09-21 16:03:52 +02:00
self . button_subscribe . set_sensitive ( True )
2009-10-13 15:32:57 +02:00
self . main_window . set_title ( _ ( ' gPodder ' ) )
2009-12-17 17:17:47 +01:00
hildon . hildon_gtk_window_take_screenshot ( self . main_window , True )
2009-09-21 16:03:52 +02:00
2010-01-28 17:39:10 +01:00
# Do the initial sync with the web service
util . idle_add ( self . mygpo_client . flush , True )
2010-01-19 23:43:59 +01:00
2009-09-05 00:12:53 +02:00
# First-time users should be asked if they want to see the OPML
2009-09-21 19:38:35 +02:00
if not self . channels and not gpodder . ui . fremantle :
2009-09-05 00:12:53 +02:00
util . idle_add ( self . on_itemUpdate_activate )
2010-01-28 17:39:10 +01:00
def on_add_remove_podcasts_mygpo ( self ) :
actions = self . mygpo_client . get_received_actions ( )
if not actions :
return False
2010-01-19 23:43:59 +01:00
existing_urls = [ c . url for c in self . channels ]
# Columns for the episode selector window - just one...
columns = (
( ' description ' , None , None , _ ( ' Action ' ) ) ,
)
# A list of actions that have to be chosen from
2010-01-28 17:39:10 +01:00
changes = [ ]
2010-01-19 23:43:59 +01:00
2010-01-28 17:39:10 +01:00
# Actions that are ignored (already carried out)
ignored = [ ]
2010-01-19 23:43:59 +01:00
2010-01-28 17:39:10 +01:00
for action in actions :
if action . is_add and action . url not in existing_urls :
changes . append ( my . Change ( action ) )
elif action . is_remove and action . url in existing_urls :
2010-01-19 23:43:59 +01:00
podcast_object = None
for podcast in self . channels :
2010-01-28 17:39:10 +01:00
if podcast . url == action . url :
2010-01-19 23:43:59 +01:00
podcast_object = podcast
break
2010-01-28 17:39:10 +01:00
changes . append ( my . Change ( action , podcast_object ) )
else :
log ( ' Ignoring action: %s ' , action , sender = self )
ignored . append ( action )
# Confirm all ignored changes
self . mygpo_client . confirm_received_actions ( ignored )
2010-01-19 23:43:59 +01:00
def execute_podcast_actions ( selected ) :
2010-01-28 17:39:10 +01:00
add_list = [ c . action . url for c in selected if c . action . is_add ]
remove_list = [ c . podcast for c in selected if c . action . is_remove ]
2010-01-19 23:43:59 +01:00
# Apply the accepted changes locally
2010-01-28 17:39:10 +01:00
self . add_podcast_list ( add_list )
2010-01-19 23:43:59 +01:00
self . remove_podcast_list ( remove_list , confirm = False )
2010-01-28 17:39:10 +01:00
# All selected items are now confirmed
self . mygpo_client . confirm_received_actions ( c . action for c in selected )
# Revert the changes on the server
rejected = [ c . action for c in changes if c not in selected ]
self . mygpo_client . reject_received_actions ( rejected )
2010-01-19 23:43:59 +01:00
def ask ( ) :
# We're abusing the Episode Selector again ;) -- thp
gPodderEpisodeSelector ( self . main_window , \
title = _ ( ' Confirm changes from my.gpodder.org ' ) , \
instructions = _ ( ' Select the actions you want to carry out. ' ) , \
2010-01-28 17:39:10 +01:00
episodes = changes , \
2010-01-19 23:43:59 +01:00
columns = columns , \
size_attribute = None , \
stock_ok_button = gtk . STOCK_APPLY , \
callback = execute_podcast_actions , \
_config = self . config )
2010-01-28 17:39:10 +01:00
# There are some actions that need the user's attention
if changes :
2010-01-19 23:43:59 +01:00
util . idle_add ( ask )
2010-01-28 17:39:10 +01:00
return True
2010-01-19 23:43:59 +01:00
2010-01-28 17:39:10 +01:00
# We have no remaining actions - no selection happens
return False
2010-01-19 23:43:59 +01:00
2010-01-28 17:39:10 +01:00
def rewrite_urls_mygpo ( self ) :
# Check if we have to rewrite URLs since the last add
rewritten_urls = self . mygpo_client . get_rewritten_urls ( )
for rewritten_url in rewritten_urls :
if not rewritten_url . new_url :
continue
for channel in self . channels :
if channel . url == rewritten_url . old_url :
log ( ' Updating URL of %s to %s ' , channel , \
rewritten_url . new_url , sender = self )
channel . url = rewritten_url . new_url
channel . save ( )
self . channel_list_changed = True
util . idle_add ( self . update_episode_list_model )
break
2010-01-19 23:43:59 +01:00
def on_send_full_subscriptions ( self ) :
# Send the full subscription list to the my.gpodder.org client
2010-01-25 00:53:25 +01:00
# (this will overwrite the subscription list on the server)
indicator = ProgressIndicator ( _ ( ' Uploading subscriptions ' ) , \
_ ( ' Your subscriptions are being uploaded to the server. ' ) , \
False , self . main_window )
try :
self . mygpo_client . set_subscriptions ( [ c . url for c in self . channels ] )
util . idle_add ( self . show_message , _ ( ' List uploaded successfully. ' ) )
except Exception , e :
2010-01-28 17:39:10 +01:00
def show_error ( e ) :
message = str ( e )
if not message :
message = e . __class__ . __name__
self . show_message ( message , \
_ ( ' Error while uploading ' ) , \
important = True )
util . idle_add ( show_error , e )
2010-01-25 00:53:25 +01:00
util . idle_add ( indicator . on_finished )
2010-01-19 23:43:59 +01:00
2009-12-17 17:17:47 +01:00
def on_podcast_selected ( self , treeview , path , column ) :
# for Maemo 5's UI
model = treeview . get_model ( )
channel = model . get_value ( model . get_iter ( path ) , \
PodcastListModel . C_CHANNEL )
self . active_channel = channel
self . update_episode_list_model ( )
self . episodes_window . channel = self . active_channel
self . episodes_window . show ( )
2009-09-16 23:51:18 +02:00
def on_button_subscribe_clicked ( self , button ) :
self . on_itemImportChannels_activate ( button )
def on_button_downloads_clicked ( self , widget ) :
self . downloads_window . show ( )
2009-12-22 17:01:47 +01:00
def show_episode_in_download_manager ( self , episode ) :
self . downloads_window . show ( )
model = self . treeDownloads . get_model ( )
selection = self . treeDownloads . get_selection ( )
selection . unselect_all ( )
it = model . get_iter_first ( )
while it is not None :
task = model . get_value ( it , DownloadStatusModel . C_TASK )
if task . episode . url == episode . url :
selection . select_iter ( it )
# FIXME: Scroll to selection in pannable area
break
it = model . iter_next ( it )
def for_each_episode_set_task_status ( self , episodes , status ) :
episode_urls = set ( episode . url for episode in episodes )
model = self . treeDownloads . get_model ( )
selected_tasks = [ ( gtk . TreeRowReference ( model , row . path ) , \
model . get_value ( row . iter , \
DownloadStatusModel . C_TASK ) ) for row in model \
if model . get_value ( row . iter , DownloadStatusModel . C_TASK ) . url \
in episode_urls ]
self . _for_each_task_set_status ( selected_tasks , status )
2009-09-22 00:45:42 +02:00
def on_window_orientation_changed ( self , orientation ) :
2009-12-17 17:17:47 +01:00
treeview = self . treeChannels
2009-09-22 00:45:42 +02:00
if orientation == Orientation . PORTRAIT :
2009-12-17 17:17:47 +01:00
treeview . set_action_area_orientation ( gtk . ORIENTATION_VERTICAL )
# Work around Maemo bug #4718
self . button_subscribe . set_name ( ' HildonButton-thumb ' )
self . button_refresh . set_name ( ' HildonButton-thumb ' )
2009-09-22 00:45:42 +02:00
else :
2009-12-17 17:17:47 +01:00
treeview . set_action_area_orientation ( gtk . ORIENTATION_HORIZONTAL )
# Work around Maemo bug #4718
self . button_subscribe . set_name ( ' HildonButton-finger ' )
self . button_refresh . set_name ( ' HildonButton-finger ' )
2009-09-22 00:45:42 +02:00
2009-09-05 00:12:53 +02:00
def on_treeview_podcasts_selection_changed ( self , selection ) :
model , iter = selection . get_selected ( )
if iter is None :
self . active_channel = None
self . episode_list_model . clear ( )
def on_treeview_button_pressed ( self , treeview , event ) :
2009-09-28 14:31:47 +02:00
if event . window != treeview . get_bin_window ( ) :
return False
2009-09-05 00:12:53 +02:00
TreeViewHelper . save_button_press_event ( treeview , event )
if getattr ( treeview , TreeViewHelper . ROLE ) == \
TreeViewHelper . ROLE_PODCASTS :
return self . currently_updating
return event . button == self . context_menu_mouse_button and \
2009-09-15 19:49:28 +02:00
gpodder . ui . desktop
2009-09-05 00:12:53 +02:00
def on_treeview_podcasts_button_released ( self , treeview , event ) :
2009-09-28 14:31:47 +02:00
if event . window != treeview . get_bin_window ( ) :
return False
2009-09-15 19:49:28 +02:00
if gpodder . ui . maemo :
2009-09-05 00:12:53 +02:00
return self . treeview_channels_handle_gestures ( treeview , event )
return self . treeview_channels_show_context_menu ( treeview , event )
def on_treeview_episodes_button_released ( self , treeview , event ) :
2009-09-28 14:31:47 +02:00
if event . window != treeview . get_bin_window ( ) :
return False
2009-09-15 19:49:28 +02:00
if gpodder . ui . maemo :
2009-09-05 17:21:13 +02:00
if self . config . enable_fingerscroll or self . config . maemo_enable_gestures :
2009-09-05 00:12:53 +02:00
return self . treeview_available_handle_gestures ( treeview , event )
return self . treeview_available_show_context_menu ( treeview , event )
def on_treeview_downloads_button_released ( self , treeview , event ) :
2009-09-28 14:31:47 +02:00
if event . window != treeview . get_bin_window ( ) :
return False
2009-09-05 00:12:53 +02:00
return self . treeview_downloads_show_context_menu ( treeview , event )
2009-11-12 03:22:14 +01:00
def on_entry_search_podcasts_changed ( self , editable ) :
if self . hbox_search_podcasts . get_property ( ' visible ' ) :
self . podcast_list_model . set_search_term ( editable . get_chars ( 0 , - 1 ) )
def on_entry_search_podcasts_key_press ( self , editable , event ) :
if event . keyval == gtk . keysyms . Escape :
self . hide_podcast_search ( )
return True
def hide_podcast_search ( self , * args ) :
self . hbox_search_podcasts . hide ( )
self . entry_search_podcasts . set_text ( ' ' )
self . podcast_list_model . set_search_term ( None )
self . treeChannels . grab_focus ( )
def show_podcast_search ( self , input_char ) :
self . hbox_search_podcasts . show ( )
self . entry_search_podcasts . insert_text ( input_char , - 1 )
self . entry_search_podcasts . grab_focus ( )
self . entry_search_podcasts . set_position ( - 1 )
2009-09-05 00:12:53 +02:00
def init_podcast_list_treeview ( self ) :
2009-08-13 22:17:41 +02:00
# Set up podcast channel tree view widget
2009-09-19 19:48:17 +02:00
if gpodder . ui . fremantle :
if self . config . podcast_list_view_mode == EpisodeListModel . VIEW_DOWNLOADED :
self . item_view_podcasts_downloaded . set_active ( True )
elif self . config . podcast_list_view_mode == EpisodeListModel . VIEW_UNPLAYED :
self . item_view_podcasts_unplayed . set_active ( True )
else :
self . item_view_podcasts_all . set_active ( True )
self . podcast_list_model . set_view_mode ( self . config . podcast_list_view_mode )
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-09-01 18:56:30 +02:00
self . treeChannels . set_model ( self . podcast_list_model . get_filtered_model ( ) )
2009-09-05 00:12:53 +02:00
# When no podcast is selected, clear the episode list model
2009-09-01 18:56:30 +02:00
selection = self . treeChannels . get_selection ( )
2009-09-05 00:12:53 +02:00
selection . connect ( ' changed ' , self . on_treeview_podcasts_selection_changed )
2005-11-21 19:21:25 +01:00
2009-11-12 03:22:14 +01:00
# Set up type-ahead find for the podcast list
def on_key_press ( treeview , event ) :
if event . keyval == gtk . keysyms . Escape :
self . hide_podcast_search ( )
elif gpodder . ui . fremantle and event . keyval == gtk . keysyms . BackSpace :
self . hide_podcast_search ( )
2009-11-25 10:14:57 +01:00
elif event . state & gtk . gdk . CONTROL_MASK :
# Don't handle type-ahead when control is pressed (so shortcuts
# with the Ctrl key still work, e.g. Ctrl+A, ...)
return True
2009-11-12 03:22:14 +01:00
else :
unicode_char_id = gtk . gdk . keyval_to_unicode ( event . keyval )
if unicode_char_id == 0 :
return False
input_char = unichr ( unicode_char_id )
self . show_podcast_search ( input_char )
return True
self . treeChannels . connect ( ' key-press-event ' , on_key_press )
2009-12-16 14:55:55 +01:00
# Enable separators to the podcast list to separate special podcasts
# from others (this is used for the "all episodes" view)
self . treeChannels . set_row_separator_func ( PodcastListModel . row_separator_func )
2009-09-05 00:12:53 +02:00
TreeViewHelper . set ( self . treeChannels , TreeViewHelper . ROLE_PODCASTS )
2009-11-05 11:00:15 +01:00
def on_entry_search_episodes_changed ( self , editable ) :
if self . hbox_search_episodes . get_property ( ' visible ' ) :
self . episode_list_model . set_search_term ( editable . get_chars ( 0 , - 1 ) )
def on_entry_search_episodes_key_press ( self , editable , event ) :
if event . keyval == gtk . keysyms . Escape :
self . hide_episode_search ( )
return True
def hide_episode_search ( self , * args ) :
self . hbox_search_episodes . hide ( )
self . entry_search_episodes . set_text ( ' ' )
self . episode_list_model . set_search_term ( None )
self . treeAvailable . grab_focus ( )
def show_episode_search ( self , input_char ) :
self . hbox_search_episodes . show ( )
2009-11-12 03:22:14 +01:00
self . entry_search_episodes . insert_text ( input_char , - 1 )
2009-11-05 11:00:15 +01:00
self . entry_search_episodes . grab_focus ( )
self . entry_search_episodes . set_position ( - 1 )
2009-09-05 00:12:53 +02:00
def init_episode_list_treeview ( self ) :
2009-08-13 23:19:12 +02:00
self . episode_list_model = EpisodeListModel ( )
2009-09-01 17:22:51 +02:00
if self . config . episode_list_view_mode == EpisodeListModel . VIEW_UNDELETED :
self . item_view_episodes_undeleted . set_active ( True )
elif self . config . episode_list_view_mode == EpisodeListModel . VIEW_DOWNLOADED :
self . item_view_episodes_downloaded . set_active ( True )
2009-09-09 16:44:48 +02:00
elif self . config . episode_list_view_mode == EpisodeListModel . VIEW_UNPLAYED :
self . item_view_episodes_unplayed . set_active ( True )
2009-09-01 17:22:51 +02:00
else :
self . item_view_episodes_all . set_active ( True )
2009-09-09 16:44:48 +02:00
self . episode_list_model . set_view_mode ( self . config . episode_list_view_mode )
2009-09-01 17:22:51 +02:00
self . treeAvailable . set_model ( self . episode_list_model . get_filtered_model ( ) )
2009-08-13 23:19:12 +02:00
2009-09-05 00:12:53 +02:00
TreeViewHelper . set ( self . treeAvailable , TreeViewHelper . ROLE_EPISODES )
2009-04-01 01:12:17 +02:00
2007-04-03 08:27:46 +02:00
iconcell = gtk . CellRendererPixbuf ( )
2009-09-15 19:49:28 +02:00
if gpodder . ui . maemo :
2009-09-06 23:05:38 +02:00
iconcell . set_fixed_size ( 50 , 50 )
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 )
2009-09-05 00:12:53 +02:00
2008-02-06 10:29:56 +01:00
for itemcolumn in ( iconcolumn , namecolumn , sizecolumn , releasecolumn ) :
2009-09-02 18:40:52 +02:00
itemcolumn . set_reorderable ( True )
2008-02-06 10:29:56 +01:00
self . treeAvailable . append_column ( itemcolumn )
2006-06-22 23:41:32 +02:00
2009-09-15 19:49:28 +02:00
if gpodder . ui . maemo :
2009-09-05 00:12:53 +02:00
sizecolumn . set_visible ( False )
releasecolumn . set_visible ( False )
2008-04-06 02:19:03 +02:00
2009-11-12 02:53:59 +01:00
# Set up type-ahead find for the episode list
def on_key_press ( treeview , event ) :
if event . keyval == gtk . keysyms . Escape :
self . hide_episode_search ( )
2009-11-12 03:22:14 +01:00
elif gpodder . ui . fremantle and event . keyval == gtk . keysyms . BackSpace :
self . hide_episode_search ( )
2009-11-25 10:14:57 +01:00
elif event . state & gtk . gdk . CONTROL_MASK :
# Don't handle type-ahead when control is pressed (so shortcuts
# with the Ctrl key still work, e.g. Ctrl+A, ...)
return False
2009-11-12 02:53:59 +01:00
else :
unicode_char_id = gtk . gdk . keyval_to_unicode ( event . keyval )
if unicode_char_id == 0 :
return False
input_char = unichr ( unicode_char_id )
self . show_episode_search ( input_char )
return True
self . treeAvailable . connect ( ' key-press-event ' , on_key_press )
2007-04-03 08:27:46 +02:00
2009-11-13 17:11:27 +01:00
if gpodder . ui . desktop :
self . treeAvailable . enable_model_drag_source ( gtk . gdk . BUTTON1_MASK , \
( ( ' text/uri-list ' , 0 , 0 ) , ) , gtk . gdk . ACTION_COPY )
def drag_data_get ( tree , context , selection_data , info , timestamp ) :
2009-11-30 19:41:13 +01:00
if self . config . on_drag_mark_played :
for episode in self . get_selected_episodes ( ) :
episode . mark ( is_played = True )
self . on_selected_episodes_status_changed ( )
2009-11-13 17:11:27 +01:00
uris = [ ' file:// ' + e . local_filename ( create = False ) \
for e in self . get_selected_episodes ( ) \
if e . was_downloaded ( and_exists = True ) ]
uris . append ( ' ' ) # for the trailing '\r\n'
selection_data . set ( selection_data . target , 8 , ' \r \n ' . join ( uris ) )
self . treeAvailable . connect ( ' drag-data-get ' , drag_data_get )
2009-09-05 00:12:53 +02:00
selection = self . treeAvailable . get_selection ( )
2009-09-16 23:51:18 +02:00
if gpodder . ui . diablo :
2009-09-05 17:21:13 +02:00
if self . config . maemo_enable_gestures or self . config . enable_fingerscroll :
2009-09-05 00:12:53 +02:00
selection . set_mode ( gtk . SELECTION_SINGLE )
else :
selection . set_mode ( gtk . SELECTION_MULTIPLE )
2009-09-16 23:51:18 +02:00
elif gpodder . ui . fremantle :
selection . set_mode ( gtk . SELECTION_SINGLE )
2009-09-05 00:12:53 +02:00
else :
selection . set_mode ( gtk . SELECTION_MULTIPLE )
2009-09-13 16:48:27 +02:00
# Update the sensitivity of the toolbar buttons on the Desktop
selection . connect ( ' changed ' , lambda s : self . play_or_download ( ) )
2009-05-30 13:28:16 +02:00
2009-09-16 23:51:18 +02:00
if gpodder . ui . diablo :
2009-09-07 18:18:23 +02:00
# Set up the tap-and-hold context menu for podcasts
menu = gtk . Menu ( )
menu . append ( self . itemUpdateChannel . create_menu_item ( ) )
menu . append ( self . itemEditChannel . create_menu_item ( ) )
menu . append ( gtk . SeparatorMenuItem ( ) )
menu . append ( self . itemRemoveChannel . create_menu_item ( ) )
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 ( item )
menu . show_all ( )
menu = self . set_finger_friendly ( menu )
self . treeChannels . tap_and_hold_setup ( menu )
2009-09-05 00:12:53 +02:00
def init_download_list_treeview ( self ) :
2006-06-22 23:41:32 +02:00
# enable multiple selection support
2009-04-01 01:12:17 +02:00
self . treeDownloads . get_selection ( ) . set_mode ( gtk . SELECTION_MULTIPLE )
2009-09-05 00:12:53 +02:00
self . treeDownloads . set_search_equal_func ( TreeViewHelper . make_search_equal_func ( DownloadStatusModel ) )
2009-04-01 01:12:17 +02: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-09-15 19:49:28 +02:00
if gpodder . ui . maemo :
2009-09-06 23:05:38 +02:00
cell . set_fixed_size ( 50 , 50 )
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-09-22 18:53:14 +02:00
column . add_attribute ( cell , ' markup ' , DownloadStatusModel . C_NAME )
2009-04-01 01:12:17 +02:00
column . set_sizing ( gtk . TREE_VIEW_COLUMN_AUTOSIZE )
column . set_expand ( True )
self . treeDownloads . append_column ( column )
# Second column: Progress
2009-09-22 18:53:14 +02:00
cell = gtk . CellRendererProgress ( )
cell . set_property ( ' yalign ' , .5 )
cell . set_property ( ' ypad ' , 6 )
column = gtk . TreeViewColumn ( _ ( ' Progress ' ) , cell ,
2009-08-24 16:17:32 +02:00
value = DownloadStatusModel . C_PROGRESS , \
text = DownloadStatusModel . C_PROGRESS_TEXT )
2009-09-22 18:53:14 +02:00
column . set_sizing ( gtk . TREE_VIEW_COLUMN_AUTOSIZE )
column . set_expand ( False )
2009-12-17 17:45:47 +01:00
self . treeDownloads . append_column ( column )
2009-09-22 18:53:14 +02:00
column . set_property ( ' min-width ' , 150 )
column . set_property ( ' max-width ' , 150 )
2009-04-01 01:12:17 +02:00
2009-08-24 16:17:32 +02:00
self . treeDownloads . set_model ( self . download_status_model )
2009-09-05 00:12:53 +02:00
TreeViewHelper . set ( self . treeDownloads , TreeViewHelper . ROLE_DOWNLOADS )
2009-02-09 23:26:47 +01:00
2009-09-05 00:12:53 +02:00
def on_treeview_expose_event ( self , treeview , event ) :
if event . window == treeview . get_bin_window ( ) :
model = treeview . get_model ( )
2009-09-02 19:05:32 +02:00
if ( model is not None and model . get_iter_first ( ) is not None ) :
2009-09-01 23:38:21 +02:00
return False
2009-09-05 00:12:53 +02:00
role = getattr ( treeview , TreeViewHelper . ROLE )
2009-09-01 23:38:21 +02:00
ctx = event . window . cairo_create ( )
ctx . rectangle ( event . area . x , event . area . y ,
event . area . width , event . area . height )
ctx . clip ( )
x , y , width , height , depth = event . window . get_geometry ( )
2009-09-05 00:12:53 +02:00
if role == TreeViewHelper . ROLE_EPISODES :
2009-09-02 19:05:32 +02:00
if self . currently_updating :
text = _ ( ' Loading episodes ' ) + ' ... '
elif self . config . episode_list_view_mode != \
2009-09-01 23:38:21 +02:00
EpisodeListModel . VIEW_ALL :
2009-09-21 16:03:52 +02:00
text = _ ( ' No episodes in current view ' )
2009-09-01 23:38:21 +02:00
else :
text = _ ( ' No episodes available ' )
2009-09-05 00:12:53 +02:00
elif role == TreeViewHelper . ROLE_PODCASTS :
2009-09-01 23:38:21 +02:00
if self . config . episode_list_view_mode != \
EpisodeListModel . VIEW_ALL and \
self . config . podcast_list_hide_boring and \
len ( self . channels ) > 0 :
text = _ ( ' No podcasts in this view ' )
else :
text = _ ( ' No subscriptions ' )
2009-09-05 00:12:53 +02:00
elif role == TreeViewHelper . ROLE_DOWNLOADS :
2009-09-21 16:03:52 +02:00
text = _ ( ' No active downloads ' )
2009-09-01 23:38:21 +02:00
else :
2009-09-05 00:12:53 +02:00
raise Exception ( ' on_treeview_expose_event: unknown role ' )
2009-09-01 23:38:21 +02:00
2009-09-21 16:03:52 +02:00
if gpodder . ui . fremantle :
from gpodder . gtkui . frmntl import style
font_desc = style . get_font_desc ( ' LargeSystemFont ' )
else :
font_desc = None
draw_text_box_centered ( ctx , treeview , width , height , text , font_desc )
2009-09-01 23:38:21 +02:00
return False
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-12-17 14:50:39 +01:00
def on_btnCleanUpDownloads_clicked ( self , button = None ) :
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 :
2009-12-17 14:50:39 +01:00
if task . status in ( task . DONE , task . CANCELLED ) or \
( task . status == task . FAILED and gpodder . ui . fremantle ) :
2009-05-26 13:47:47 +02:00
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-09-02 15:57:09 +02:00
# Tell the shownotes window that we have removed the episode
if self . episode_shownotes_window is not None and \
self . episode_shownotes_window . episode is not None and \
self . episode_shownotes_window . episode . url in changed_episode_urls :
self . episode_shownotes_window . _download_status_changed ( None )
2009-08-24 00:43:55 +02:00
# Update the tab title and downloads list
2009-12-17 14:50:39 +01:00
self . update_downloads_list ( from_cleanup = True )
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-12-22 17:01:47 +01:00
def add_download_task_monitor ( self , monitor ) :
self . download_task_monitors . add ( monitor )
model = self . download_status_model
if model is None :
model = ( )
for row in model :
task = row [ self . download_status_model . C_TASK ]
monitor . task_updated ( task )
def remove_download_task_monitor ( self , monitor ) :
self . download_task_monitors . remove ( monitor )
2009-12-17 14:50:39 +01:00
def update_downloads_list ( self , from_cleanup = False ) :
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-09-16 23:51:18 +02:00
downloading , failed , finished , queued , paused , others = 0 , 0 , 0 , 0 , 0 , 0
2009-06-05 11:46:20 +02:00
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-09-02 15:57:09 +02:00
# Remember the DownloadTask object for the episode that
2009-06-05 11:46:20 +02:00
# 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 :
2009-09-02 15:57:09 +02:00
shownotes_episode = self . episode_shownotes_window . episode
shownotes_task = None
2009-06-05 11:46:20 +02:00
else :
2009-09-02 15:57:09 +02:00
shownotes_episode = None
shownotes_task = 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-12-17 14:50:39 +01:00
failed_downloads = [ ]
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-12-22 17:01:47 +01:00
# Let the download task monitors know of changes
for monitor in self . download_task_monitors :
monitor . task_updated ( task )
2009-06-05 11:46:20 +02:00
total_size + = size
done_size + = size * progress
2009-04-01 01:12:17 +02:00
2009-09-02 15:57:09 +02:00
if shownotes_episode is not None and \
shownotes_episode . url == task . episode . url :
shownotes_task = task
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 :
2009-12-17 14:50:39 +01:00
failed_downloads . append ( task )
2009-06-05 11:46:20 +02:00
failed + = 1
elif status == download . DownloadTask . DONE :
finished + = 1
elif status == download . DownloadTask . QUEUED :
queued + = 1
2009-09-16 23:51:18 +02:00
elif status == download . DownloadTask . PAUSED :
paused + = 1
2009-06-05 11:46:20 +02:00
else :
others + = 1
# Remember which tasks we have seen after this run
self . download_tasks_seen = download_tasks_seen
2009-09-16 23:51:18 +02:00
if gpodder . ui . desktop :
text = [ _ ( ' Downloads ' ) ]
if downloading + failed + finished + queued > 0 :
s = [ ]
if downloading > 0 :
2009-12-21 23:18:00 +01:00
s . append ( N_ ( ' %d active ' , ' %d active ' , downloading ) % downloading )
2009-09-16 23:51:18 +02:00
if failed > 0 :
2009-12-21 23:18:00 +01:00
s . append ( N_ ( ' %d failed ' , ' %d failed ' , failed ) % failed )
2009-09-16 23:51:18 +02:00
if finished > 0 :
2009-12-21 23:18:00 +01:00
s . append ( N_ ( ' %d done ' , ' %d done ' , finished ) % finished )
2009-09-16 23:51:18 +02:00
if queued > 0 :
2009-12-21 23:18:00 +01:00
s . append ( N_ ( ' %d queued ' , ' %d queued ' , queued ) % queued )
2009-09-16 23:51:18 +02:00
text . append ( ' ( ' + ' , ' . join ( s ) + ' ) ' )
self . labelDownloads . set_text ( ' ' . join ( text ) )
elif gpodder . ui . diablo :
sum = downloading + failed + finished + queued + paused + others
2009-06-05 11:46:20 +02:00
if sum :
self . tool_downloads . set_label ( _ ( ' Downloads ( %d ) ' ) % sum )
else :
self . tool_downloads . set_label ( _ ( ' Downloads ' ) )
2009-09-16 23:51:18 +02:00
elif gpodder . ui . fremantle :
if downloading + queued > 0 :
2009-12-21 23:18:00 +01:00
self . button_downloads . set_value ( N_ ( ' %d active ' , ' %d active ' , downloading + queued ) % ( downloading + queued ) )
2009-09-16 23:51:18 +02:00
elif failed > 0 :
2009-12-21 23:18:00 +01:00
self . button_downloads . set_value ( N_ ( ' %d failed ' , ' %d failed ' , failed ) % failed )
2009-09-16 23:51:18 +02:00
elif paused > 0 :
2009-12-21 23:18:00 +01:00
self . button_downloads . set_value ( N_ ( ' %d paused ' , ' %d paused ' , paused ) % paused )
2009-09-16 23:51:18 +02:00
else :
2009-12-17 17:17:47 +01:00
self . button_downloads . set_value ( _ ( ' Idle ' ) )
2009-06-05 11:46:20 +02:00
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 :
2009-12-21 23:18:00 +01:00
title . append ( N_ ( ' downloading %d file ' , ' downloading %d files ' , count ) % count )
2009-06-05 11:46:20 +02:00
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. )
2009-12-17 14:50:39 +01:00
elif self . last_download_count > 0 and not from_cleanup :
2009-06-05 11:46:20 +02:00
if self . tray_icon is not None :
# Update the tray icon status
self . tray_icon . set_status ( )
2009-12-28 20:37:29 +01:00
if gpodder . ui . desktop :
self . downloads_finished ( self . download_tasks_seen )
2009-09-15 19:49:28 +02:00
if gpodder . ui . diablo :
2009-06-05 11:46:20 +02:00
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-12-17 14:50:39 +01:00
if gpodder . ui . fremantle and failed :
message = ' \n ' . join ( [ ' %s : %s ' % ( str ( task ) , \
task . error_message ) for task in failed_downloads ] )
self . show_message ( message , _ ( ' Downloads failed ' ) , important = True )
# Automatically remove finished downloads from the list
if self . config . auto_cleanup_downloads :
self . on_btnCleanUpDownloads_clicked ( )
2009-06-05 11:46:20 +02:00
self . last_download_count = count
2009-10-05 19:48:28 +02:00
if not gpodder . ui . fremantle :
self . gPodder . set_title ( ' - ' . join ( title ) )
2009-06-05 11:46:20 +02:00
self . update_episode_list_icons ( episode_urls )
2009-09-02 15:57:09 +02:00
if self . episode_shownotes_window is not None :
if ( shownotes_task and shownotes_task . url in episode_urls ) or \
shownotes_task != self . episode_shownotes_window . task :
self . episode_shownotes_window . _download_status_changed ( shownotes_task )
self . episode_shownotes_window . _download_status_progress ( )
2009-06-05 11:46:20 +02:00
self . play_or_download ( )
if channel_urls :
2009-09-04 03:11:01 +02:00
self . update_podcast_list_model ( 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 )
2009-08-24 23:00:25 +02:00
self . show_message ( ' %s \n \n %s ' % ( _ ( ' Please report this problem and restart gPodder: ' ) , str ( e ) ) , _ ( ' Unhandled exception ' ) , important = True )
2009-06-05 11:46:20 +02:00
# 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 on_config_changed ( self , name , old_value , new_value ) :
2009-09-15 19:49:28 +02:00
if name == ' show_toolbar ' and gpodder . ui . desktop :
2009-09-05 15:42:55 +02:00
self . toolbar . set_property ( ' visible ' , new_value )
2009-09-02 18:40:52 +02:00
elif name == ' episode_list_descriptions ' :
2009-09-04 03:11:01 +02:00
self . update_episode_list_model ( )
2010-01-07 22:30:58 +01:00
elif name == ' episode_list_thumbnails ' :
self . update_episode_list_icons ( all = True )
2009-10-13 20:09:01 +02:00
elif name == ' rotation_mode ' :
self . _fremantle_rotation . set_mode ( new_value )
2009-10-13 22:48:12 +02:00
elif name in ( ' auto_update_feeds ' , ' auto_update_frequency ' ) :
self . restart_auto_update_timer ( )
2009-12-16 14:55:55 +01:00
elif name == ' podcast_list_view_all ' :
# Force a update of the podcast list model
self . channel_list_changed = True
self . update_podcast_list_model ( )
2009-12-17 14:50:39 +01:00
elif name == ' auto_cleanup_downloads ' and new_value :
# Always cleanup when this option is enabled
self . on_btnCleanUpDownloads_clicked ( )
2008-04-24 19:42:57 +02:00
2009-09-05 00:12:53 +02:00
def on_treeview_query_tooltip ( self , treeview , x , y , keyboard_tooltip , tooltip ) :
2008-06-15 14:28:24 +02:00
# 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
2009-09-05 00:12:53 +02:00
if not getattr ( treeview , TreeViewHelper . CAN_TOOLTIP ) or ( column is not None and column != treeview . get_columns ( ) [ 0 ] ) :
setattr ( treeview , TreeViewHelper . LAST_TOOLTIP , None )
2008-06-15 14:28:24 +02:00
return False
if path is not None :
model = treeview . get_model ( )
iter = model . get_iter ( path )
2009-09-05 00:12:53 +02:00
role = getattr ( treeview , TreeViewHelper . ROLE )
2008-06-15 14:28:24 +02:00
2009-09-05 00:12:53 +02:00
if role == TreeViewHelper . ROLE_EPISODES :
id = model . get_value ( iter , EpisodeListModel . C_URL )
elif role == TreeViewHelper . ROLE_PODCASTS :
id = model . get_value ( iter , PodcastListModel . C_URL )
2008-11-19 19:29:55 +01:00
2009-09-05 00:12:53 +02:00
last_tooltip = getattr ( treeview , TreeViewHelper . LAST_TOOLTIP )
if last_tooltip is not None and last_tooltip != id :
setattr ( treeview , TreeViewHelper . LAST_TOOLTIP , None )
2009-08-13 23:19:12 +02:00
return False
2009-09-05 00:12:53 +02:00
setattr ( treeview , TreeViewHelper . LAST_TOOLTIP , id )
if role == TreeViewHelper . ROLE_EPISODES :
2009-12-27 14:27:44 +01:00
description = model . get_value ( iter , EpisodeListModel . C_TOOLTIP )
2009-09-05 00:12:53 +02:00
tooltip . set_text ( description )
elif role == TreeViewHelper . ROLE_PODCASTS :
channel = model . get_value ( iter , PodcastListModel . C_CHANNEL )
2009-12-16 14:55:55 +01:00
if channel is None :
return False
2009-09-05 00:12:53 +02:00
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
2009-08-13 23:19:12 +02:00
else :
2009-09-05 00:12:53 +02:00
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-09-05 00:12:53 +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-09-05 00:12:53 +02:00
table . show_all ( )
tooltip . set_custom ( table )
2009-08-13 23:19:12 +02:00
return True
2007-11-14 21:57:31 +01:00
2009-09-05 00:12:53 +02:00
setattr ( treeview , TreeViewHelper . LAST_TOOLTIP , None )
2007-11-14 21:57:31 +01:00
return False
2009-09-05 00:12:53 +02:00
def treeview_allow_tooltips ( self , treeview , allow ) :
setattr ( treeview , TreeViewHelper . CAN_TOOLTIP , allow )
2008-03-10 16:50:12 +01:00
def update_m3u_playlist_clicked ( self , widget ) :
2009-09-01 18:56:30 +02:00
if self . active_channel is not None :
self . active_channel . update_m3u_playlist ( )
self . show_message ( _ ( ' Updated M3U playlist in download folder. ' ) , _ ( ' Updated playlist ' ) , widget = self . treeChannels )
2008-03-10 16:50:12 +01:00
2009-09-05 00:12:53 +02:00
def treeview_handle_context_menu_click ( self , treeview , event ) :
x , y = int ( event . x ) , int ( event . y )
path , column , rx , ry = treeview . get_path_at_pos ( x , y ) or ( None , ) * 4
2009-04-01 01:12:17 +02:00
2009-09-05 00:12:53 +02:00
selection = treeview . get_selection ( )
model , paths = selection . get_selected_rows ( )
2009-04-01 01:12:17 +02:00
2009-09-05 00:12:53 +02:00
if path is None or ( path not in paths and \
event . button == self . context_menu_mouse_button ) :
# We have right-clicked, but not into the selection,
# assume we don't want to operate on the selection
2009-04-01 01:12:17 +02:00
paths = [ ]
2009-09-05 00:12:53 +02:00
if path is not None and not paths and \
event . button == self . context_menu_mouse_button :
# No selection or clicked outside selection;
# select the single item where we clicked
treeview . grab_focus ( )
treeview . set_cursor ( path , column , 0 )
paths = [ path ]
if not paths :
# Unselect any remaining items (clicked elsewhere)
if hasattr ( treeview , ' is_rubber_banding_active ' ) :
if not treeview . is_rubber_banding_active ( ) :
selection . unselect_all ( )
else :
selection . unselect_all ( )
return model , paths
2009-09-22 19:38:27 +02:00
def downloads_list_get_selection ( self , model = None , paths = None ) :
if model is None and paths is None :
selection = self . treeDownloads . get_selection ( )
model , paths = selection . get_selected_rows ( )
can_queue , can_cancel , can_pause , can_remove = ( True , ) * 4
selected_tasks = [ ( gtk . TreeRowReference ( model , path ) , \
model . get_value ( model . get_iter ( path ) , \
DownloadStatusModel . C_TASK ) ) for path in paths ]
for row_reference , task in selected_tasks :
if task . status not in ( download . DownloadTask . PAUSED , \
download . DownloadTask . FAILED , \
download . DownloadTask . CANCELLED ) :
can_queue = False
if task . status not in ( download . DownloadTask . PAUSED , \
download . DownloadTask . QUEUED , \
download . DownloadTask . DOWNLOADING ) :
can_cancel = False
if task . status not in ( download . DownloadTask . QUEUED , \
download . DownloadTask . DOWNLOADING ) :
can_pause = False
if task . status not in ( download . DownloadTask . CANCELLED , \
download . DownloadTask . FAILED , \
download . DownloadTask . DONE ) :
can_remove = False
return selected_tasks , can_queue , can_cancel , can_pause , can_remove
2009-12-28 20:37:29 +01:00
def downloads_finished ( self , download_tasks_seen ) :
# FIXME: Filter all tasks that have already been reported
finished_downloads = [ str ( task ) for task in download_tasks_seen if task . status == task . DONE ]
failed_downloads = [ str ( task ) + ' ( ' + task . error_message + ' ) ' for task in download_tasks_seen if task . status == task . FAILED ]
if finished_downloads and failed_downloads :
message = self . format_episode_list ( finished_downloads , 5 )
message + = ' \n \n <i> %s </i> \n ' % _ ( ' These downloads failed: ' )
message + = self . format_episode_list ( failed_downloads , 5 )
2010-01-25 00:55:41 +01:00
self . show_message ( message , _ ( ' Downloads finished ' ) , True , widget = self . labelDownloads )
2009-12-28 20:37:29 +01:00
elif finished_downloads :
message = self . format_episode_list ( finished_downloads )
2010-01-25 00:55:41 +01:00
self . show_message ( message , _ ( ' Downloads finished ' ) , widget = self . labelDownloads )
2009-12-28 20:37:29 +01:00
elif failed_downloads :
message = self . format_episode_list ( failed_downloads )
2010-01-25 00:55:41 +01:00
self . show_message ( message , _ ( ' Downloads failed ' ) , True , widget = self . labelDownloads )
2009-12-28 20:37:29 +01:00
def format_episode_list ( self , episode_list , max_episodes = 10 ) :
"""
Format a list of episode names for notifications
Will truncate long episode names and limit the amount of
episodes displayed ( max_episodes = 10 ) .
The episode_list parameter should be a list of strings .
"""
MAX_TITLE_LENGTH = 100
result = [ ]
for title in episode_list [ : min ( len ( episode_list ) , max_episodes ) ] :
if len ( title ) > MAX_TITLE_LENGTH :
middle = ( MAX_TITLE_LENGTH / 2 ) - 2
title = ' %s ... %s ' % ( title [ 0 : middle ] , title [ - middle : ] )
result . append ( saxutils . escape ( title ) )
result . append ( ' \n ' )
more_episodes = len ( episode_list ) - max_episodes
if more_episodes > 0 :
result . append ( ' (... ' )
result . append ( N_ ( ' %d more episode ' , ' %d more episodes ' , more_episodes ) % more_episodes )
result . append ( ' ...) ' )
return ( ' ' . join ( result ) ) . strip ( )
2009-09-22 19:38:27 +02:00
def _for_each_task_set_status ( self , tasks , status ) :
episode_urls = set ( )
model = self . treeDownloads . get_model ( )
for row_reference , task in tasks :
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 )
self . enable_download_list_update ( )
elif status == download . DownloadTask . CANCELLED :
# Cancelling a download allowed when downloading/queued
if task . status in ( task . QUEUED , task . DOWNLOADING ) :
task . status = status
# Cancelling paused downloads requires a call to .run()
elif task . status == task . PAUSED :
task . status = status
# Call run, so the partial file gets deleted
task . run ( )
elif status == download . DownloadTask . PAUSED :
# Pausing a download only when queued/downloading
if task . status in ( task . DOWNLOADING , task . QUEUED ) :
task . status = status
elif status is None :
# 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 ( ) ) )
# 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 )
episode_urls . add ( task . url )
# Tell the task that it has been removed (so it can clean up)
task . removed_from_list ( )
else :
# We can (hopefully) simply set the task status here
task . status = status
# Tell the podcasts tab to update icons for our removed podcasts
self . update_episode_list_icons ( episode_urls )
# Update the tab title and downloads list
self . update_downloads_list ( )
2009-09-05 00:12:53 +02:00
def treeview_downloads_show_context_menu ( self , treeview , event ) :
model , paths = self . treeview_handle_context_menu_click ( treeview , event )
if not paths :
if not hasattr ( treeview , ' is_rubber_banding_active ' ) :
2009-04-01 01:12:17 +02:00
return True
2009-09-05 00:12:53 +02:00
else :
return not treeview . is_rubber_banding_active ( )
if event . button == self . context_menu_mouse_button :
2009-09-22 19:38:27 +02:00
selected_tasks , can_queue , can_cancel , can_pause , can_remove = \
self . downloads_list_get_selection ( model , paths )
2009-04-01 01:12:17 +02:00
2009-09-22 19:38:27 +02:00
def make_menu_item ( label , stock_id , tasks , status , sensitive ) :
2009-04-01 01:12:17 +02:00
# This creates a menu item for selection-wide actions
item = gtk . ImageMenuItem ( label )
item . set_image ( gtk . image_new_from_stock ( stock_id , gtk . ICON_SIZE_MENU ) )
2009-09-22 19:38:27 +02:00
item . connect ( ' activate ' , lambda item : self . _for_each_task_set_status ( tasks , status ) )
item . set_sensitive ( sensitive )
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 )
2009-09-06 23:05:38 +02:00
menu . append ( self . set_finger_friendly ( item ) )
2009-04-01 01:12:17 +02:00
menu . append ( gtk . SeparatorMenuItem ( ) )
2009-09-22 19:38:27 +02:00
menu . append ( make_menu_item ( _ ( ' Download ' ) , gtk . STOCK_GO_DOWN , selected_tasks , download . DownloadTask . QUEUED , can_queue ) )
menu . append ( make_menu_item ( _ ( ' Cancel ' ) , gtk . STOCK_CANCEL , selected_tasks , download . DownloadTask . CANCELLED , can_cancel ) )
menu . append ( make_menu_item ( _ ( ' Pause ' ) , gtk . STOCK_MEDIA_PAUSE , selected_tasks , download . DownloadTask . PAUSED , can_pause ) )
2009-04-01 01:12:17 +02:00
menu . append ( gtk . SeparatorMenuItem ( ) )
2009-09-22 19:38:27 +02:00
menu . append ( make_menu_item ( _ ( ' Remove from list ' ) , gtk . STOCK_REMOVE , selected_tasks , None , can_remove ) )
2009-04-01 01:12:17 +02:00
2009-09-15 19:49:28 +02:00
if gpodder . ui . maemo :
2009-04-02 02:22:09 +02:00
# 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 ) )
2009-09-02 18:40:52 +02:00
2009-04-02 02:22:09 +02:00
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
2009-09-05 00:12:53 +02:00
def treeview_channels_show_context_menu ( self , treeview , event ) :
model , paths = self . treeview_handle_context_menu_click ( treeview , event )
if not paths :
return True
2007-09-15 16:29:37 +02:00
2009-12-16 14:55:55 +01:00
# Check for valid channel id, if there's no id then
# assume that it is a proxy channel or equivalent
# and cannot be operated with right click
if self . active_channel . id is None :
return True
2009-09-05 00:12:53 +02:00
if event . button == 3 :
2007-09-15 16:29:37 +02:00
menu = gtk . Menu ( )
2009-09-06 23:05:38 +02:00
ICON = lambda x : x
2007-11-02 07:53:30 +01:00
item = gtk . ImageMenuItem ( _ ( ' Open download folder ' ) )
2009-09-06 23:05:38 +02:00
item . set_image ( gtk . image_new_from_icon_name ( ICON ( ' 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 ' ) )
2009-09-06 23:05:38 +02:00
item . set_image ( gtk . image_new_from_stock ( gtk . STOCK_REFRESH , gtk . ICON_SIZE_MENU ) )
2008-08-30 19:23:04 +02:00
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 ' ) )
2009-09-06 23:05:38 +02:00
item . set_image ( gtk . image_new_from_icon_name ( ICON ( ' web-browser ' ) , 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
2009-09-05 00:12:53 +02:00
self . treeview_allow_tooltips ( self . treeChannels , False )
menu . connect ( ' deactivate ' , lambda menushell : self . treeview_allow_tooltips ( self . treeChannels , True ) )
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 :
2009-09-02 18:40:52 +02:00
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-09-05 00:53:46 +02:00
PRIVATE_FOLDER_ATTRIBUTE = ' _save_episodes_as_file_folder '
2009-02-09 23:26:47 +01:00
if episode . was_downloaded ( and_exists = True ) :
2009-09-05 00:53:46 +02:00
folder = getattr ( self , PRIVATE_FOLDER_ATTRIBUTE , None )
2009-02-09 23:26:47 +01:00
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-09-05 00:53:46 +02:00
setattr ( self , PRIVATE_FOLDER_ATTRIBUTE , 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-24 23:00:25 +02:00
def convert_and_send_thread ( episode ) :
2009-08-10 23:14:35 +02:00
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 )
2009-08-24 23:00:25 +02:00
self . notification ( _ ( ' Error converting file. ' ) , _ ( ' Bluetooth file transfer ' ) , important = True )
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-24 23:00:25 +02:00
threading . Thread ( target = convert_and_send_thread , args = [ episodes_to_copy ] ) . start ( )
2008-01-21 10:52:09 +01:00
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) '
2009-09-05 00:12:53 +02:00
def _treeview_button_released ( self , treeview , event ) :
xpos , ypos = TreeViewHelper . get_button_press_event ( treeview )
dy = int ( abs ( event . y - ypos ) )
dx = int ( event . x - xpos )
2009-01-19 19:00:24 +01:00
2009-09-05 00:12:53 +02:00
selection = treeview . get_selection ( )
path = treeview . get_path_at_pos ( int ( event . x ) , int ( event . y ) )
if path is None or dy > 30 :
return ( False , dx , dy )
path , column , x , y = path
selection . select_path ( path )
treeview . set_cursor ( path )
treeview . grab_focus ( )
return ( True , dx , dy )
def treeview_channels_handle_gestures ( self , treeview , event ) :
if self . currently_updating :
2009-09-07 18:18:23 +02:00
return False
2009-09-02 18:40:52 +02:00
2009-09-05 00:12:53 +02:00
selected , dx , dy = self . _treeview_button_released ( treeview , event )
2009-01-19 19:00:24 +01:00
2009-09-05 00:12:53 +02:00
if selected :
if self . config . maemo_enable_gestures :
if dx > 70 :
self . on_itemUpdateChannel_activate ( )
elif dx < - 70 :
self . on_itemEditChannel_activate ( treeview )
2007-08-26 17:20:46 +02:00
2009-09-07 18:18:23 +02:00
return False
2007-08-26 17:20:46 +02:00
2009-09-05 00:12:53 +02:00
def treeview_available_handle_gestures ( self , treeview , event ) :
selected , dx , dy = self . _treeview_button_released ( treeview , event )
if selected :
if self . config . maemo_enable_gestures :
if dx > 70 :
self . on_playback_selected_episodes ( None )
return True
elif dx < - 70 :
self . on_shownotes_selected_episodes ( None )
return True
# Pass the event to the context menu handler for treeAvailable
self . treeview_available_show_context_menu ( treeview , event )
return True
def treeview_available_show_context_menu ( self , treeview , event ) :
model , paths = self . treeview_handle_context_menu_click ( treeview , event )
if not paths :
if not hasattr ( treeview , ' is_rubber_banding_active ' ) :
2007-08-26 17:20:46 +02:00
return True
2009-09-05 00:12:53 +02:00
else :
return not treeview . is_rubber_banding_active ( )
2007-08-26 17:20:46 +02:00
2009-09-05 00:12:53 +02:00
if event . button == self . context_menu_mouse_button :
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 )
2009-09-02 15:57:09 +02:00
item . connect ( ' activate ' , self . on_item_cancel_download_activate )
2009-04-02 02:22:09 +02:00
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-09-06 23:05:38 +02:00
ICON = lambda x : x
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 ' ) )
2009-09-06 23:05:38 +02:00
item . set_image ( gtk . image_new_from_icon_name ( ICON ( ' 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 ( ) )
2009-09-06 23:05:38 +02:00
item . set_image ( gtk . image_new_from_icon_name ( ICON ( ' 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 ' ) )
2009-09-06 23:05:38 +02:00
item . set_image ( gtk . image_new_from_icon_name ( ICON ( ' web-browser ' ) , 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
2009-09-15 19:49:28 +02:00
if gpodder . ui . maemo :
2008-04-06 02:19:03 +02:00
# 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
2009-09-05 00:12:53 +02:00
self . treeview_allow_tooltips ( self . treeAvailable , False )
menu . connect ( ' deactivate ' , lambda menushell : self . treeview_allow_tooltips ( self . treeAvailable , True ) )
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 ) :
2009-09-16 23:51:18 +02:00
if not gpodder . ui . fremantle :
self . default_title = new_title
self . gPodder . set_title ( new_title )
2008-04-06 02:19:03 +02:00
2009-09-04 03:11:01 +02:00
def update_episode_list_icons ( self , urls = None , selected = False , all = False ) :
2008-12-13 13:29:45 +01:00
"""
2009-09-04 03:11:01 +02:00
Updates the status icons in the episode list .
If urls is given , it should be a list of URLs
of episodes that should be updated .
If urls is None , set ONE OF selected , all to
True ( the former updates just the selected
episodes and the latter updates all episodes ) .
2008-12-13 13:29:45 +01:00
"""
2010-01-07 22:30:58 +01:00
additional_args = ( self . episode_is_downloading , \
self . config . episode_list_descriptions and gpodder . ui . desktop , \
self . config . episode_list_thumbnails and gpodder . ui . desktop )
2009-09-04 03:11:01 +02:00
if urls is not None :
# We have a list of URLs to walk through
2010-01-07 22:30:58 +01:00
self . episode_list_model . update_by_urls ( urls , * additional_args )
2009-09-04 03:11:01 +02:00
elif selected and not all :
# We should update all selected episodes
selection = self . treeAvailable . get_selection ( )
model , paths = selection . get_selected_rows ( )
for path in reversed ( paths ) :
iter = model . get_iter ( path )
self . episode_list_model . update_by_filter_iter ( iter , \
2010-01-07 22:30:58 +01:00
* additional_args )
2009-09-04 03:11:01 +02:00
elif all and not selected :
# We update all (even the filter-hidden) episodes
2010-01-07 22:30:58 +01:00
self . episode_list_model . update_all ( * additional_args )
2009-09-04 03:11:01 +02:00
else :
# Wrong/invalid call - have to specify at least one parameter
raise ValueError ( ' Invalid call to update_episode_list_icons ' )
2008-12-13 14:41:32 +01:00
2009-09-02 15:57:09 +02:00
def episode_list_status_changed ( self , episodes ) :
2009-09-22 00:42:36 +02:00
self . update_episode_list_icons ( set ( e . url for e in episodes ) )
self . update_podcast_list_model ( set ( e . channel . url for e in episodes ) )
self . db . commit ( )
2009-09-02 15:57:09 +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 ) :
2009-11-18 00:35:08 +01:00
if gpodder . ui . desktop :
# User has to have a media player set on the Desktop, or else we
# would probably open the browser when giving a URL to xdg-open..
return ( self . config . player and self . config . player != ' default ' )
elif gpodder . ui . maemo :
# On Maemo, the default is to use the Nokia Media Player, which is
# already able to deal with HTTP URLs the right way, so we
# unconditionally enable streaming always on Maemo
return True
return False
2009-08-24 18:11:58 +02:00
def playback_episodes_for_real ( self , episodes ) :
groups = collections . defaultdict ( list )
for episode in episodes :
file_type = episode . file_type ( )
if file_type == ' video ' and self . config . videoplayer and \
self . config . videoplayer != ' default ' :
player = self . config . videoplayer
2009-09-15 19:49:28 +02:00
if gpodder . ui . diablo :
2009-09-09 00:17:54 +02:00
# Use the wrapper script if it's installed to crop 3GP YouTube
# videos to fit the screen (looks much nicer than w/ black border)
if player == ' mplayer ' and util . find_command ( ' gpodder-mplayer ' ) :
player = ' gpodder-mplayer '
2009-08-24 18:11:58 +02:00
elif file_type == ' audio ' and self . config . player and \
self . config . player != ' default ' :
player = self . config . player
else :
player = ' default '
2009-09-09 14:06:17 +02:00
if file_type not in ( ' audio ' , ' video ' ) or \
( file_type == ' audio ' and not self . config . audio_played_dbus ) or \
( file_type == ' video ' and not self . config . video_played_dbus ) :
# Mark episode as played in the database
episode . mark ( is_played = True )
2010-01-28 23:58:28 +01:00
self . mygpo_client . on_playback ( [ episode ] )
2009-09-09 14:06:17 +02:00
2009-08-24 18:11:58 +02:00
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 ' ]
2009-09-22 16:36:07 +02:00
elif gpodder . ui . maemo :
# When on Maemo and not opening with default, show a notification
# (no startup notification for Panucci / MPlayer yet...)
2009-08-05 12:18:03 +02:00
if len ( episodes ) == 1 :
2009-09-02 18:40:52 +02:00
text = _ ( ' Opening %s ' ) % episodes [ 0 ] . title
2009-08-05 12:18:03 +02:00
else :
2009-12-21 23:18:00 +01:00
count = len ( episodes )
text = N_ ( ' Opening %d episode ' , ' Opening %d episodes ' , count ) % count
2009-09-15 19:49:28 +02:00
2009-12-05 11:39:42 +01:00
banner = hildon . hildon_banner_show_animation ( self . gPodder , ' ' , text )
2009-09-15 19:49:28 +02:00
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
2009-09-22 16:36:07 +02:00
# 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 )
2010-01-28 23:58:28 +01:00
# Flush updated episode status
self . mygpo_client . flush ( )
2009-09-22 16:36:07 +02:00
def playback_episodes ( self , episodes ) :
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 )
2009-12-05 11:39:42 +01:00
if gpodder . ui . desktop :
self . show_message ( _ ( ' Please check your media player settings in the preferences dialog. ' ) , \
_ ( ' Error opening player ' ) , widget = self . toolPreferences )
else :
self . show_message ( _ ( ' Please check your media player settings in the preferences dialog. ' ) )
2009-07-13 15:32:46 +02:00
2009-09-04 03:11:01 +02:00
channel_urls = set ( )
episode_urls = set ( )
for episode in episodes :
channel_urls . add ( episode . channel . url )
episode_urls . add ( episode . url )
self . update_episode_list_icons ( episode_urls )
self . update_podcast_list_model ( channel_urls )
2007-04-03 13:21:12 +02:00
2008-06-30 03:10:18 +02:00
def play_or_download ( self ) :
2009-09-16 23:51:18 +02:00
if not gpodder . ui . fremantle :
if self . wNotebook . get_current_page ( ) > 0 :
if gpodder . ui . desktop :
self . toolCancel . set_sensitive ( True )
return
2007-03-31 04:00:30 +02:00
2009-09-15 00:21:12 +02:00
if self . currently_updating :
return ( False , False , False , False , False , False )
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
2009-09-13 16:48:27 +02:00
can_download = can_download and not can_cancel
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 and not open_instead_of_play
2007-08-26 17:20:46 +02:00
2009-09-15 19:49:28 +02:00
if gpodder . ui . desktop :
2009-09-02 18:40:52 +02:00
if open_instead_of_play :
2009-05-11 23:10:56 +02:00
self . toolPlay . set_stock_id ( gtk . STOCK_OPEN )
2009-09-02 18:40:52 +02:00
else :
2009-05-11 23:10:56 +02:00
self . toolPlay . set_stock_id ( gtk . STOCK_MEDIA_PLAY )
2009-09-05 15:42:55 +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 )
2007-08-26 17:20:46 +02:00
2009-09-16 23:51:18 +02:00
if not gpodder . ui . fremantle :
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 )
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-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 ) :
2009-09-04 03:11:01 +02:00
self . update_podcast_list_model ( )
2008-12-13 14:41:32 +01:00
self . update_episode_list_icons ( urls )
2008-04-17 17:45:29 +02:00
2009-09-04 03:11:01 +02:00
def update_podcast_list_model ( self , urls = None , selected = False , select_url = None ) :
""" Update the podcast list treeview model
If urls is given , it should list the URLs of each
podcast that has to be updated in the list .
If selected is True , only update the model contents
for the currently - selected podcast - nothing more .
The caller can optionally specify " select_url " ,
which is the URL of the podcast that is to be
selected in the list after the update is complete .
This only works if the podcast list has to be
reloaded ; i . e . something has been added or removed
since the last update of the podcast list ) .
"""
2008-12-13 13:29:45 +01:00
selection = self . treeChannels . get_selection ( )
2009-09-04 03:11:01 +02:00
model , iter = selection . get_selected ( )
2007-09-19 17:04:42 +02:00
2009-09-04 03:11:01 +02:00
if selected :
2008-12-13 13:29:45 +01:00
# very cheap! only update selected channel
2009-09-04 03:11:01 +02:00
if iter is not None :
2009-09-01 18:56:30 +02:00
self . podcast_list_model . update_by_filter_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
2009-09-04 03:11:01 +02:00
if urls is None :
2008-12-13 13:29:45 +01:00
# still cheaper than reloading the whole list
2009-09-14 20:46:42 +02:00
self . podcast_list_model . update_all ( )
2008-12-13 13:29:45 +01:00
else :
# ok, we got a bunch of urls to update
2009-09-04 03:11:01 +02:00
self . podcast_list_model . update_by_urls ( urls )
2008-09-06 22:34:35 +02:00
else :
2009-09-04 03:11:01 +02:00
if model and iter and select_url is None :
2008-09-06 22:34:35 +02:00
# Get the URL of the currently-selected podcast
2009-09-04 03:11:01 +02:00
select_url = model . get_value ( iter , PodcastListModel . C_URL )
2008-06-30 03:10:18 +02:00
2009-08-13 22:08:47 +02:00
# Update the podcast list model with new channels
2009-12-16 14:55:55 +01:00
self . podcast_list_model . set_channels ( self . db , self . config , self . channels )
2008-05-14 15:38:06 +02:00
2008-09-06 22:34:35 +02:00
try :
2009-09-04 03:11:01 +02:00
selected_iter = model . get_iter_first ( )
2008-09-06 22:34:35 +02:00
# Find the previously-selected URL in the new
# model if we have an URL (else select first)
2009-09-04 03:11:01 +02:00
if select_url is not None :
2008-09-06 22:34:35 +02:00
pos = model . get_iter_first ( )
while pos is not None :
2009-09-04 03:11:01 +02:00
url = model . get_value ( pos , PodcastListModel . C_URL )
if url == select_url :
selected_iter = pos
2008-09-06 22:34:35 +02:00
break
pos = model . iter_next ( pos )
2009-09-16 23:51:18 +02:00
if not gpodder . ui . fremantle :
if selected_iter is not None :
selection . select_iter ( selected_iter )
self . on_treeChannels_cursor_changed ( self . treeChannels )
2008-09-06 22:34:35 +02:00
except :
2009-09-04 03:11:01 +02:00
log ( ' Cannot select podcast in list ' , traceback = True , sender = self )
2008-12-13 13:29:45 +01:00
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
2009-09-04 03:11:01 +02:00
def update_episode_list_model ( self ) :
2008-03-04 12:06:55 +01:00
if self . channels and self . active_channel is not None :
2009-09-15 19:49:28 +02:00
if gpodder . ui . diablo :
2009-09-02 18:40:52 +02:00
banner = hildon . hildon_banner_show_animation ( self . gPodder , None , _ ( ' Loading episodes ' ) )
2009-01-19 19:00:24 +01:00
else :
banner = None
2009-08-13 23:19:12 +02:00
2009-09-21 19:38:35 +02:00
if gpodder . ui . fremantle :
hildon . hildon_gtk_window_set_progress_indicator ( self . episodes_window . main_window , True )
2009-01-19 19:00:24 +01:00
self . currently_updating = True
2009-09-15 00:21:12 +02:00
self . episode_list_model . clear ( )
2009-09-04 03:11:01 +02:00
def do_update_episode_list_model ( ) :
2010-01-07 22:30:58 +01:00
additional_args = ( self . episode_is_downloading , \
self . config . episode_list_descriptions and gpodder . ui . desktop , \
self . config . episode_list_thumbnails and gpodder . ui . desktop )
self . episode_list_model . add_from_channel ( self . active_channel , * additional_args )
2009-09-04 03:11:01 +02:00
def on_episode_list_model_updated ( ) :
if banner is not None :
banner . destroy ( )
2009-09-21 19:38:35 +02:00
if gpodder . ui . fremantle :
hildon . hildon_gtk_window_set_progress_indicator ( self . episodes_window . main_window , False )
2009-09-04 03:11:01 +02:00
self . treeAvailable . columns_autosize ( )
self . currently_updating = False
2009-09-15 00:21:12 +02:00
self . play_or_download ( )
2009-09-04 03:11:01 +02:00
util . idle_add ( on_episode_list_model_updated )
threading . Thread ( target = do_update_episode_list_model ) . start ( )
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
2009-09-09 00:20:24 +02:00
def offer_new_episodes ( self , channels = None ) :
new_episodes = self . get_new_episodes ( channels )
2009-08-25 16:19:14 +02:00
if new_episodes :
self . new_episodes_show ( new_episodes )
return True
return False
2008-09-23 03:48:59 +02:00
2009-09-05 20:40:35 +02:00
def add_podcast_list ( self , urls , auth_tokens = None ) :
""" Subscribe to a list of podcast given their URLs
2009-08-25 16:19:14 +02:00
2009-09-05 20:40:35 +02:00
If auth_tokens is given , it should be a dictionary
mapping URLs to ( username , password ) tuples . """
if auth_tokens is None :
auth_tokens = { }
# Sort and split the URL list into five buckets
queued , failed , existing , worked , authreq = [ ] , [ ] , [ ] , [ ] , [ ]
2009-08-25 16:19:14 +02:00
for input_url in urls :
url = util . normalize_feed_url ( input_url )
if url is None :
# Fail this one because the URL is not valid
failed . append ( input_url )
2009-09-01 18:56:30 +02:00
elif self . podcast_list_model . get_filter_path_from_url ( url ) is not None :
2009-08-25 16:19:14 +02:00
# A podcast already exists in the list for this URL
existing . append ( url )
else :
# This URL has survived the first round - queue for add
queued . append ( url )
2009-09-05 20:40:35 +02:00
if url != input_url and input_url in auth_tokens :
auth_tokens [ url ] = auth_tokens [ input_url ]
error_messages = { }
redirections = { }
2009-08-25 16:19:14 +02:00
2009-09-12 15:32:10 +02:00
progress = ProgressIndicator ( _ ( ' Adding podcasts ' ) , \
_ ( ' Please wait while episode information is downloaded. ' ) , \
parent = self . main_window )
def on_after_update ( ) :
progress . on_finished ( )
# Report already-existing subscriptions to the user
if existing :
title = _ ( ' Existing subscriptions skipped ' )
message = _ ( ' You are already subscribed to these podcasts: ' ) \
+ ' \n \n ' + ' \n ' . join ( saxutils . escape ( url ) for url in existing )
self . show_message ( message , title , widget = self . treeChannels )
# Report subscriptions that require authentication
if authreq :
retry_podcasts = { }
for url in authreq :
title = _ ( ' Podcast requires authentication ' )
message = _ ( ' Please login to %s : ' ) % ( saxutils . escape ( url ) , )
success , auth_tokens = self . show_login_dialog ( title , message )
if success :
retry_podcasts [ url ] = auth_tokens
else :
# Stop asking the user for more login data
retry_podcasts = { }
for url in authreq :
error_messages [ url ] = _ ( ' Authentication failed ' )
failed . append ( url )
break
2009-09-05 20:40:35 +02:00
2009-09-12 15:32:10 +02:00
# If we have authentication data to retry, do so here
if retry_podcasts :
self . add_podcast_list ( retry_podcasts . keys ( ) , retry_podcasts )
# Report website redirections
for url in redirections :
title = _ ( ' Website redirection detected ' )
2010-01-18 21:20:22 +01:00
message = _ ( ' The URL %(url)s redirects to %(target)s . ' ) \
2009-09-12 15:32:10 +02:00
+ ' \n \n ' + _ ( ' Do you want to visit the website now? ' )
2010-01-18 21:20:22 +01:00
message = message % { ' url ' : url , ' target ' : redirections [ url ] }
2009-09-12 15:32:10 +02:00
if self . show_confirmation ( message , title ) :
2009-09-22 16:18:45 +02:00
util . open_website ( url )
2009-09-05 20:40:35 +02:00
else :
2009-09-12 15:32:10 +02:00
break
# Report failed subscriptions to the user
if failed :
title = _ ( ' Could not add some podcasts ' )
message = _ ( ' Some podcasts could not be added to your list: ' ) \
+ ' \n \n ' + ' \n ' . join ( saxutils . escape ( ' %s : %s ' % ( url , \
error_messages . get ( url , _ ( ' Unknown ' ) ) ) ) for url in failed )
self . show_message ( message , title , important = True )
2010-01-19 23:43:59 +01:00
# Upload subscription changes to my.gpodder.org
self . mygpo_client . on_subscribe ( worked )
2009-09-12 15:32:10 +02:00
# If at least one podcast has been added, save and update all
if self . channel_list_changed :
2010-01-28 17:39:10 +01:00
# Fix URLs if mygpo has rewritten them
self . rewrite_urls_mygpo ( )
2009-09-12 15:32:10 +02:00
self . save_channels_opml ( )
# If only one podcast was added, select it after the update
if len ( worked ) == 1 :
url = worked [ 0 ]
2009-09-05 20:40:35 +02:00
else :
2009-09-12 15:32:10 +02:00
url = None
# Update the list of subscribed podcasts
self . update_feed_cache ( force_update = False , select_url_afterwards = url )
self . update_podcasts_tab ( )
# Offer to download new episodes
self . offer_new_episodes ( channels = [ c for c in self . channels if c . url in worked ] )
def thread_proc ( ) :
# After the initial sorting and splitting, try all queued podcasts
length = len ( queued )
for index , url in enumerate ( queued ) :
progress . on_progress ( float ( index ) / float ( length ) )
progress . on_message ( url )
log ( ' QUEUE RUNNER: %s ' , url , sender = self )
try :
# The URL is valid and does not exist already - subscribe!
channel = PodcastChannel . load ( self . db , url = url , create = True , \
authentication_tokens = auth_tokens . get ( url , None ) , \
max_episodes = self . config . max_episodes_per_feed , \
2009-12-12 14:04:28 +01:00
download_dir = self . config . download_dir , \
allow_empty_feeds = self . config . allow_empty_feeds )
2009-09-12 15:32:10 +02:00
try :
username , password = util . username_password_from_url ( url )
except ValueError , ve :
username , password = ( None , None )
if username is not None and channel . username is None and \
password is not None and channel . password is None :
channel . username = username
channel . password = password
channel . save ( )
self . _update_cover ( channel )
except feedcore . AuthenticationRequired :
if url in auth_tokens :
# Fail for wrong authentication data
2009-09-05 20:40:35 +02:00
error_messages [ url ] = _ ( ' Authentication failed ' )
failed . append ( url )
2009-09-12 15:32:10 +02:00
else :
# Queue for login dialog later
authreq . append ( url )
continue
except feedcore . WifiLogin , error :
redirections [ url ] = error . data
failed . append ( url )
error_messages [ url ] = _ ( ' Redirection detected ' )
continue
except Exception , e :
log ( ' Subscription error: %s ' , e , traceback = True , sender = self )
error_messages [ url ] = str ( e )
failed . append ( url )
continue
assert channel is not None
worked . append ( channel . url )
self . channels . append ( channel )
self . channel_list_changed = True
util . idle_add ( on_after_update )
threading . Thread ( target = thread_proc ) . start ( )
2008-10-20 06:17:22 +02:00
2009-08-25 16:19:14 +02:00
def save_channels_opml ( self ) :
exporter = opml . Exporter ( gpodder . subscription_file )
return exporter . write ( self . channels )
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-09-04 03:11:01 +02:00
self . update_podcast_list_model ( select_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
2009-09-16 23:51:18 +02:00
if gpodder . ui . fremantle :
2009-10-13 14:46:44 +02:00
self . button_subscribe . set_sensitive ( True )
2009-12-17 17:17:47 +01:00
self . button_refresh . set_image ( gtk . image_new_from_icon_name ( \
self . ICON_GENERAL_REFRESH , gtk . ICON_SIZE_BUTTON ) )
2009-09-16 23:51:18 +02:00
hildon . hildon_gtk_window_set_progress_indicator ( self . main_window , False )
2009-09-22 20:53:26 +02:00
self . update_podcasts_tab ( )
2009-12-17 17:17:47 +01:00
if self . feed_cache_update_cancelled :
return
2009-09-16 23:51:18 +02:00
if episodes :
2009-10-13 22:48:12 +02:00
if self . config . auto_download == ' always ' :
2009-12-21 23:18:00 +01:00
count = len ( episodes )
title = N_ ( ' Downloading %d new episode. ' , ' Downloading %d new episodes. ' , count ) % count
2009-10-13 22:48:12 +02:00
self . show_message ( title )
self . download_episode_list ( episodes )
elif self . config . auto_download == ' queue ' :
self . show_message ( _ ( ' New episodes have been added to the download list. ' ) )
self . download_episode_list_paused ( episodes )
else :
self . new_episodes_show ( episodes )
elif not self . config . auto_update_feeds :
self . show_message ( _ ( ' No new episodes. Please check for new episodes later. ' ) )
2009-09-16 23:51:18 +02:00
return
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 )
2009-09-15 19:49:28 +02:00
if gpodder . ui . maemo :
2009-04-02 15:24:54 +02:00
# 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 :
2009-12-21 23:18:00 +01:00
count = len ( episodes )
2009-07-12 21:10:11 +02:00
# New episodes are available
self . pbFeedUpdate . set_fraction ( 1.0 )
# Are we minimized and should we auto download?
2009-09-05 00:37:12 +02:00
if ( self . is_iconified ( ) 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 )
2009-12-21 23:18:00 +01:00
title = N_ ( ' Downloading %d new episode. ' , ' Downloading %d new episodes. ' , count ) % count
2009-10-13 22:48:12 +02:00
self . show_message ( title , _ ( ' New episodes available ' ) , widget = self . labelDownloads )
2009-07-12 21:10:11 +02:00
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-09-09 00:20:24 +02:00
self . new_episodes_show ( episodes , notification = True )
2009-06-11 18:01:01 +02:00
else :
2009-12-21 23:18:00 +01:00
message = N_ ( ' %d new episode available ' , ' %d new episodes available ' , count ) % count
2009-07-12 21:10:11 +02:00
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 ) :
2009-08-24 21:04:50 +02:00
if channel is not None and not os . path . exists ( channel . cover_file ) and channel . image :
2009-08-24 16:47:59 +02:00
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-09-01 14:18:24 +02:00
# Update if timeout is not reached or we update a single podcast or skipping is disabled
if channel . query_automatic_update ( ) or total == 1 or not self . config . feed_update_skipping :
channel . update ( max_episodes = self . config . max_episodes_per_feed )
else :
log ( ' Skipping update of %s (see feed_update_skipping) ' , channel . title , sender = self )
2009-08-24 16:47:59 +02:00
self . _update_cover ( channel )
2009-06-12 00:51:13 +02:00
except Exception , e :
2010-01-18 21:20:22 +01:00
d = { ' url ' : saxutils . escape ( channel . url ) , ' message ' : saxutils . escape ( str ( e ) ) }
message = _ ( ' There has been an error updating %(url)s : %(message)s ' )
self . notification ( message % d , _ ( ' Error while updating feed ' ) , widget = self . treeChannels )
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-09-16 23:51:18 +02:00
if self . feed_cache_update_cancelled :
break
if gpodder . ui . fremantle :
2009-12-17 17:17:47 +01:00
util . idle_add ( self . button_refresh . set_title , \
2010-01-18 21:20:22 +01:00
_ ( ' %(position)d / %(total)d updated ' ) % { ' position ' : updated , ' total ' : total } )
2009-09-16 23:51:18 +02:00
continue
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 ( ) :
2010-01-18 21:20:22 +01:00
d = { ' podcast ' : channel . title , ' position ' : updated , ' total ' : total }
progression = _ ( ' Updated %(podcast)s ( %(position)d / %(total)d ) ' ) % d
2009-06-11 18:01:01 +02:00
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 )
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
2009-09-15 19:49:28 +02:00
if gpodder . ui . maemo :
2009-06-11 18:01:01 +02:00
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 ) :
2009-12-17 17:17:47 +01:00
if self . updating_feed_cache :
if gpodder . ui . fremantle :
self . feed_cache_update_cancelled = True
2008-07-14 18:28:34 +02:00
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-09-04 03:11:01 +02:00
self . update_podcast_list_model ( select_url = select_url_afterwards )
2008-08-30 19:23:04 +02:00
return
2010-01-28 17:39:10 +01:00
# Fix URLs if mygpo has rewritten them
self . rewrite_urls_mygpo ( )
2008-07-14 18:28:34 +02:00
self . updating_feed_cache = True
2008-08-30 19:23:04 +02:00
if channels is None :
channels = self . channels
2009-09-16 23:51:18 +02:00
if gpodder . ui . fremantle :
hildon . hildon_gtk_window_set_progress_indicator ( self . main_window , True )
2009-12-17 17:17:47 +01:00
self . button_refresh . set_title ( _ ( ' Updating... ' ) )
2009-10-13 14:46:44 +02:00
self . button_subscribe . set_sensitive ( False )
2009-12-17 17:17:47 +01:00
self . button_refresh . set_image ( gtk . image_new_from_icon_name ( \
self . ICON_GENERAL_CLOSE , gtk . ICON_SIZE_BUTTON ) )
self . feed_cache_update_cancelled = False
2008-08-30 19:23:04 +02:00
else :
2009-09-16 23:51:18 +02:00
self . itemUpdate . set_sensitive ( False )
self . itemUpdateChannel . set_sensitive ( False )
2008-01-09 23:46:17 +01:00
2009-09-16 23:51:18 +02:00
if self . tray_icon :
self . tray_icon . set_status ( self . tray_icon . STATUS_UPDATING_FEED_CACHE )
2009-12-21 23:18:00 +01:00
2009-09-16 23:51:18 +02:00
if len ( channels ) == 1 :
text = _ ( ' Updating " %s " ... ' ) % channels [ 0 ] . title
else :
2009-12-21 23:18:00 +01:00
count = len ( channels )
text = N_ ( ' Updating %d feed... ' , ' Updating %d feeds... ' , count ) % count
2009-09-16 23:51:18 +02:00
self . pbFeedUpdate . set_text ( text )
self . pbFeedUpdate . set_fraction ( 0 )
self . feed_cache_update_cancelled = False
self . btnCancelFeedUpdate . show ( )
self . btnCancelFeedUpdate . set_sensitive ( True )
if gpodder . ui . maemo :
self . toolbarSpacer . set_expand ( False )
self . toolbarSpacer . set_draw ( True )
self . btnUpdateSelectedFeed . hide ( )
self . toolFeedUpdateProgress . show_all ( )
else :
self . btnCancelFeedUpdate . set_image ( gtk . image_new_from_stock ( gtk . STOCK_STOP , gtk . ICON_SIZE_BUTTON ) )
self . hboxUpdateFeeds . show_all ( )
self . btnUpdateFeeds . hide ( )
2009-06-11 18:01:01 +02:00
args = ( channels , select_url_afterwards )
2009-08-24 20:59:21 +02:00
threading . 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-12-12 13:35:50 +01:00
if 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 :
2009-10-13 20:09:01 +02:00
if gpodder . ui . fremantle :
self . close_gpodder ( )
elif gpodder . ui . diablo :
2008-04-06 02:19:03 +02:00
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 )
2009-08-25 12:52:23 +02:00
quit_button = dialog . add_button ( gtk . STOCK_QUIT , gtk . RESPONSE_CLOSE )
2008-01-09 23:46:17 +01:00
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 ( )
2009-08-25 12:52:23 +02:00
quit_button . grab_focus ( )
2008-01-09 23:46:17 +01:00
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 ( ) :
2010-01-19 23:43:59 +01:00
pass # FIXME: Add mygpo synchronization here
2008-12-10 23:07:10 +01:00
else :
2009-08-24 23:00:25 +02:00
self . show_message ( _ ( ' Please check your permissions and free disk space. ' ) , _ ( ' Error saving podcast list ' ) , important = True )
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
2009-09-02 15:57:09 +02:00
def delete_episode_list ( self , episodes , confirm = True ) :
if not episodes :
2009-10-13 14:36:06 +02:00
return False
2008-09-06 22:34:35 +02:00
2009-09-02 15:57:09 +02:00
count = len ( episodes )
if count == 1 :
episode = episodes [ 0 ]
if episode . is_locked :
title = _ ( ' %s is locked ' ) % saxutils . escape ( episode . title )
message = _ ( ' You cannot delete this locked episode. You must unlock it before you can delete it. ' )
self . notification ( message , title , widget = self . treeAvailable )
2009-10-13 14:36:06 +02:00
return False
2009-09-02 15:57:09 +02:00
title = _ ( ' Remove %s ? ' ) % saxutils . escape ( episode . title )
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-11-08 20:11:57 +01:00
else :
2009-12-21 23:18:00 +01:00
title = N_ ( ' Remove %d episode? ' , ' Remove %d episodes? ' , count ) % count
2009-09-02 15:57:09 +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-11-08 20:11:57 +01:00
2009-09-02 15:57:09 +02:00
locked_count = sum ( int ( e . is_locked ) for e in episodes if e . is_locked is not None )
if count == locked_count :
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 , widget = self . treeAvailable )
2009-10-13 14:36:06 +02:00
return False
2009-09-02 15:57:09 +02:00
elif locked_count > 0 :
2010-01-18 21:20:22 +01:00
title = _ ( ' Remove %(unlocked)d out of %(selected)d episodes? ' ) % { ' unlocked ' : count - locked_count , ' selected ' : count }
2009-09-02 15:57:09 +02: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. ' )
if confirm and not self . show_confirmation ( message , title ) :
2009-10-13 14:36:06 +02:00
return False
2007-11-08 20:11:57 +01:00
2009-12-16 21:57:35 +01:00
progress = ProgressIndicator ( _ ( ' Removing episodes ' ) , \
_ ( ' Please wait while episodes are deleted ' ) , \
parent = self . main_window )
def finish_deletion ( episode_urls , channel_urls ) :
progress . on_finished ( )
# Episodes have been deleted - persist the database
self . db . commit ( )
self . update_episode_list_icons ( episode_urls )
self . update_podcast_list_model ( channel_urls )
self . play_or_download ( )
def thread_proc ( ) :
episode_urls = set ( )
channel_urls = set ( )
2010-01-28 23:58:28 +01:00
episodes_status_update = [ ]
2009-12-16 21:57:35 +01:00
for idx , episode in enumerate ( episodes ) :
progress . on_progress ( float ( idx ) / float ( len ( episodes ) ) )
if episode . is_locked :
log ( ' Not deleting episode (is locked): %s ' , episode . title )
else :
log ( ' Deleting episode: %s ' , episode . title )
progress . on_message ( _ ( ' Deleting: %s ' ) % episode . title )
episode . delete_from_disk ( )
episode_urls . add ( episode . url )
channel_urls . add ( episode . channel . url )
2010-01-28 23:58:28 +01:00
episodes_status_update . append ( episode )
2009-12-16 21:57:35 +01:00
# Tell the shownotes window that we have removed the episode
if self . episode_shownotes_window is not None and \
self . episode_shownotes_window . episode is not None and \
self . episode_shownotes_window . episode . url == episode . url :
util . idle_add ( self . episode_shownotes_window . _download_status_changed , None )
2010-01-28 23:58:28 +01:00
# Notify the web service about the status update + upload
self . mygpo_client . on_delete ( episodes_status_update )
self . mygpo_client . flush ( )
2009-12-16 21:57:35 +01:00
util . idle_add ( finish_deletion , episode_urls , channel_urls )
threading . Thread ( target = thread_proc ) . start ( )
2009-04-27 14:54:02 +02:00
2009-10-13 14:36:06 +02:00
return True
2007-11-08 20:11:57 +01:00
def on_itemRemoveOldEpisodes_activate ( self , widget ) :
2009-09-15 19:49:28 +02:00
if gpodder . ui . maemo :
2009-09-08 21:35:36 +02:00
columns = (
( ' maemo_remove_markup ' , None , None , _ ( ' Episode ' ) ) ,
)
else :
columns = (
2009-08-13 20:39:00 +02:00
( ' title_markup ' , None , None , _ ( ' Episode ' ) ) ,
2008-08-13 03:47:01 +02:00
( ' filesize_prop ' , ' length ' , gobject . TYPE_INT , _ ( ' Size ' ) ) ,
( ' pubdate_prop ' , ' pubDate ' , gobject . TYPE_INT , _ ( ' Released ' ) ) ,
( ' played_prop ' , None , None , _ ( ' Status ' ) ) ,
( ' age_prop ' , None , None , _ ( ' Downloaded ' ) ) ,
2009-09-08 21:35:36 +02:00
)
2007-11-08 20:11:57 +01:00
2009-12-21 23:18:00 +01:00
msg_older_than = N_ ( ' Select older than %d day ' , ' Select older than %d days ' , self . config . episode_old_age )
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-12-21 23:18:00 +01:00
msg_older_than % self . config . episode_old_age : lambda episode : episode . age_in_days ( ) > self . config . episode_old_age ,
2007-11-08 20:11:57 +01:00
}
2009-09-08 21:35:36 +02:00
instructions = _ ( ' Select the episodes you want to delete: ' )
2007-11-08 20:11:57 +01:00
episodes = [ ]
selected = [ ]
for channel in self . channels :
2008-06-30 03:10:18 +02:00
for episode in channel . get_downloaded_episodes ( ) :
2009-12-12 14:11:52 +01:00
# Disallow deletion of locked episodes that still exist
if not episode . is_locked or not episode . file_exists ( ) :
2008-06-30 03:10:18 +02:00
episodes . append ( episode )
2009-12-12 14:11:52 +01:00
# Automatically select played and file-less episodes
selected . append ( episode . is_played or \
not episode . file_exists ( ) )
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 ) :
2009-09-04 03:11:01 +02:00
self . update_episode_list_icons ( selected = True )
self . update_podcast_list_model ( selected = 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 ) :
2009-09-01 18:56:30 +02:00
if self . active_channel is None :
return
2008-11-19 17:05:19 +01:00
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
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
2009-09-04 03:11:01 +02:00
self . update_podcast_list_model ( selected = True )
self . update_episode_list_icons ( all = True )
2008-11-19 17:05:19 +01:00
2008-08-30 19:23:04 +02:00
def on_itemUpdateChannel_activate ( self , widget = None ) :
2009-09-01 18:56:30 +02:00
if self . active_channel is None :
title = _ ( ' No podcast selected ' )
message = _ ( ' Please select a podcast in the podcasts list to update. ' )
self . show_message ( message , title , widget = self . treeChannels )
return
self . update_feed_cache ( channels = [ self . active_channel ] )
2008-08-30 19:23:04 +02:00
2009-06-11 18:01:01 +02:00
def on_itemUpdate_activate ( self , widget = None ) :
2010-01-28 17:39:10 +01:00
# Check if we have outstanding subscribe/unsubscribe actions
if self . on_add_remove_podcasts_mygpo ( ) :
log ( ' Update cancelled (received server changes) ' , sender = self )
return
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 :
2010-01-19 23:43:59 +01:00
gPodderWelcome ( self . gPodder ,
center_on_widget = self . gPodder ,
show_example_podcasts_callback = self . on_itemImportChannels_activate ,
setup_my_gpodder_callback = self . on_mygpo_settings_activate )
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 :
2010-01-18 21:20:22 +01:00
d = { ' episode ' : episode . title , ' message ' : str ( e ) }
message = _ ( ' Download error while downloading %(episode)s : %(message)s ' )
self . show_message ( message % d , _ ( ' Download error ' ) , important = True )
2009-06-04 12:35:18 +02:00
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 :
2010-01-28 23:58:28 +01:00
self . mygpo_client . on_download ( [ task . episode ] )
2009-04-01 01:12:17 +02:00
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
2010-01-28 23:58:28 +01:00
# Flush updated episode status
self . mygpo_client . flush ( )
2009-09-02 15:57:09 +02:00
def cancel_task_list ( self , tasks ) :
if not tasks :
return
for task in tasks :
if task . status in ( task . QUEUED , task . DOWNLOADING ) :
task . status = task . CANCELLED
elif task . status == task . PAUSED :
task . status = task . CANCELLED
# Call run, so the partial file gets deleted
task . run ( )
self . update_episode_list_icons ( [ task . url for task in tasks ] )
self . play_or_download ( )
# Update the tab title and downloads list
self . update_downloads_list ( )
2009-09-09 00:20:24 +02:00
def new_episodes_show ( self , episodes , notification = False ) :
2009-09-15 19:49:28 +02:00
if gpodder . ui . maemo :
2009-09-08 21:35:36 +02:00
columns = (
( ' maemo_markup ' , None , None , _ ( ' Episode ' ) ) ,
)
2009-09-09 00:20:24 +02:00
show_notification = notification
2009-09-08 21:35:36 +02:00
else :
columns = (
2009-08-13 20:39:00 +02:00
( ' title_markup ' , None , None , _ ( ' Episode ' ) ) ,
2008-08-13 03:47:01 +02:00
( ' filesize_prop ' , ' length ' , gobject . TYPE_INT , _ ( ' Size ' ) ) ,
( ' pubdate_prop ' , ' pubDate ' , gobject . TYPE_INT , _ ( ' Released ' ) ) ,
2009-09-08 21:35:36 +02:00
)
2009-09-09 00:20:24 +02:00
show_notification = False
2007-11-08 20:11:57 +01:00
2009-09-08 21:35:36 +02:00
instructions = _ ( ' Select the episodes you want to download: ' )
2007-03-10 18:42:32 +01:00
2009-09-28 14:23:21 +02:00
if self . new_episodes_window is not None :
self . new_episodes_window . main_window . destroy ( )
self . new_episodes_window = None
def download_episodes_callback ( episodes ) :
self . new_episodes_window = None
self . download_episode_list ( episodes )
self . new_episodes_window = gPodderEpisodeSelector ( self . gPodder , \
title = _ ( ' New episodes available ' ) , \
instructions = instructions , \
episodes = episodes , \
columns = columns , \
selected_default = True , \
stock_ok_button = ' gpodder-download ' , \
callback = download_episodes_callback , \
remove_callback = lambda e : e . mark_old ( ) , \
remove_action = _ ( ' Mark as old ' ) , \
remove_finished = self . episode_new_status_changed , \
_config = self . config , \
show_notification = show_notification )
2008-01-22 10:10:08 +01:00
def on_itemDownloadAllNew_activate ( self , widget , * args ) :
2009-08-25 16:19:14 +02:00
if not self . offer_new_episodes ( ) :
self . show_message ( _ ( ' Please check for new episodes later. ' ) , \
_ ( ' No new episodes available ' ) , widget = self . btnUpdateFeeds )
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-01-28 12:38:53 +01:00
def on_sync_to_ipod_activate ( self , widget , episodes = None ) :
2009-09-05 14:24:37 +02:00
self . sync_ui . on_synchronize_episodes ( self . channels , episodes )
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
2006-04-06 16:11:03 +02:00
def on_cleanup_ipod_activate ( self , widget , * args ) :
2009-09-05 14:24:37 +02:00
self . sync_ui . on_cleanup_device ( )
2006-04-06 16:11:03 +02:00
2008-11-05 08:15:23 +01:00
def on_manage_device_playlist ( self , widget ) :
2009-09-05 14:24:37 +02:00
self . sync_ui . on_manage_device_playlist ( )
2008-11-05 08:15:23 +01:00
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 :
2009-09-04 20:17:33 +02:00
self . tray_icon = GPodderStatusIcon ( self , gpodder . icon_file , self . config )
2009-08-24 18:11:58 +02:00
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 :
2009-09-05 00:37:12 +02:00
self . tray_icon . set_visible ( self . is_iconified ( ) )
2008-01-09 23:46:17 +01:00
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
2009-09-01 18:56:30 +02:00
def on_item_view_hide_boring_podcasts_toggled ( self , toggleaction ) :
self . config . podcast_list_hide_boring = toggleaction . get_active ( )
if self . config . podcast_list_hide_boring :
self . podcast_list_model . set_view_mode ( self . config . episode_list_view_mode )
else :
2009-09-09 16:44:48 +02:00
self . podcast_list_model . set_view_mode ( - 1 )
2009-09-01 18:56:30 +02:00
2009-09-19 19:48:17 +02:00
def on_item_view_podcasts_changed ( self , radioaction , current ) :
# Only on Fremantle
if current == self . item_view_podcasts_all :
self . podcast_list_model . set_view_mode ( - 1 )
elif current == self . item_view_podcasts_downloaded :
self . podcast_list_model . set_view_mode ( EpisodeListModel . VIEW_DOWNLOADED )
elif current == self . item_view_podcasts_unplayed :
self . podcast_list_model . set_view_mode ( EpisodeListModel . VIEW_UNPLAYED )
self . config . podcast_list_view_mode = self . podcast_list_model . get_view_mode ( )
2009-09-01 17:22:51 +02:00
def on_item_view_episodes_changed ( self , radioaction , current ) :
if current == self . item_view_episodes_all :
self . episode_list_model . set_view_mode ( EpisodeListModel . VIEW_ALL )
elif current == self . item_view_episodes_undeleted :
self . episode_list_model . set_view_mode ( EpisodeListModel . VIEW_UNDELETED )
elif current == self . item_view_episodes_downloaded :
self . episode_list_model . set_view_mode ( EpisodeListModel . VIEW_DOWNLOADED )
2009-09-09 16:44:48 +02:00
elif current == self . item_view_episodes_unplayed :
self . episode_list_model . set_view_mode ( EpisodeListModel . VIEW_UNPLAYED )
2009-09-01 17:22:51 +02:00
self . config . episode_list_view_mode = self . episode_list_model . get_view_mode ( )
2009-09-19 19:48:17 +02:00
if self . config . podcast_list_hide_boring and not gpodder . ui . fremantle :
2009-09-01 18:56:30 +02:00
self . podcast_list_model . set_view_mode ( self . config . episode_list_view_mode )
2007-11-08 20:11:57 +01:00
def update_item_device ( self ) :
2009-09-16 23:51:18 +02:00
if not gpodder . ui . fremantle :
if self . config . device_type != ' none ' :
self . itemDevice . set_visible ( True )
self . itemDevice . label = self . get_device_name ( )
else :
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 ( )
2009-09-15 19:49:28 +02:00
if gpodder . ui . maemo :
2009-09-05 00:12:53 +02:00
selection = self . treeAvailable . get_selection ( )
2009-09-05 17:21:13 +02:00
if self . config . maemo_enable_gestures or \
self . config . enable_fingerscroll :
2009-09-05 00:12:53 +02:00
selection . set_mode ( gtk . SELECTION_SINGLE )
else :
selection . set_mode ( gtk . SELECTION_MULTIPLE )
2007-11-08 20:11:57 +01:00
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 , \
2009-11-22 12:04:54 +01:00
user_apps_reader = self . user_apps_reader , \
2010-01-19 23:43:59 +01:00
mygpo_login = self . on_mygpo_settings_activate )
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
2009-12-27 14:35:42 +01:00
def on_goto_mygpo ( self , widget ) :
2010-01-19 23:43:59 +01:00
self . mygpo_client . open_website ( )
2008-12-08 17:10:53 +01:00
2010-01-19 23:43:59 +01:00
def on_mygpo_settings_activate ( self , action = None ) :
2010-01-25 00:53:25 +01:00
settings = MygPodderSettings ( self . main_window , \
config = self . config , \
2010-01-28 17:39:10 +01:00
mygpo_client = self . mygpo_client , \
on_send_full_subscriptions = self . on_send_full_subscriptions )
2008-12-10 23:07:10 +01:00
2009-09-24 17:32:32 +02:00
def on_itemAddChannel_activate ( self , widget = None ) :
2009-08-25 16:19:14 +02:00
gPodderAddPodcast ( self . gPodder , \
add_urls_callback = self . add_podcast_list )
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. ' )
2009-08-24 23:00:25 +02:00
self . show_message ( message , title , widget = self . treeChannels )
2006-04-06 16:11:03 +02:00
return
2007-07-11 20:28:09 +02:00
2009-09-07 18:18:23 +02:00
callback_closed = lambda : self . update_podcast_list_model ( selected = True )
gPodderChannel ( self . main_window , \
channel = self . active_channel , \
callback_closed = callback_closed , \
cover_downloader = self . cover_downloader )
2008-03-03 23:09:34 +01:00
2009-12-16 23:33:12 +01:00
def on_itemMassUnsubscribe_activate ( self , item = None ) :
columns = (
( ' title ' , None , None , _ ( ' Podcast ' ) ) ,
)
# We're abusing the Episode Selector for selecting Podcasts here,
# but it works and looks good, so why not? -- thp
gPodderEpisodeSelector ( self . main_window , \
title = _ ( ' Remove podcasts ' ) , \
instructions = _ ( ' Select the podcast you want to remove. ' ) , \
episodes = self . channels , \
columns = columns , \
size_attribute = None , \
stock_ok_button = gtk . STOCK_DELETE , \
callback = self . remove_podcast_list , \
_config = self . config )
def remove_podcast_list ( self , channels , confirm = True ) :
if not channels :
log ( ' No podcasts selected for deletion ' , sender = self )
2009-09-01 18:56:30 +02:00
return
2009-12-16 23:33:12 +01:00
if len ( channels ) == 1 :
title = _ ( ' Removing podcast ' )
info = _ ( ' Please wait while the podcast is removed ' )
message = _ ( ' Do you really want to remove this podcast and its episodes? ' )
else :
title = _ ( ' Removing podcasts ' )
info = _ ( ' Please wait while the podcasts are removed ' )
message = _ ( ' Do you really want to remove the selected podcasts and their episodes? ' )
2009-12-16 21:57:35 +01:00
2009-12-16 23:33:12 +01:00
if confirm and not self . show_confirmation ( message , title ) :
return
progress = ProgressIndicator ( title , info , parent = self . main_window )
2009-12-16 21:57:35 +01:00
2009-12-16 23:33:12 +01:00
def finish_deletion ( select_url ) :
2010-01-19 23:43:59 +01:00
# Upload subscription list changes to the web service
self . mygpo_client . on_unsubscribe ( [ c . url for c in channels ] )
2009-12-16 23:33:12 +01:00
# Re-load the channels and select the desired new channel
self . update_feed_cache ( force_update = False , select_url_afterwards = select_url )
progress . on_finished ( )
self . update_podcasts_tab ( )
2009-12-16 21:57:35 +01:00
2009-12-16 23:33:12 +01:00
def thread_proc ( ) :
select_url = None
for idx , channel in enumerate ( channels ) :
# Update the UI for correct status messages
progress . on_progress ( float ( idx ) / float ( len ( channels ) ) )
progress . on_message ( _ ( ' Removing %s ' ) % channel . title )
# Delete downloaded episodes
channel . remove_downloaded ( )
# cancel any active downloads from this channel
for episode in channel . get_all_episodes ( ) :
util . idle_add ( self . download_status_model . cancel_by_url ,
episode . url )
if len ( channels ) == 1 :
2009-12-16 21:57:35 +01:00
# get the URL of the podcast we want to select next
2010-01-19 23:43:59 +01:00
if channel in self . channels :
position = self . channels . index ( channel )
else :
position = - 1
2009-12-16 21:57:35 +01:00
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
2008-05-14 15:38:06 +02:00
2009-12-16 23:33:12 +01:00
# Remove the channel and clean the database entries
channel . delete ( purge = True )
self . channels . remove ( channel )
2008-05-14 15:38:06 +02:00
2009-12-16 23:33:12 +01:00
# Clean up downloads and download directories
self . clean_up_downloads ( )
2009-09-16 23:51:18 +02:00
2009-12-16 23:33:12 +01:00
self . channel_list_changed = True
self . save_channels_opml ( )
# The remaining stuff is to be done in the GTK main thread
util . idle_add ( finish_deletion , select_url )
threading . Thread ( target = thread_proc ) . start ( )
def on_itemRemoveChannel_activate ( self , widget , * args ) :
if self . active_channel is None :
title = _ ( ' No podcast selected ' )
message = _ ( ' Please select a podcast in the podcasts list to remove. ' )
self . show_message ( message , title , widget = self . treeChannels )
return
self . remove_podcast_list ( [ self . active_channel ] )
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 :
2009-09-15 19:49:28 +02:00
if gpodder . ui . desktop or gpodder . ui . fremantle :
# FIXME: Hildonization on Fremantle
2008-06-14 15:17:55 +02:00
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 )
2009-09-15 19:49:28 +02:00
elif gpodder . ui . diablo :
2008-06-14 15:17:55 +02:00
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-25 16:19:14 +02:00
dir = gPodderPodcastDirectory ( self . gPodder , _config = self . config , \
custom_title = _ ( ' Import podcasts from OPML file ' ) , \
add_urls_callback = self . add_podcast_list , \
hide_url_entry = True )
dir . download_opml_file ( filename )
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. ' )
2009-08-24 23:00:25 +02:00
self . show_message ( message , title , widget = self . treeChannels )
2006-12-09 02:59:53 +01:00
return
2009-09-15 19:49:28 +02:00
if gpodder . ui . desktop or gpodder . ui . fremantle :
# FIXME: Hildonization on Fremantle
2008-05-02 17:36:43 +02:00
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 )
2009-09-15 19:49:28 +02:00
elif gpodder . ui . diablo :
2008-05-02 17:36:43 +02:00
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 ) :
2009-12-21 23:18:00 +01:00
count = len ( self . channels )
title = N_ ( ' %d subscription exported ' , ' %d subscriptions exported ' , count ) % count
2009-08-24 23:00:25 +02:00
self . show_message ( _ ( ' Your podcast list has been successfully exported. ' ) , title , widget = self . treeChannels )
2008-09-10 16:02:06 +02:00
else :
2009-08-24 23:00:25 +02:00
self . show_message ( _ ( ' Could not export OPML to file. Please check your permissions. ' ) , _ ( ' OPML export failed ' ) , important = True )
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-09-24 17:32:32 +02:00
if gpodder . ui . fremantle :
gPodderPodcastDirectory . show_add_podcast_picker ( self . main_window , \
self . config . toplist_url , \
self . config . opml_url , \
self . add_podcast_list , \
self . on_itemAddChannel_activate , \
2010-01-19 23:43:59 +01:00
self . on_mygpo_settings_activate , \
2009-09-24 17:32:32 +02:00
self . show_text_edit_dialog )
else :
dir = gPodderPodcastDirectory ( self . main_window , _config = self . config , \
add_urls_callback = self . add_podcast_list )
2009-09-19 18:33:55 +02:00
util . idle_add ( dir . download_opml_file , self . config . opml_url )
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 ) :
2010-02-05 14:38:16 +01:00
util . open_website ( ' http://gpodder.org/wiki/User_Manual ' )
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-09-15 19:49:28 +02:00
if gpodder . ui . maemo :
2009-05-31 13:20:43 +02:00
util . open_website ( ' http://bugs.maemo.org/enter_bug.cgi?product=gPodder ' )
else :
2010-02-05 14:38:16 +01:00
util . open_website ( ' https://bugs.gpodder.org/enter_bug.cgi?product=gPodder ' )
2009-05-31 11:45:54 +02:00
2009-12-12 15:58:01 +01:00
def on_item_support_activate ( self , widget ) :
util . open_website ( ' http://gpodder.org/donate ' )
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__ )
2009-10-13 15:32:57 +02:00
dlg . set_comments ( _ ( ' A podcast client with focus on usability ' ) )
if not gpodder . ui . fremantle :
# Disable the URL label in Fremantle because of style issues
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
2009-09-15 19:49:28 +02:00
if gpodder . ui . desktop :
2008-04-06 02:19:03 +02:00
# For the "GUI" version, we add some more
# items to the about dialog (credits and logo)
2009-08-24 20:59:21 +02:00
app_authors = [
_ ( ' Maintainer: ' ) ,
' Thomas Perl <thpinfo.com> ' ,
]
if os . path . exists ( gpodder . credits_file ) :
credits = open ( gpodder . credits_file ) . read ( ) . strip ( ) . split ( ' \n ' )
app_authors + = [ ' ' , _ ( ' Patches, bug reports and donations by: ' ) ]
app_authors + = credits
2008-04-06 02:19:03 +02:00
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 ' )
2009-09-22 20:53:26 +02:00
elif gpodder . ui . fremantle :
for parent in dlg . vbox . get_children ( ) :
for child in parent . get_children ( ) :
if isinstance ( child , gtk . Label ) :
child . set_selectable ( False )
2009-10-13 15:32:57 +02:00
child . set_alignment ( 0.0 , 0.5 )
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 ]
2009-09-15 19:49:28 +02:00
if gpodder . ui . 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 )
2009-09-15 19:49:28 +02:00
if gpodder . ui . desktop :
2009-09-05 19:57:47 +02:00
self . toolDownload . set_sensitive ( False )
2009-09-05 15:42:55 +02:00
self . toolPlay . set_sensitive ( False )
self . toolTransfer . set_sensitive ( False )
self . toolCancel . set_sensitive ( False )
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
2009-09-15 19:49:28 +02:00
if gpodder . ui . maemo :
2008-04-06 02:19:03 +02:00
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 )
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 )
2006-12-16 18:44:13 +01:00
2009-09-04 03:11:01 +02:00
self . update_episode_list_model ( )
2005-11-21 19:21:25 +01:00
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 :
2009-08-24 23:00:25 +02:00
self . show_message ( _ ( ' Please select an episode from the episode list to display shownotes. ' ) , _ ( ' No episode selected ' ) , widget = self . treeAvailable )
2009-07-13 14:09:43 +02:00
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 )
2009-08-24 23:00:25 +02:00
self . show_message ( _ ( ' Please check your media player settings in the preferences dialog. ' ) , _ ( ' Unable to stream episode ' ) , widget = self . toolPreferences )
2009-07-13 23:32:14 +02:00
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-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-09-02 15:57:09 +02:00
_download_episode_list = self . download_episode_list , \
_playback_episodes = self . playback_episodes , \
_delete_episode_list = self . delete_episode_list , \
_episode_list_status_changed = self . episode_list_status_changed , \
2009-09-22 00:42:36 +02:00
_cancel_task_list = self . cancel_task_list , \
2009-11-18 00:35:08 +01:00
_episode_is_downloading = self . episode_is_downloading , \
_streaming_possible = self . streaming_possible ( ) )
2009-09-02 15:57:09 +02:00
self . episode_shownotes_window . show ( episode )
if self . episode_is_downloading ( episode ) :
self . update_downloads_list ( )
2009-05-12 10:36:01 +02:00
2009-10-13 22:48:12 +02:00
def restart_auto_update_timer ( self ) :
if self . _auto_update_timer_source_id is not None :
log ( ' Removing existing auto update timer. ' , sender = self )
gobject . source_remove ( self . _auto_update_timer_source_id )
self . _auto_update_timer_source_id = None
if self . config . auto_update_feeds :
interval = 60 * 1000 * self . config . auto_update_frequency
log ( ' Setting up auto update timer with interval %d . ' , \
self . config . auto_update_frequency , sender = self )
self . _auto_update_timer_source_id = gobject . timeout_add ( \
interval , self . _on_auto_update_timer )
def _on_auto_update_timer ( self ) :
log ( ' Auto update timer fired. ' , sender = self )
self . update_feed_cache ( force_update = True )
2010-01-28 17:39:10 +01:00
# Ask web service for sub changes (if enabled)
self . mygpo_client . flush ( )
2009-10-13 22:48:12 +02:00
return True
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
# 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 ( )
2009-09-02 15:57:09 +02:00
def on_item_cancel_download_activate ( self , widget ) :
if self . wNotebook . get_current_page ( ) == 0 :
selection = self . treeAvailable . get_selection ( )
( model , paths ) = selection . get_selected_rows ( )
urls = [ model . get_value ( model . get_iter ( path ) , \
self . episode_list_model . C_URL ) for path in paths ]
selected_tasks = [ task for task in self . download_tasks_seen \
if task . url in urls ]
else :
selection = self . treeDownloads . get_selection ( )
( model , paths ) = selection . get_selected_rows ( )
selected_tasks = [ model . get_value ( model . get_iter ( path ) , \
self . download_status_model . C_TASK ) for path in paths ]
self . cancel_task_list ( selected_tasks )
2005-11-23 20:53:18 +01:00
2006-07-20 15:19:19 +02:00
def on_btnCancelAll_clicked ( self , widget , * args ) :
2009-09-02 15:57:09 +02:00
self . cancel_task_list ( self . download_tasks_seen )
2009-08-24 00:43:55 +02:00
2006-03-24 20:08:59 +01:00
def on_btnDownloadedDelete_clicked ( self , widget , * args ) :
2009-07-13 14:34:43 +02:00
if self . wNotebook . get_current_page ( ) == 1 :
2009-09-02 15:57:09 +02:00
# Downloads tab visibile - skip (for now)
2009-07-13 14:34:43 +02:00
return
2009-08-10 23:14:35 +02:00
episodes = self . get_selected_episodes ( )
2009-09-02 15:57:09 +02:00
self . delete_episode_list ( episodes )
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
2009-09-15 19:49:28 +02:00
if not gpodder . ui . maemo :
2008-09-27 14:20:43 +02:00
return False
2008-04-06 02:19:03 +02:00
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
2009-09-05 00:37:12 +02:00
def on_iconify ( self ) :
if self . tray_icon :
self . gPodder . set_skip_taskbar_hint ( True )
if self . config . minimize_to_tray :
self . tray_icon . set_visible ( True )
else :
self . gPodder . set_skip_taskbar_hint ( False )
2007-07-11 13:05:02 +02:00
2009-09-05 00:37:12 +02:00
def on_uniconify ( self ) :
if self . tray_icon :
self . gPodder . set_skip_taskbar_hint ( False )
if self . config . minimize_to_tray :
self . tray_icon . set_visible ( False )
else :
2008-01-09 23:46:17 +01:00
self . gPodder . set_skip_taskbar_hint ( False )
def uniconify_main_window ( self ) :
2009-09-05 00:37:12 +02:00
if self . is_iconified ( ) :
2008-01-09 23:46:17 +01:00
self . gPodder . present ( )
def iconify_main_window ( self ) :
2009-09-05 00:37:12 +02:00
if not self . is_iconified ( ) :
2008-07-11 19:13:04 +02:00
self . gPodder . iconify ( )
def update_podcasts_tab ( self ) :
if len ( self . channels ) :
2009-09-16 23:51:18 +02:00
if gpodder . ui . fremantle :
2009-12-17 17:17:47 +01:00
self . button_refresh . set_title ( _ ( ' Check for new episodes ' ) )
self . button_refresh . show ( )
2009-09-16 23:51:18 +02:00
else :
self . label2 . set_text ( _ ( ' Podcasts ( %d ) ' ) % len ( self . channels ) )
2008-07-11 19:13:04 +02:00
else :
2009-09-16 23:51:18 +02:00
if gpodder . ui . fremantle :
2009-12-17 17:17:47 +01:00
self . button_refresh . hide ( )
2009-09-16 23:51:18 +02:00
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 ( )
2009-08-26 14:45:54 +02:00
@dbus.service.method ( gpodder . dbus_interface )
def subscribe_to_url ( self , url ) :
gPodderAddPodcast ( self . gPodder ,
add_urls_callback = self . add_podcast_list ,
preset_url = url )
2009-09-09 14:06:17 +02:00
@dbus.service.method ( gpodder . dbus_interface )
def mark_episode_played ( self , filename ) :
if filename is None :
return False
for channel in self . channels :
for episode in channel . get_all_episodes ( ) :
fn = episode . local_filename ( create = False , check_only = True )
if fn == filename :
episode . mark ( is_played = True )
self . db . commit ( )
self . update_episode_list_icons ( [ episode . url ] )
self . update_podcast_list_model ( [ episode . channel . url ] )
return True
return False
2005-11-21 19:21:25 +01:00
2009-08-26 14:45:54 +02:00
def main ( options = None ) :
2006-04-03 22:59:10 +02:00
gobject . threads_init ( )
2009-09-05 15:42:55 +02:00
gobject . set_application_name ( ' gPodder ' )
2009-09-06 23:05:38 +02:00
2009-10-13 15:05:45 +02:00
if gpodder . ui . maemo :
2009-09-06 23:05:38 +02:00
# Try to enable the custom icon theme for gPodder on Maemo
settings = gtk . settings_get_default ( )
settings . set_string_property ( ' gtk-icon-theme-name ' , \
' gpodder ' , __file__ )
2009-12-06 23:10:08 +01:00
# Extend the search path for the optified icon theme (Maemo 5)
icon_theme = gtk . icon_theme_get_default ( )
icon_theme . prepend_search_path ( ' /opt/gpodder-icon-theme/ ' )
2009-09-06 23:05:38 +02:00
2009-09-05 15:42:55 +02:00
gtk . window_set_default_icon_name ( ' gpodder ' )
2009-09-05 00:53:46 +02:00
gtk . about_dialog_set_url_hook ( lambda dlg , link , data : util . open_website ( link ) , None )
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 )
2009-11-18 00:01:15 +01:00
gpodder . load_plugins ( )
2009-08-24 18:11:58 +02:00
config = UIConfig ( gpodder . config_file )
2009-09-15 19:49:28 +02:00
if gpodder . ui . diablo :
2009-08-24 18:11:58 +02:00
# 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... ' )
2009-09-12 15:20:56 +02:00
for basedir in [ ' /media/mmc1 ' , ' /media/mmc2 ' ] + glob . glob ( ' /media/usb/* ' ) + [ ' /home/user/MyDocs ' ] :
2009-08-24 18:11:58 +02:00
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 )
2009-09-05 17:21:13 +02:00
if config . enable_fingerscroll :
2009-08-24 20:59:21 +02:00
BuilderWidget . use_fingerscroll = True
2009-09-15 19:49:28 +02:00
elif gpodder . ui . fremantle :
2009-10-13 20:09:01 +02:00
config . on_quit_ask = False
2009-05-11 21:35:33 +02:00
2009-08-24 18:11:58 +02:00
gp = gPodder ( bus_name , config )
2009-08-26 14:45:54 +02:00
# Handle options
if options . subscribe :
util . idle_add ( gp . subscribe_to_url , options . subscribe )
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