From 48391603fc793625843346917737373b891212a9 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Sat, 15 May 2010 12:14:30 -0500 Subject: [PATCH] Implementing an audio seekbar --- src/hildonize.py | 22 ++++++++++++++++++++++ src/mormonchannel_gtk.py | 1 - src/player.py | 9 +++++++++ src/stream.py | 2 ++ src/util/go_utils.py | 6 +++++- src/windows.py | 34 ++++++++++++++++++++++++++++++---- 6 files changed, 68 insertions(+), 6 deletions(-) diff --git a/src/hildonize.py b/src/hildonize.py index 34488b7..17a03f8 100644 --- a/src/hildonize.py +++ b/src/hildonize.py @@ -356,6 +356,28 @@ else: hildonize_combo_entry = _null_hildonize_combo_entry +def _null_create_seekbar(): + adjustment = gtk.Adjustment(0, 0, 101, 1, 5, 1) + seek = gtk.HScale(adjustment) + seek.set_draw_value(False) + return seek + + +def _fremantle_create_seekbar(): + seek = hildon.Seekbar() + seek.set_range(0.0, 100) + seek.set_draw_value(False) + seek.set_update_policy(gtk.UPDATE_DISCONTINUOUS) + return seek + + +try: + hildon.Seekbar + create_seekbar = _fremantle_create_seekbar +except AttributeError: + create_seekbar = _null_create_seekbar + + def _fremantle_hildonize_scrollwindow(scrolledWindow): pannableWindow = hildon.PannableArea() diff --git a/src/mormonchannel_gtk.py b/src/mormonchannel_gtk.py index 76dd812..2bb2c7f 100755 --- a/src/mormonchannel_gtk.py +++ b/src/mormonchannel_gtk.py @@ -4,7 +4,6 @@ """ @todo Restructure so there is a windows/ folder with a file per source @todo Add additional sources -@todo Audio seek bar @todo Need to confirm id's are persistent (not just for todos but broken behavior on transition) @todo Track recent @todo Persisted Pause diff --git a/src/player.py b/src/player.py index a3efa6f..6e61429 100644 --- a/src/player.py +++ b/src/player.py @@ -107,6 +107,15 @@ class Player(gobject.GObject): self._nextSearch = stream_index.AsyncWalker(stream_index.get_next) self._nextSearch.start(self.node, self._on_next_node, self._on_node_search_error) + def seek(self, percent): + target = percent * self._stream.duration + self._stream.seek_time(target) + + @property + def percent_elapsed(self): + percent = float(self._stream.elapsed) / float(self._stream.duration) + return percent + def _set_piece_by_node(self, node): assert node is None or node.is_leaf(), node if self._node is node: diff --git a/src/stream.py b/src/stream.py index 5ae4313..63afede 100644 --- a/src/stream.py +++ b/src/stream.py @@ -98,6 +98,7 @@ class GSTStream(gobject.GObject): _moduleLogger.info("Stopped") self.emit("state-change", self.STATE_STOP) + @property def elapsed(self): try: self._elapsed = self._player.query_position(self._timeFormat, None)[0] @@ -105,6 +106,7 @@ class GSTStream(gobject.GObject): pass return self._elapsed + @property def duration(self): try: self._duration = self._player.query_duration(self._timeFormat, None)[0] diff --git a/src/util/go_utils.py b/src/util/go_utils.py index 16bcf89..515041d 100644 --- a/src/util/go_utils.py +++ b/src/util/go_utils.py @@ -95,16 +95,20 @@ class Async(object): class Timeout(object): - def __init__(self, func): + def __init__(self, func, once = True): self.__func = func self.__timeoutId = None + self.__once = once def start(self, **kwds): assert self.__timeoutId is None + callback = self._on_once if self.__once else self.__func + assert len(kwds) == 1 timeoutInSeconds = kwds["seconds"] assert 0 <= timeoutInSeconds + if timeoutInSeconds == 0: self.__timeoutId = gobject.idle_add(self._on_once) else: diff --git a/src/windows.py b/src/windows.py index 4027a41..0820900 100644 --- a/src/windows.py +++ b/src/windows.py @@ -654,7 +654,10 @@ class ListWindow(BasicWindow): self._model.clear() def _select_row(self): - path = (self._get_current_row(), ) + rowIndex = self._get_current_row() + if rowIndex < 0: + return + path = (rowIndex, ) self._treeView.scroll_to_cell(path) self._treeView.get_selection().select_path(path) @@ -861,6 +864,7 @@ class ConferenceTalkWindow(BasicWindow): self._node = node self._playerNode = self._player.node self._nextSearch = None + self._updateSeek = None self.connect_auto(self._player, "state-change", self._on_player_state_change) self.connect_auto(self._player, "title-change", self._on_player_title_change) @@ -879,8 +883,12 @@ class ConferenceTalkWindow(BasicWindow): self._presenterNavigation.connect("action", self._on_nav_action) self._presenterNavigation.connect("navigating", self._on_navigating) + self._seekbar = hildonize.create_seekbar() + self._seekbar.connect("change-value", self._on_user_seek) + self._layout.pack_start(self._loadingBanner.toplevel, False, False) self._layout.pack_start(self._presenterNavigation.toplevel, True, True) + self._layout.pack_start(self._seekbar, False, False) self._window.set_title(self._node.title) @@ -890,6 +898,7 @@ class ConferenceTalkWindow(BasicWindow): self._errorBanner.toplevel.hide() self._loadingBanner.toplevel.hide() self._set_context(self._player.state) + self._seekbar.hide() def jump_to(self, node): assert self._node is node @@ -920,11 +929,28 @@ class ConferenceTalkWindow(BasicWindow): _moduleLogger.info("Unhandled player state %s" % state) @misc_utils.log_exception(_moduleLogger) + def _on_user_seek(self, widget, scroll, value): + self._player.seek(value / 100.0) + + @misc_utils.log_exception(_moduleLogger) + def _on_player_update_seek(self): + self._seekbar.set_value(self._player.percent_elapsed * 100) + return True if not self._isDestroyed else False + + @misc_utils.log_exception(_moduleLogger) def _on_player_state_change(self, player, newState): - if self._presenterNavigation.is_active(): - return + if self._active and self._player.state == self._player.STATE_PLAY: + self._seekbar.show() + assert self._updateSeek is None + self._updateSeek = go_utils.Timeout(self._updateSeek, once=False) + self._updateSeek.start(seconds=30) + else: + self._seekbar.hide() + self._updateSeek.cancel() + self._updateSeek = None - self._set_context(newState) + if not self._presenterNavigation.is_active(): + self._set_context(newState) @misc_utils.log_exception(_moduleLogger) def _on_player_title_change(self, player, node): -- 1.7.9.5