2009-08-13 23:36:18 +02:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
#
|
|
|
|
# gPodder - A media aggregator and podcast client
|
2011-04-01 18:59:42 +02:00
|
|
|
# Copyright (c) 2005-2011 Thomas Perl and the gPodder Team
|
2009-08-13 23:36:18 +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.
|
|
|
|
#
|
|
|
|
# gPodder is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License
|
|
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
#
|
|
|
|
|
|
|
|
|
|
|
|
#
|
|
|
|
# gpodder.gtkui.model - GUI model classes for gPodder (2009-08-13)
|
|
|
|
# Based on code from libpodcasts.py (thp, 2005-10-29)
|
|
|
|
#
|
|
|
|
|
|
|
|
import gpodder
|
|
|
|
|
|
|
|
_ = gpodder.gettext
|
|
|
|
|
|
|
|
from gpodder import util
|
2010-11-29 12:51:51 +01:00
|
|
|
from gpodder import model
|
|
|
|
from gpodder import query
|
2011-07-15 16:32:06 +02:00
|
|
|
|
|
|
|
import logging
|
|
|
|
logger = logging.getLogger(__name__)
|
2009-08-14 00:18:32 +02:00
|
|
|
|
|
|
|
from gpodder.gtkui import draw
|
2009-08-13 23:36:18 +02:00
|
|
|
|
2009-12-29 17:43:28 +01:00
|
|
|
import os
|
2009-08-13 23:36:18 +02:00
|
|
|
import gtk
|
2011-02-25 21:05:26 +01:00
|
|
|
import cgi
|
2011-03-02 10:58:15 +01:00
|
|
|
import re
|
2009-08-13 23:36:18 +02:00
|
|
|
|
2010-02-23 15:23:26 +01:00
|
|
|
try:
|
|
|
|
import gio
|
|
|
|
have_gio = True
|
|
|
|
except ImportError:
|
|
|
|
have_gio = False
|
|
|
|
|
2011-02-25 21:14:54 +01:00
|
|
|
# ----------------------------------------------------------
|
|
|
|
|
|
|
|
class GEpisode(model.PodcastEpisode):
|
2011-07-16 17:26:04 +02:00
|
|
|
__slots__ = ()
|
|
|
|
|
2011-02-25 21:14:54 +01:00
|
|
|
@property
|
|
|
|
def title_markup(self):
|
|
|
|
return '%s\n<small>%s</small>' % (cgi.escape(self.title),
|
|
|
|
cgi.escape(self.channel.title))
|
|
|
|
|
|
|
|
@property
|
|
|
|
def markup_new_episodes(self):
|
|
|
|
if self.file_size > 0:
|
|
|
|
length_str = '%s; ' % util.format_filesize(self.file_size)
|
|
|
|
else:
|
|
|
|
length_str = ''
|
|
|
|
return ('<b>%s</b>\n<small>%s'+_('released %s')+ \
|
|
|
|
'; '+_('from %s')+'</small>') % (\
|
|
|
|
cgi.escape(re.sub('\s+', ' ', self.title)), \
|
|
|
|
cgi.escape(length_str), \
|
|
|
|
cgi.escape(self.pubdate_prop), \
|
|
|
|
cgi.escape(re.sub('\s+', ' ', self.channel.title)))
|
|
|
|
|
|
|
|
@property
|
|
|
|
def markup_delete_episodes(self):
|
|
|
|
if self.total_time and self.current_position:
|
|
|
|
played_string = self.get_play_info_string()
|
|
|
|
elif not self.is_new:
|
|
|
|
played_string = _('played')
|
|
|
|
else:
|
|
|
|
played_string = _('unplayed')
|
|
|
|
downloaded_string = self.get_age_string()
|
|
|
|
if not downloaded_string:
|
|
|
|
downloaded_string = _('today')
|
|
|
|
return ('<b>%s</b>\n<small>%s; %s; '+_('downloaded %s')+ \
|
|
|
|
'; '+_('from %s')+'</small>') % (\
|
|
|
|
cgi.escape(self.title), \
|
|
|
|
cgi.escape(util.format_filesize(self.file_size)), \
|
|
|
|
cgi.escape(played_string), \
|
|
|
|
cgi.escape(downloaded_string), \
|
|
|
|
cgi.escape(self.channel.title))
|
|
|
|
|
|
|
|
class GPodcast(model.PodcastChannel):
|
2011-07-16 17:26:04 +02:00
|
|
|
__slots__ = ()
|
|
|
|
|
2011-02-25 21:14:54 +01:00
|
|
|
EpisodeClass = GEpisode
|
|
|
|
|
|
|
|
class Model(model.Model):
|
|
|
|
PodcastClass = GPodcast
|
|
|
|
|
|
|
|
# ----------------------------------------------------------
|
|
|
|
|
2011-07-28 14:58:17 +02:00
|
|
|
# Singleton indicator if a row is a section
|
|
|
|
class SeparatorMarker(object): pass
|
|
|
|
class SectionMarker(object): pass
|
|
|
|
|
2010-11-19 18:05:16 +01:00
|
|
|
class EpisodeListModel(gtk.ListStore):
|
2009-08-13 23:36:18 +02:00
|
|
|
C_URL, C_TITLE, C_FILESIZE_TEXT, C_EPISODE, C_STATUS_ICON, \
|
2009-12-27 14:27:44 +01:00
|
|
|
C_PUBLISHED_TEXT, C_DESCRIPTION, C_TOOLTIP, \
|
2009-09-09 16:44:48 +02:00
|
|
|
C_VIEW_SHOW_UNDELETED, C_VIEW_SHOW_DOWNLOADED, \
|
2010-09-27 02:35:24 +02:00
|
|
|
C_VIEW_SHOW_UNPLAYED, C_FILESIZE, C_PUBLISHED, \
|
2011-04-11 13:09:50 +02:00
|
|
|
C_TIME, C_TIME_VISIBLE, C_TOTAL_TIME, \
|
|
|
|
C_LOCKED = range(17)
|
2009-09-01 17:22:51 +02:00
|
|
|
|
2009-09-09 16:44:48 +02:00
|
|
|
VIEW_ALL, VIEW_UNDELETED, VIEW_DOWNLOADED, VIEW_UNPLAYED = range(4)
|
2009-08-13 23:36:18 +02:00
|
|
|
|
2010-11-19 18:05:16 +01:00
|
|
|
# In which steps the UI is updated for "loading" animations
|
|
|
|
_UI_UPDATE_STEP = .03
|
2010-03-01 17:48:56 +01:00
|
|
|
|
2010-11-22 14:28:27 +01:00
|
|
|
def __init__(self, on_filter_changed=lambda has_episodes: None):
|
2010-11-19 18:05:16 +01:00
|
|
|
gtk.ListStore.__init__(self, str, str, str, object, \
|
|
|
|
str, str, str, str, bool, bool, bool, \
|
2011-04-11 13:09:50 +02:00
|
|
|
int, int, str, bool, int, bool)
|
2009-09-01 17:22:51 +02:00
|
|
|
|
2010-11-22 14:28:27 +01:00
|
|
|
# Callback for when the filter / list changes, gets one parameter
|
|
|
|
# (has_episodes) that is True if the list has any episodes
|
|
|
|
self._on_filter_changed = on_filter_changed
|
2010-03-01 17:48:56 +01:00
|
|
|
|
2009-09-01 17:22:51 +02:00
|
|
|
# Filter to allow hiding some episodes
|
|
|
|
self._filter = self.filter_new()
|
2010-09-27 01:22:41 +02:00
|
|
|
self._sorter = gtk.TreeModelSort(self._filter)
|
2009-09-01 17:22:51 +02:00
|
|
|
self._view_mode = self.VIEW_ALL
|
2009-11-05 11:00:15 +01:00
|
|
|
self._search_term = None
|
2011-02-01 20:01:35 +01:00
|
|
|
self._search_term_eql = None
|
2009-09-01 17:22:51 +02:00
|
|
|
self._filter.set_visible_func(self._filter_visible_func)
|
|
|
|
|
2010-03-11 19:03:14 +01:00
|
|
|
# Are we currently showing the "all episodes" view?
|
|
|
|
self._all_episodes_view = False
|
|
|
|
|
2011-07-16 18:49:19 +02:00
|
|
|
self.ICON_AUDIO_FILE = 'audio-x-generic'
|
|
|
|
self.ICON_VIDEO_FILE = 'video-x-generic'
|
|
|
|
self.ICON_IMAGE_FILE = 'image-x-generic'
|
|
|
|
self.ICON_GENERIC_FILE = 'text-x-generic'
|
2009-09-06 23:05:38 +02:00
|
|
|
self.ICON_DOWNLOADING = gtk.STOCK_GO_DOWN
|
|
|
|
self.ICON_DELETED = gtk.STOCK_DELETE
|
2009-08-13 23:36:18 +02:00
|
|
|
|
2010-09-26 23:38:21 +02:00
|
|
|
if 'KDE_FULL_SESSION' in os.environ:
|
|
|
|
# Workaround until KDE adds all the freedesktop icons
|
|
|
|
# See https://bugs.kde.org/show_bug.cgi?id=233505 and
|
|
|
|
# http://gpodder.org/bug/553
|
2011-07-16 18:49:19 +02:00
|
|
|
self.ICON_DELETED = 'archive-remove'
|
2010-09-26 23:38:21 +02:00
|
|
|
|
2009-08-13 23:36:18 +02:00
|
|
|
|
|
|
|
def _format_filesize(self, episode):
|
2010-12-20 14:35:46 +01:00
|
|
|
if episode.file_size > 0:
|
|
|
|
return util.format_filesize(episode.file_size, 1)
|
2009-08-13 23:36:18 +02:00
|
|
|
else:
|
|
|
|
return None
|
|
|
|
|
2009-09-01 17:22:51 +02:00
|
|
|
def _filter_visible_func(self, model, iter):
|
2009-11-05 11:00:15 +01:00
|
|
|
# If searching is active, set visibility based on search text
|
|
|
|
if self._search_term is not None:
|
2010-11-29 12:51:51 +01:00
|
|
|
episode = model.get_value(iter, self.C_EPISODE)
|
|
|
|
if episode is None:
|
|
|
|
return False
|
|
|
|
|
|
|
|
try:
|
2011-02-01 20:01:35 +01:00
|
|
|
return self._search_term_eql.match(episode)
|
2010-11-29 12:51:51 +01:00
|
|
|
except Exception, e:
|
|
|
|
return True
|
2009-11-05 11:00:15 +01:00
|
|
|
|
2009-09-01 17:22:51 +02:00
|
|
|
if self._view_mode == self.VIEW_ALL:
|
|
|
|
return True
|
|
|
|
elif self._view_mode == self.VIEW_UNDELETED:
|
2009-09-09 16:44:48 +02:00
|
|
|
return model.get_value(iter, self.C_VIEW_SHOW_UNDELETED)
|
2009-09-01 17:22:51 +02:00
|
|
|
elif self._view_mode == self.VIEW_DOWNLOADED:
|
2009-09-09 16:44:48 +02:00
|
|
|
return model.get_value(iter, self.C_VIEW_SHOW_DOWNLOADED)
|
|
|
|
elif self._view_mode == self.VIEW_UNPLAYED:
|
|
|
|
return model.get_value(iter, self.C_VIEW_SHOW_UNPLAYED)
|
2009-09-01 17:22:51 +02:00
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
def get_filtered_model(self):
|
|
|
|
"""Returns a filtered version of this episode model
|
|
|
|
|
|
|
|
The filtered version should be displayed in the UI,
|
|
|
|
as this model can have some filters set that should
|
|
|
|
be reflected in the UI.
|
|
|
|
"""
|
2010-09-27 01:22:41 +02:00
|
|
|
return self._sorter
|
2009-09-01 17:22:51 +02:00
|
|
|
|
2010-11-22 14:28:27 +01:00
|
|
|
def has_episodes(self):
|
|
|
|
"""Returns True if episodes are visible (filtered)
|
|
|
|
|
|
|
|
If episodes are visible with the current filter
|
|
|
|
applied, return True (otherwise return False).
|
|
|
|
"""
|
|
|
|
return bool(len(self._filter))
|
|
|
|
|
2009-09-01 17:22:51 +02:00
|
|
|
def set_view_mode(self, new_mode):
|
|
|
|
"""Sets a new view mode for this model
|
|
|
|
|
|
|
|
After setting the view mode, the filtered model
|
|
|
|
might be updated to reflect the new mode."""
|
|
|
|
if self._view_mode != new_mode:
|
|
|
|
self._view_mode = new_mode
|
|
|
|
self._filter.refilter()
|
2010-11-22 14:28:27 +01:00
|
|
|
self._on_filter_changed(self.has_episodes())
|
2009-09-01 17:22:51 +02:00
|
|
|
|
|
|
|
def get_view_mode(self):
|
|
|
|
"""Returns the currently-set view mode"""
|
|
|
|
return self._view_mode
|
|
|
|
|
2009-11-05 11:00:15 +01:00
|
|
|
def set_search_term(self, new_term):
|
|
|
|
if self._search_term != new_term:
|
|
|
|
self._search_term = new_term
|
2011-02-01 20:01:35 +01:00
|
|
|
self._search_term_eql = query.UserEQL(new_term)
|
2009-11-05 11:00:15 +01:00
|
|
|
self._filter.refilter()
|
2010-11-22 14:28:27 +01:00
|
|
|
self._on_filter_changed(self.has_episodes())
|
2009-11-05 11:00:15 +01:00
|
|
|
|
|
|
|
def get_search_term(self):
|
|
|
|
return self._search_term
|
|
|
|
|
2011-07-16 18:04:07 +02:00
|
|
|
def _format_description(self, episode, include_description=False):
|
2010-11-19 18:05:16 +01:00
|
|
|
a, b = '', ''
|
2011-02-26 16:32:34 +01:00
|
|
|
if episode.state != gpodder.STATE_DELETED and episode.is_new:
|
2010-11-19 18:05:16 +01:00
|
|
|
a, b = '<b>', '</b>'
|
2010-03-11 19:03:14 +01:00
|
|
|
if include_description and self._all_episodes_view:
|
2011-02-25 21:05:26 +01:00
|
|
|
return '%s%s%s\n<small>%s</small>' % (a, cgi.escape(episode.title), b,
|
|
|
|
_('from %s') % cgi.escape(episode.channel.title))
|
2010-03-11 19:03:14 +01:00
|
|
|
elif include_description:
|
2011-02-25 21:05:26 +01:00
|
|
|
return '%s%s%s\n<small>%s</small>' % (a, cgi.escape(episode.title), b,
|
|
|
|
cgi.escape(episode.one_line_description()))
|
2009-12-22 00:24:26 +01:00
|
|
|
else:
|
2011-02-25 21:05:26 +01:00
|
|
|
return ''.join((a, cgi.escape(episode.title), b))
|
2009-09-01 17:22:51 +02:00
|
|
|
|
2011-07-16 18:04:07 +02:00
|
|
|
def replace_from_channel(self, channel, include_description=False,
|
2010-11-19 18:05:16 +01:00
|
|
|
treeview=None):
|
2009-08-13 23:36:18 +02:00
|
|
|
"""
|
2009-09-15 00:21:12 +02:00
|
|
|
Add episode from the given channel to this model.
|
2009-08-13 23:36:18 +02:00
|
|
|
Downloading should be a callback.
|
|
|
|
include_description should be a boolean value (True if description
|
|
|
|
is to be added to the episode row, or False if not)
|
|
|
|
"""
|
|
|
|
|
2010-11-19 18:05:16 +01:00
|
|
|
# Remove old episodes in the list store
|
|
|
|
self.clear()
|
2010-03-11 19:03:14 +01:00
|
|
|
|
2010-11-19 18:05:16 +01:00
|
|
|
if treeview is not None:
|
|
|
|
util.idle_add(treeview.queue_draw)
|
2010-11-19 15:36:39 +01:00
|
|
|
|
2010-11-19 18:05:16 +01:00
|
|
|
self._all_episodes_view = getattr(channel, 'ALL_EPISODES_PROXY', False)
|
2010-11-19 15:36:39 +01:00
|
|
|
|
2011-03-02 09:58:39 +01:00
|
|
|
# Avoid gPodder bug 1291
|
|
|
|
if channel is None:
|
|
|
|
episodes = []
|
|
|
|
else:
|
|
|
|
episodes = channel.get_all_episodes()
|
|
|
|
|
2010-11-19 18:05:16 +01:00
|
|
|
if not isinstance(episodes, list):
|
|
|
|
episodes = list(episodes)
|
|
|
|
count = len(episodes)
|
|
|
|
|
|
|
|
for position, episode in enumerate(episodes):
|
|
|
|
iter = self.append((episode.url, \
|
|
|
|
episode.title, \
|
|
|
|
self._format_filesize(episode), \
|
|
|
|
episode, \
|
|
|
|
None, \
|
|
|
|
episode.cute_pubdate(), \
|
|
|
|
'', \
|
|
|
|
'', \
|
|
|
|
True, \
|
|
|
|
True, \
|
|
|
|
True, \
|
2010-12-20 14:35:46 +01:00
|
|
|
episode.file_size, \
|
|
|
|
episode.published, \
|
2010-11-19 18:05:16 +01:00
|
|
|
episode.get_play_info_string(), \
|
2011-04-11 13:09:50 +02:00
|
|
|
bool(episode.total_time), \
|
|
|
|
episode.total_time, \
|
2011-02-26 16:48:48 +01:00
|
|
|
episode.archive))
|
2010-11-19 18:05:16 +01:00
|
|
|
|
2011-07-16 18:04:07 +02:00
|
|
|
self.update_by_iter(iter, include_description)
|
2010-11-19 18:05:16 +01:00
|
|
|
|
2010-11-22 14:28:27 +01:00
|
|
|
self._on_filter_changed(self.has_episodes())
|
2009-09-02 19:05:32 +02:00
|
|
|
|
2011-07-16 18:04:07 +02:00
|
|
|
def update_all(self, include_description=False):
|
2010-11-19 18:05:16 +01:00
|
|
|
for row in self:
|
2011-07-16 18:04:07 +02:00
|
|
|
self.update_by_iter(row.iter, include_description)
|
2009-09-04 03:11:01 +02:00
|
|
|
|
2011-07-16 18:04:07 +02:00
|
|
|
def update_by_urls(self, urls, include_description=False):
|
2010-11-19 18:05:16 +01:00
|
|
|
for row in self:
|
|
|
|
if row[self.C_URL] in urls:
|
2011-07-16 18:04:07 +02:00
|
|
|
self.update_by_iter(row.iter, include_description)
|
2009-08-13 23:36:18 +02:00
|
|
|
|
2011-07-16 18:04:07 +02:00
|
|
|
def update_by_filter_iter(self, iter, include_description=False):
|
2009-09-01 17:22:51 +02:00
|
|
|
# Convenience function for use by "outside" methods that use iters
|
|
|
|
# from the filtered episode list model (i.e. all UI things normally)
|
2010-09-27 01:22:41 +02:00
|
|
|
iter = self._sorter.convert_iter_to_child_iter(None, iter)
|
2011-07-16 18:04:07 +02:00
|
|
|
self.update_by_iter(self._filter.convert_iter_to_child_iter(iter),
|
|
|
|
include_description)
|
2009-09-01 17:22:51 +02:00
|
|
|
|
2011-07-16 18:04:07 +02:00
|
|
|
def update_by_iter(self, iter, include_description=False):
|
2010-11-19 18:05:16 +01:00
|
|
|
episode = self.get_value(iter, self.C_EPISODE)
|
2010-02-06 15:22:14 +01:00
|
|
|
|
2010-11-19 18:05:16 +01:00
|
|
|
show_bullet = False
|
|
|
|
show_padlock = False
|
|
|
|
show_missing = False
|
|
|
|
status_icon = None
|
|
|
|
tooltip = []
|
|
|
|
view_show_undeleted = True
|
|
|
|
view_show_downloaded = False
|
|
|
|
view_show_unplayed = False
|
|
|
|
icon_theme = gtk.icon_theme_get_default()
|
|
|
|
|
2011-07-16 18:04:07 +02:00
|
|
|
if episode.downloading:
|
2010-11-19 18:05:16 +01:00
|
|
|
tooltip.append(_('Downloading'))
|
|
|
|
status_icon = self.ICON_DOWNLOADING
|
|
|
|
view_show_downloaded = True
|
|
|
|
view_show_unplayed = True
|
|
|
|
else:
|
|
|
|
if episode.state == gpodder.STATE_DELETED:
|
|
|
|
tooltip.append(_('Deleted'))
|
|
|
|
status_icon = self.ICON_DELETED
|
|
|
|
view_show_undeleted = False
|
|
|
|
elif episode.state == gpodder.STATE_NORMAL and \
|
2011-02-26 16:32:34 +01:00
|
|
|
episode.is_new:
|
2010-11-19 18:05:16 +01:00
|
|
|
tooltip.append(_('New episode'))
|
|
|
|
view_show_downloaded = True
|
|
|
|
view_show_unplayed = True
|
|
|
|
elif episode.state == gpodder.STATE_DOWNLOADED:
|
|
|
|
tooltip = []
|
|
|
|
view_show_downloaded = True
|
2011-02-26 16:32:34 +01:00
|
|
|
view_show_unplayed = episode.is_new
|
|
|
|
show_bullet = episode.is_new
|
2011-02-26 16:48:48 +01:00
|
|
|
show_padlock = episode.archive
|
2010-11-19 18:05:16 +01:00
|
|
|
show_missing = not episode.file_exists()
|
|
|
|
filename = episode.local_filename(create=False, check_only=True)
|
|
|
|
|
|
|
|
file_type = episode.file_type()
|
|
|
|
if file_type == 'audio':
|
|
|
|
tooltip.append(_('Downloaded episode'))
|
|
|
|
status_icon = self.ICON_AUDIO_FILE
|
|
|
|
elif file_type == 'video':
|
|
|
|
tooltip.append(_('Downloaded video episode'))
|
|
|
|
status_icon = self.ICON_VIDEO_FILE
|
|
|
|
elif file_type == 'image':
|
|
|
|
tooltip.append(_('Downloaded image'))
|
|
|
|
status_icon = self.ICON_IMAGE_FILE
|
|
|
|
else:
|
|
|
|
tooltip.append(_('Downloaded file'))
|
|
|
|
status_icon = self.ICON_GENERIC_FILE
|
|
|
|
|
|
|
|
# Try to find a themed icon for this file
|
|
|
|
if filename is not None and have_gio:
|
|
|
|
file = gio.File(filename)
|
|
|
|
if file.query_exists():
|
|
|
|
file_info = file.query_info('*')
|
|
|
|
icon = file_info.get_icon()
|
|
|
|
for icon_name in icon.get_names():
|
|
|
|
if icon_theme.has_icon(icon_name):
|
|
|
|
status_icon = icon_name
|
|
|
|
break
|
|
|
|
|
|
|
|
if show_missing:
|
|
|
|
tooltip.append(_('missing file'))
|
|
|
|
else:
|
|
|
|
if show_bullet:
|
|
|
|
if file_type == 'image':
|
|
|
|
tooltip.append(_('never displayed'))
|
|
|
|
elif file_type in ('audio', 'video'):
|
|
|
|
tooltip.append(_('never played'))
|
|
|
|
else:
|
|
|
|
tooltip.append(_('never opened'))
|
|
|
|
else:
|
|
|
|
if file_type == 'image':
|
|
|
|
tooltip.append(_('displayed'))
|
|
|
|
elif file_type in ('audio', 'video'):
|
|
|
|
tooltip.append(_('played'))
|
|
|
|
else:
|
|
|
|
tooltip.append(_('opened'))
|
|
|
|
if show_padlock:
|
|
|
|
tooltip.append(_('deletion prevented'))
|
|
|
|
|
|
|
|
if episode.total_time > 0 and episode.current_position:
|
|
|
|
tooltip.append('%d%%' % (100.*float(episode.current_position)/float(episode.total_time),))
|
|
|
|
|
|
|
|
if episode.total_time:
|
|
|
|
total_time = util.format_time(episode.total_time)
|
|
|
|
if total_time:
|
|
|
|
tooltip.append(total_time)
|
|
|
|
|
|
|
|
tooltip = ', '.join(tooltip)
|
|
|
|
|
2011-07-16 18:04:07 +02:00
|
|
|
description = self._format_description(episode, include_description)
|
2010-11-19 18:05:16 +01:00
|
|
|
self.set(iter, \
|
|
|
|
self.C_STATUS_ICON, status_icon, \
|
|
|
|
self.C_VIEW_SHOW_UNDELETED, view_show_undeleted, \
|
|
|
|
self.C_VIEW_SHOW_DOWNLOADED, view_show_downloaded, \
|
|
|
|
self.C_VIEW_SHOW_UNPLAYED, view_show_unplayed, \
|
|
|
|
self.C_DESCRIPTION, description, \
|
|
|
|
self.C_TOOLTIP, tooltip, \
|
2011-04-11 13:09:50 +02:00
|
|
|
self.C_TIME, episode.get_play_info_string(duration_only=True), \
|
|
|
|
self.C_TIME_VISIBLE, bool(episode.total_time), \
|
|
|
|
self.C_TOTAL_TIME, episode.total_time, \
|
2011-02-26 16:48:48 +01:00
|
|
|
self.C_LOCKED, episode.archive, \
|
2010-12-20 15:00:41 +01:00
|
|
|
self.C_FILESIZE_TEXT, self._format_filesize(episode), \
|
|
|
|
self.C_FILESIZE, episode.file_size)
|
2009-08-13 23:36:18 +02:00
|
|
|
|
|
|
|
|
2009-12-16 14:55:55 +01:00
|
|
|
class PodcastChannelProxy(object):
|
2010-03-11 19:03:14 +01:00
|
|
|
ALL_EPISODES_PROXY = True
|
|
|
|
|
2009-12-16 14:55:55 +01:00
|
|
|
def __init__(self, db, config, channels):
|
|
|
|
self._db = db
|
|
|
|
self._config = config
|
|
|
|
self.channels = channels
|
|
|
|
self.title = _('All episodes')
|
|
|
|
self.description = _('from all podcasts')
|
2011-07-16 17:26:04 +02:00
|
|
|
#self.parse_error = ''
|
2009-12-16 14:55:55 +01:00
|
|
|
self.url = ''
|
|
|
|
self.id = None
|
2010-02-24 01:50:21 +01:00
|
|
|
self.cover_file = os.path.join(gpodder.images_folder, 'podcast-all.png')
|
2010-12-20 14:35:46 +01:00
|
|
|
self.pause_subscription = False
|
2011-04-01 11:57:46 +02:00
|
|
|
self.auto_archive_episodes = False
|
2009-12-16 14:55:55 +01:00
|
|
|
|
|
|
|
def __getattribute__(self, name):
|
|
|
|
try:
|
|
|
|
return object.__getattribute__(self, name)
|
|
|
|
except AttributeError:
|
2011-07-15 16:32:06 +02:00
|
|
|
logger.warn('Unsupported method call (%s)', name)
|
2009-12-16 14:55:55 +01:00
|
|
|
|
|
|
|
def get_statistics(self):
|
|
|
|
# Get the total statistics for all channels from the database
|
2011-07-16 14:30:08 +02:00
|
|
|
return self._db.get_podcast_statistics()
|
2009-12-16 14:55:55 +01:00
|
|
|
|
|
|
|
def get_all_episodes(self):
|
|
|
|
"""Returns a generator that yields every episode"""
|
2011-07-16 14:30:08 +02:00
|
|
|
return Model.sort_episodes_by_pubdate((e for c in self.channels
|
|
|
|
for e in c.get_all_episodes()), True)
|
2009-12-16 14:55:55 +01:00
|
|
|
|
|
|
|
|
2010-11-19 18:05:16 +01:00
|
|
|
class PodcastListModel(gtk.ListStore):
|
2009-08-13 23:36:18 +02:00
|
|
|
C_URL, C_TITLE, C_DESCRIPTION, C_PILL, C_CHANNEL, \
|
2009-09-01 18:56:30 +02:00
|
|
|
C_COVER, C_ERROR, C_PILL_VISIBLE, \
|
2009-09-09 16:44:48 +02:00
|
|
|
C_VIEW_SHOW_UNDELETED, C_VIEW_SHOW_DOWNLOADED, \
|
2010-09-27 02:08:34 +02:00
|
|
|
C_VIEW_SHOW_UNPLAYED, C_HAS_EPISODES, C_SEPARATOR, \
|
2011-07-27 16:26:20 +02:00
|
|
|
C_DOWNLOADS, C_COVER_VISIBLE = range(15)
|
2009-08-13 23:36:18 +02:00
|
|
|
|
2010-11-19 18:05:16 +01:00
|
|
|
SEARCH_COLUMNS = (C_TITLE, C_DESCRIPTION)
|
2009-09-05 00:12:53 +02:00
|
|
|
|
2009-12-16 14:55:55 +01:00
|
|
|
@classmethod
|
|
|
|
def row_separator_func(cls, model, iter):
|
|
|
|
return model.get_value(iter, cls.C_SEPARATOR)
|
|
|
|
|
2010-02-24 01:50:21 +01:00
|
|
|
def __init__(self, cover_downloader):
|
2010-11-19 18:05:16 +01:00
|
|
|
gtk.ListStore.__init__(self, str, str, str, gtk.gdk.Pixbuf, \
|
|
|
|
object, gtk.gdk.Pixbuf, str, bool, bool, bool, bool, \
|
2011-07-27 16:26:20 +02:00
|
|
|
bool, bool, int, bool)
|
2009-09-01 18:56:30 +02:00
|
|
|
|
|
|
|
# Filter to allow hiding some episodes
|
|
|
|
self._filter = self.filter_new()
|
2009-09-09 16:44:48 +02:00
|
|
|
self._view_mode = -1
|
2009-11-12 03:22:14 +01:00
|
|
|
self._search_term = None
|
2009-09-01 18:56:30 +02:00
|
|
|
self._filter.set_visible_func(self._filter_visible_func)
|
2009-08-13 23:36:18 +02:00
|
|
|
|
|
|
|
self._cover_cache = {}
|
2011-07-16 18:35:14 +02:00
|
|
|
self._max_image_side = 40
|
2009-08-24 16:47:59 +02:00
|
|
|
self._cover_downloader = cover_downloader
|
2009-08-13 23:36:18 +02:00
|
|
|
|
2011-07-16 18:49:19 +02:00
|
|
|
self.ICON_DISABLED = 'gtk-media-pause'
|
2010-09-27 00:08:30 +02:00
|
|
|
|
2009-09-01 18:56:30 +02:00
|
|
|
def _filter_visible_func(self, model, iter):
|
2009-11-12 03:22:14 +01:00
|
|
|
# If searching is active, set visibility based on search text
|
|
|
|
if self._search_term is not None:
|
2011-10-17 17:11:49 +02:00
|
|
|
if model.get_value(iter, self.C_CHANNEL) == SectionMarker:
|
|
|
|
return True
|
2009-11-12 03:22:14 +01:00
|
|
|
key = self._search_term.lower()
|
2010-03-20 20:23:17 +01:00
|
|
|
columns = (model.get_value(iter, c) for c in self.SEARCH_COLUMNS)
|
|
|
|
return any((key in c.lower() for c in columns if c is not None))
|
2009-11-12 03:22:14 +01:00
|
|
|
|
2009-12-16 14:55:55 +01:00
|
|
|
if model.get_value(iter, self.C_SEPARATOR):
|
|
|
|
return True
|
2011-10-17 17:11:49 +02:00
|
|
|
elif self._view_mode == EpisodeListModel.VIEW_ALL:
|
2009-09-09 16:44:48 +02:00
|
|
|
return model.get_value(iter, self.C_HAS_EPISODES)
|
2009-09-01 18:56:30 +02:00
|
|
|
elif self._view_mode == EpisodeListModel.VIEW_UNDELETED:
|
2009-09-09 16:44:48 +02:00
|
|
|
return model.get_value(iter, self.C_VIEW_SHOW_UNDELETED)
|
2009-09-01 18:56:30 +02:00
|
|
|
elif self._view_mode == EpisodeListModel.VIEW_DOWNLOADED:
|
2009-09-09 16:44:48 +02:00
|
|
|
return model.get_value(iter, self.C_VIEW_SHOW_DOWNLOADED)
|
|
|
|
elif self._view_mode == EpisodeListModel.VIEW_UNPLAYED:
|
|
|
|
return model.get_value(iter, self.C_VIEW_SHOW_UNPLAYED)
|
2009-09-01 18:56:30 +02:00
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
def get_filtered_model(self):
|
|
|
|
"""Returns a filtered version of this episode model
|
|
|
|
|
|
|
|
The filtered version should be displayed in the UI,
|
|
|
|
as this model can have some filters set that should
|
|
|
|
be reflected in the UI.
|
|
|
|
"""
|
|
|
|
return self._filter
|
|
|
|
|
|
|
|
def set_view_mode(self, new_mode):
|
|
|
|
"""Sets a new view mode for this model
|
|
|
|
|
|
|
|
After setting the view mode, the filtered model
|
|
|
|
might be updated to reflect the new mode."""
|
|
|
|
if self._view_mode != new_mode:
|
|
|
|
self._view_mode = new_mode
|
|
|
|
self._filter.refilter()
|
|
|
|
|
|
|
|
def get_view_mode(self):
|
|
|
|
"""Returns the currently-set view mode"""
|
|
|
|
return self._view_mode
|
|
|
|
|
2009-11-12 03:22:14 +01:00
|
|
|
def set_search_term(self, new_term):
|
|
|
|
if self._search_term != new_term:
|
|
|
|
self._search_term = new_term
|
|
|
|
self._filter.refilter()
|
|
|
|
|
|
|
|
def get_search_term(self):
|
|
|
|
return self._search_term
|
|
|
|
|
2009-12-16 14:55:55 +01:00
|
|
|
def enable_separators(self, channeltree):
|
|
|
|
channeltree.set_row_separator_func(self._show_row_separator)
|
|
|
|
|
|
|
|
def _show_row_separator(self, model, iter):
|
|
|
|
return model.get_value(iter, self.C_SEPARATOR)
|
2009-09-01 18:56:30 +02:00
|
|
|
|
2009-08-14 00:14:06 +02:00
|
|
|
def _resize_pixbuf_keep_ratio(self, url, pixbuf):
|
|
|
|
"""
|
|
|
|
Resizes a GTK Pixbuf but keeps its aspect ratio.
|
|
|
|
Returns None if the pixbuf does not need to be
|
|
|
|
resized or the newly resized pixbuf if it does.
|
|
|
|
"""
|
|
|
|
changed = False
|
|
|
|
result = None
|
|
|
|
|
|
|
|
if url in self._cover_cache:
|
|
|
|
return self._cover_cache[url]
|
|
|
|
|
|
|
|
# Resize if too wide
|
2009-08-14 00:18:32 +02:00
|
|
|
if pixbuf.get_width() > self._max_image_side:
|
|
|
|
f = float(self._max_image_side)/pixbuf.get_width()
|
2009-08-14 00:14:06 +02:00
|
|
|
(width, height) = (int(pixbuf.get_width()*f), int(pixbuf.get_height()*f))
|
|
|
|
pixbuf = pixbuf.scale_simple(width, height, gtk.gdk.INTERP_BILINEAR)
|
|
|
|
changed = True
|
|
|
|
|
|
|
|
# Resize if too high
|
2009-08-14 00:18:32 +02:00
|
|
|
if pixbuf.get_height() > self._max_image_side:
|
|
|
|
f = float(self._max_image_side)/pixbuf.get_height()
|
2009-08-14 00:14:06 +02:00
|
|
|
(width, height) = (int(pixbuf.get_width()*f), int(pixbuf.get_height()*f))
|
|
|
|
pixbuf = pixbuf.scale_simple(width, height, gtk.gdk.INTERP_BILINEAR)
|
|
|
|
changed = True
|
|
|
|
|
|
|
|
if changed:
|
2009-08-14 00:18:32 +02:00
|
|
|
self._cover_cache[url] = pixbuf
|
2009-08-14 00:14:06 +02:00
|
|
|
result = pixbuf
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
2009-08-13 23:36:18 +02:00
|
|
|
def _resize_pixbuf(self, url, pixbuf):
|
|
|
|
if pixbuf is None:
|
|
|
|
return None
|
|
|
|
|
2009-08-14 00:14:06 +02:00
|
|
|
return self._resize_pixbuf_keep_ratio(url, pixbuf) or pixbuf
|
2009-08-13 23:36:18 +02:00
|
|
|
|
2010-09-27 00:08:30 +02:00
|
|
|
def _overlay_pixbuf(self, pixbuf, icon):
|
|
|
|
try:
|
|
|
|
icon_theme = gtk.icon_theme_get_default()
|
|
|
|
emblem = icon_theme.load_icon(icon, self._max_image_side/2, 0)
|
|
|
|
(width, height) = (emblem.get_width(), emblem.get_height())
|
|
|
|
xpos = pixbuf.get_width() - width
|
|
|
|
ypos = pixbuf.get_height() - height
|
|
|
|
if ypos < 0:
|
|
|
|
# need to resize overlay for none standard icon size
|
|
|
|
emblem = icon_theme.load_icon(icon, pixbuf.get_height() - 1, 0)
|
|
|
|
(width, height) = (emblem.get_width(), emblem.get_height())
|
|
|
|
xpos = pixbuf.get_width() - width
|
|
|
|
ypos = pixbuf.get_height() - height
|
|
|
|
emblem.composite(pixbuf, xpos, ypos, width, height, xpos, ypos, 1, 1, gtk.gdk.INTERP_BILINEAR, 255)
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
|
|
|
|
return pixbuf
|
|
|
|
|
|
|
|
def _get_cover_image(self, channel, add_overlay=False):
|
2009-08-24 16:47:59 +02:00
|
|
|
if self._cover_downloader is None:
|
|
|
|
return None
|
|
|
|
|
|
|
|
pixbuf = self._cover_downloader.get_cover(channel, avoid_downloading=True)
|
2010-09-27 00:08:30 +02:00
|
|
|
pixbuf_overlay = self._resize_pixbuf(channel.url, pixbuf)
|
2010-12-20 14:35:46 +01:00
|
|
|
if add_overlay and channel.pause_subscription:
|
2010-09-27 00:08:30 +02:00
|
|
|
pixbuf_overlay = self._overlay_pixbuf(pixbuf_overlay, self.ICON_DISABLED)
|
|
|
|
pixbuf_overlay.saturate_and_pixelate(pixbuf_overlay, 0.0, False)
|
|
|
|
|
|
|
|
return pixbuf_overlay
|
2009-08-13 23:36:18 +02:00
|
|
|
|
2009-09-01 18:56:30 +02:00
|
|
|
def _get_pill_image(self, channel, count_downloaded, count_unplayed):
|
2009-08-13 23:36:18 +02:00
|
|
|
if count_unplayed > 0 or count_downloaded > 0:
|
|
|
|
return draw.draw_pill_pixbuf(str(count_unplayed), str(count_downloaded))
|
|
|
|
else:
|
|
|
|
return None
|
|
|
|
|
2009-12-22 00:24:26 +01:00
|
|
|
def _format_description(self, channel, total, deleted, \
|
|
|
|
new, downloaded, unplayed):
|
2011-02-25 21:05:26 +01:00
|
|
|
title_markup = cgi.escape(channel.title)
|
2010-12-20 14:35:46 +01:00
|
|
|
if not channel.pause_subscription:
|
2011-02-25 21:05:26 +01:00
|
|
|
description_markup = cgi.escape(util.get_first_line(channel.description) or ' ')
|
2010-09-27 00:08:30 +02:00
|
|
|
else:
|
2011-02-25 21:05:26 +01:00
|
|
|
description_markup = cgi.escape(_('Subscription paused'))
|
2009-08-13 23:36:18 +02:00
|
|
|
d = []
|
2009-12-22 00:24:26 +01:00
|
|
|
if new:
|
2009-08-13 23:36:18 +02:00
|
|
|
d.append('<span weight="bold">')
|
|
|
|
d.append(title_markup)
|
2009-12-22 00:24:26 +01:00
|
|
|
if new:
|
2009-08-13 23:36:18 +02:00
|
|
|
d.append('</span>')
|
2011-04-05 21:27:28 +02:00
|
|
|
|
|
|
|
if description_markup.strip():
|
|
|
|
return ''.join(d+['\n', '<small>', description_markup, '</small>'])
|
|
|
|
else:
|
|
|
|
return ''.join(d)
|
2009-08-13 23:36:18 +02:00
|
|
|
|
|
|
|
def _format_error(self, channel):
|
2011-07-16 17:26:04 +02:00
|
|
|
#if channel.parse_error:
|
|
|
|
# return str(channel.parse_error)
|
|
|
|
#else:
|
|
|
|
# return None
|
|
|
|
return None
|
2009-08-13 23:36:18 +02:00
|
|
|
|
2009-12-16 14:55:55 +01:00
|
|
|
def set_channels(self, db, config, channels):
|
2010-11-19 18:05:16 +01:00
|
|
|
# Clear the model and update the list of podcasts
|
|
|
|
self.clear()
|
|
|
|
|
|
|
|
def channel_to_row(channel, add_overlay=False):
|
2011-07-28 14:58:17 +02:00
|
|
|
return (channel.url, '', '', None, channel,
|
|
|
|
self._get_cover_image(channel, add_overlay), '', True,
|
|
|
|
True, True, True, True, False, 0, True)
|
2010-08-20 23:59:24 +02:00
|
|
|
|
2010-11-19 18:05:16 +01:00
|
|
|
if config.podcast_list_view_all and channels:
|
|
|
|
all_episodes = PodcastChannelProxy(db, config, channels)
|
|
|
|
iter = self.append(channel_to_row(all_episodes))
|
|
|
|
self.update_by_iter(iter)
|
2009-12-16 14:55:55 +01:00
|
|
|
|
2010-11-19 18:05:16 +01:00
|
|
|
# Separator item
|
2011-07-27 14:19:02 +02:00
|
|
|
if not config.podcast_list_sections:
|
2011-07-28 14:58:17 +02:00
|
|
|
self.append(('', '', '', None, SeparatorMarker, None, '',
|
|
|
|
True, True, True, True, True, True, 0, False))
|
2010-11-19 18:05:16 +01:00
|
|
|
|
2011-07-27 14:19:02 +02:00
|
|
|
def key_func(pair):
|
|
|
|
section, podcast = pair
|
|
|
|
return (section, model.Model.podcast_sort_key(podcast))
|
|
|
|
|
|
|
|
if config.podcast_list_sections:
|
|
|
|
def convert(channels):
|
|
|
|
for channel in channels:
|
2011-07-27 15:36:04 +02:00
|
|
|
yield (channel.group_by, channel)
|
2011-07-27 14:19:02 +02:00
|
|
|
else:
|
|
|
|
def convert(channels):
|
|
|
|
for channel in channels:
|
|
|
|
yield (None, channel)
|
|
|
|
|
2011-07-28 14:58:17 +02:00
|
|
|
added_sections = []
|
2011-07-27 14:19:02 +02:00
|
|
|
old_section = None
|
|
|
|
for section, channel in sorted(convert(channels), key=key_func):
|
|
|
|
if old_section != section:
|
2011-07-28 14:58:17 +02:00
|
|
|
it = self.append(('-', section, '', None, SectionMarker, None,
|
2011-07-27 16:26:20 +02:00
|
|
|
'', True, True, True, True, True, False, 0, False))
|
2011-07-28 14:58:17 +02:00
|
|
|
added_sections.append(it)
|
2011-07-27 14:19:02 +02:00
|
|
|
old_section = section
|
|
|
|
|
2010-11-19 18:05:16 +01:00
|
|
|
iter = self.append(channel_to_row(channel, True))
|
|
|
|
self.update_by_iter(iter)
|
2009-08-13 23:36:18 +02:00
|
|
|
|
2011-07-28 14:58:17 +02:00
|
|
|
# Update section header stats only after all podcasts
|
|
|
|
# have been added to the list to get the stats right
|
|
|
|
for it in added_sections:
|
|
|
|
self.update_by_iter(it)
|
|
|
|
|
2009-09-01 18:56:30 +02:00
|
|
|
def get_filter_path_from_url(self, url):
|
|
|
|
# Return the path of the filtered model for a given URL
|
|
|
|
child_path = self.get_path_from_url(url)
|
|
|
|
if child_path is None:
|
|
|
|
return None
|
|
|
|
else:
|
2009-09-01 23:38:21 +02:00
|
|
|
return self._filter.convert_child_path_to_path(child_path)
|
2009-09-01 18:56:30 +02:00
|
|
|
|
2009-08-25 16:19:14 +02:00
|
|
|
def get_path_from_url(self, url):
|
|
|
|
# Return the tree model path for a given URL
|
|
|
|
if url is None:
|
|
|
|
return None
|
|
|
|
|
2010-11-19 18:05:16 +01:00
|
|
|
for row in self:
|
|
|
|
if row[self.C_URL] == url:
|
|
|
|
return row.path
|
2009-08-25 16:19:14 +02:00
|
|
|
return None
|
|
|
|
|
2010-02-22 23:22:58 +01:00
|
|
|
def update_first_row(self):
|
|
|
|
# Update the first row in the model (for "all episodes" updates)
|
2010-11-19 18:05:16 +01:00
|
|
|
self.update_by_iter(self.get_iter_first())
|
2010-02-22 23:22:58 +01:00
|
|
|
|
2009-08-13 23:36:18 +02:00
|
|
|
def update_by_urls(self, urls):
|
|
|
|
# Given a list of URLs, update each matching row
|
2010-11-19 18:05:16 +01:00
|
|
|
for row in self:
|
|
|
|
if row[self.C_URL] in urls:
|
|
|
|
self.update_by_iter(row.iter)
|
2009-08-13 23:36:18 +02:00
|
|
|
|
2010-02-22 23:22:58 +01:00
|
|
|
def iter_is_first_row(self, iter):
|
|
|
|
iter = self._filter.convert_iter_to_child_iter(iter)
|
|
|
|
path = self.get_path(iter)
|
|
|
|
return (path == (0,))
|
|
|
|
|
2009-09-01 18:56:30 +02:00
|
|
|
def update_by_filter_iter(self, iter):
|
|
|
|
self.update_by_iter(self._filter.convert_iter_to_child_iter(iter))
|
|
|
|
|
2009-09-14 20:46:42 +02:00
|
|
|
def update_all(self):
|
2010-11-19 18:05:16 +01:00
|
|
|
for row in self:
|
|
|
|
self.update_by_iter(row.iter)
|
2009-09-14 20:46:42 +02:00
|
|
|
|
2011-07-28 14:58:17 +02:00
|
|
|
def update_sections(self):
|
|
|
|
for row in self:
|
|
|
|
if row[self.C_CHANNEL] is SectionMarker:
|
|
|
|
self.update_by_iter(row.iter)
|
|
|
|
|
2009-08-13 23:36:18 +02:00
|
|
|
def update_by_iter(self, iter):
|
2011-07-28 14:58:17 +02:00
|
|
|
if iter is None:
|
2010-11-19 18:05:16 +01:00
|
|
|
return
|
2011-07-28 14:58:17 +02:00
|
|
|
|
|
|
|
# Given a GtkTreeIter, update volatile information
|
|
|
|
channel = self.get_value(iter, self.C_CHANNEL)
|
|
|
|
|
|
|
|
if channel is SectionMarker:
|
|
|
|
section = self.get_value(iter, self.C_TITLE)
|
|
|
|
|
|
|
|
# This row is a section header - update its visibility flags
|
|
|
|
channels = [c for c in (row[self.C_CHANNEL] for row in self)
|
|
|
|
if isinstance(c, GPodcast) and c.section == section]
|
|
|
|
|
|
|
|
# Calculate the stats over all podcasts of this section
|
|
|
|
total, deleted, new, downloaded, unplayed = map(sum,
|
|
|
|
zip(*[c.get_statistics() for c in channels]))
|
|
|
|
|
|
|
|
# We could customized the section header here with the list
|
|
|
|
# of channels and their stats (i.e. add some "new" indicator)
|
|
|
|
description = '<span size="16000"> </span><b>%s</b>' % (
|
|
|
|
cgi.escape(section))
|
|
|
|
|
|
|
|
self.set(iter,
|
|
|
|
self.C_DESCRIPTION, description,
|
|
|
|
self.C_VIEW_SHOW_UNDELETED, total - deleted > 0,
|
|
|
|
self.C_VIEW_SHOW_DOWNLOADED, downloaded + new > 0,
|
|
|
|
self.C_VIEW_SHOW_UNPLAYED, unplayed + new > 0)
|
|
|
|
|
|
|
|
if (not isinstance(channel, GPodcast) and
|
|
|
|
not isinstance(channel, PodcastChannelProxy)):
|
2010-11-19 18:05:16 +01:00
|
|
|
return
|
2011-07-28 14:58:17 +02:00
|
|
|
|
2010-11-19 18:05:16 +01:00
|
|
|
total, deleted, new, downloaded, unplayed = channel.get_statistics()
|
|
|
|
description = self._format_description(channel, total, deleted, new, \
|
|
|
|
downloaded, unplayed)
|
|
|
|
|
2011-07-16 18:35:14 +02:00
|
|
|
pill_image = self._get_pill_image(channel, downloaded, unplayed)
|
2010-11-19 18:05:16 +01:00
|
|
|
|
|
|
|
self.set(iter, \
|
|
|
|
self.C_TITLE, channel.title, \
|
|
|
|
self.C_DESCRIPTION, description, \
|
|
|
|
self.C_ERROR, self._format_error(channel), \
|
|
|
|
self.C_PILL, pill_image, \
|
|
|
|
self.C_PILL_VISIBLE, pill_image != None, \
|
|
|
|
self.C_VIEW_SHOW_UNDELETED, total - deleted > 0, \
|
|
|
|
self.C_VIEW_SHOW_DOWNLOADED, downloaded + new > 0, \
|
|
|
|
self.C_VIEW_SHOW_UNPLAYED, unplayed + new > 0, \
|
|
|
|
self.C_HAS_EPISODES, total > 0, \
|
|
|
|
self.C_DOWNLOADS, downloaded)
|
2009-08-13 23:36:18 +02:00
|
|
|
|
2010-09-27 00:08:30 +02:00
|
|
|
def add_cover_by_channel(self, channel, pixbuf):
|
2009-08-13 23:36:18 +02:00
|
|
|
# Resize and add the new cover image
|
2010-09-27 00:08:30 +02:00
|
|
|
pixbuf = self._resize_pixbuf(channel.url, pixbuf)
|
2010-12-20 14:35:46 +01:00
|
|
|
if channel.pause_subscription:
|
2010-09-27 00:08:30 +02:00
|
|
|
pixbuf = self._overlay_pixbuf(pixbuf, self.ICON_DISABLED)
|
|
|
|
pixbuf.saturate_and_pixelate(pixbuf, 0.0, False)
|
|
|
|
|
2010-11-19 18:05:16 +01:00
|
|
|
for row in self:
|
|
|
|
if row[self.C_URL] == channel.url:
|
|
|
|
row[self.C_COVER] = pixbuf
|
|
|
|
break
|
2009-08-13 23:36:18 +02:00
|
|
|
|
|
|
|
def delete_cover_by_url(self, url):
|
2010-11-19 18:05:16 +01:00
|
|
|
# Remove the cover from the model
|
|
|
|
for row in self:
|
|
|
|
if row[self.C_URL] == url:
|
|
|
|
row[self.C_COVER] = None
|
|
|
|
break
|
|
|
|
|
|
|
|
# Remove the cover from the cache
|
2009-09-11 01:50:51 +02:00
|
|
|
if url in self._cover_cache:
|
|
|
|
del self._cover_cache[url]
|
2009-08-13 23:36:18 +02:00
|
|
|
|