From 7c20ffd167d7cf980d19adbaa78da042a0f00897 Mon Sep 17 00:00:00 2001 From: Thomas Perl Date: Fri, 4 Jun 2010 20:43:38 +0200 Subject: [PATCH] Determine episode duration (bug 811) This patch adds two methods for detecting the episode length (time units, not bytes): * iTunes-specific "duration" in the RSS feed * GStreamer-based length detection after download The patch also adds duration information to the tooltip in the episode list as a first step for displaying this information in the UI. --- src/gpodder/gtkui/model.py | 17 ++++++++++------ src/gpodder/model.py | 26 ++++++++++++++++++++++- src/gpodder/util.py | 51 ++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 85 insertions(+), 9 deletions(-) diff --git a/src/gpodder/gtkui/model.py b/src/gpodder/gtkui/model.py index 4a4daa25..81d92095 100644 --- a/src/gpodder/gtkui/model.py +++ b/src/gpodder/gtkui/model.py @@ -237,25 +237,25 @@ class EpisodeListModel(gtk.ListStore): show_missing = False status_icon = None status_icon_to_build_from_file = False - tooltip = '' + tooltip = [] view_show_undeleted = True view_show_downloaded = False view_show_unplayed = False icon_theme = gtk.icon_theme_get_default() if downloading is not None and downloading(episode): - tooltip = _('Downloading') + tooltip.append(_('Downloading')) status_icon = self.ICON_DOWNLOADING view_show_downloaded = True view_show_unplayed = True else: if episode.state == gpodder.STATE_DELETED: - tooltip = _('Deleted') + tooltip.append(_('Deleted')) status_icon = self.ICON_DELETED view_show_undeleted = False elif episode.state == gpodder.STATE_NORMAL and \ not episode.is_played: - tooltip = _('New episode') + tooltip.append(_('New episode')) status_icon = self.ICON_NEW view_show_downloaded = True view_show_unplayed = True @@ -322,9 +322,14 @@ class EpisodeListModel(gtk.ListStore): 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))) + tooltip.append('%d%%' % (100.*float(episode.current_position)/float(episode.total_time),)) - tooltip = ', '.join(tooltip) + if episode.total_time: + total_time = util.format_time(episode.total_time) + if total_time: + tooltip.append(total_time) + + tooltip = ', '.join(tooltip) if status_icon is not None: status_icon = self._get_tree_icon(status_icon, show_bullet, \ diff --git a/src/gpodder/model.py b/src/gpodder/model.py index 5f4da9c4..edc72a28 100644 --- a/src/gpodder/model.py +++ b/src/gpodder/model.py @@ -28,6 +28,7 @@ from gpodder import util from gpodder import feedcore from gpodder import youtube from gpodder import corestats +from gpodder import gstreamer from gpodder.liblogger import log @@ -690,6 +691,13 @@ class PodcastEpisode(PodcastModelObject): episode.link = entry.get('link', '') episode.description = entry.get('summary', '') + try: + # Parse iTunes-specific podcast duration metadata + total_time = util.parse_time(entry.get('itunes_duration', '')) + episode.total_time = total_time + except: + pass + # Fallback to subtitle if summary is not available0 if not episode.description: episode.description = entry.get('subtitle', '') @@ -804,7 +812,7 @@ class PodcastEpisode(PodcastModelObject): # Time attributes self.total_time = 0 self.current_position = 0 - self.current_position_updated = time.time() + self.current_position_updated = 0 def get_is_locked(self): return self._is_locked @@ -823,6 +831,22 @@ class PodcastEpisode(PodcastModelObject): self.state = gpodder.STATE_DOWNLOADED self.is_played = False self.length = os.path.getsize(filename) + + if not self.total_time: + try: + length = gstreamer.get_track_length(filename) + if length is not None: + length = int(length/1000) + log('Detected media length: %d seconds', length, \ + sender=self) + self.total_time = length + self.db.save_episode(self) + self.db.commit() + return + except Exception, e: + log('Error while detecting media length: %s', str(e), \ + sender=self) + self.db.save_downloaded_episode(self) self.db.commit() diff --git a/src/gpodder/util.py b/src/gpodder/util.py index 024d7569..e28f6256 100644 --- a/src/gpodder/util.py +++ b/src/gpodder/util.py @@ -1039,8 +1039,55 @@ def bluetooth_send_file(filename): else: log('Cannot send file. Please install "bluetooth-sendto" or "gnome-obex-send".') return False - - + + +def format_time(value): + """Format a seconds value to a string + + >>> format_time(0) + '00:00' + >>> format_time(20) + '00:20' + >>> format_time(3600) + '01:00:00' + >>> format_time(10921) + '03:02:01' + """ + dt = datetime.datetime.utcfromtimestamp(value) + if dt.hour == 0: + return dt.strftime('%M:%S') + else: + return dt.strftime('%H:%M:%S') + + +def parse_time(value): + """Parse a time string into seconds + >>> parse_time('00:00') + 0 + >>> parse_time('00:00:00') + 0 + >>> parse_time('00:20') + 20 + >>> parse_time('00:00:20') + 20 + >>> parse_time('01:00:00') + 3600 + >>> parse_time('03:02:01') + 10921 + """ + if not value: + raise ValueError('Invalid value: %s' % (str(value),)) + + for format in ('%H:%M:%S', '%M:%S'): + try: + t = time.strptime(value, format) + return (t.tm_hour * 60 + t.tm_min) * 60 + t.tm_sec + except ValueError, ve: + continue + + return int(value) + + def format_seconds_to_hour_min_sec(seconds): """ Take the number of seconds and format it into a -- 2.11.4.GIT