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.
This commit is contained in:
parent
7917c78ee8
commit
7c20ffd167
|
@ -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, \
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue