From: Ed Page Date: Sat, 15 May 2010 17:52:19 +0000 (-0500) Subject: Adding additional sources X-Git-Url: http://git.maemo.org/git/?p=watersofshiloah;a=commitdiff_plain;h=f77cda973fab5dd7c1d4f6621650c6bce1725aa7;hp=74db32a3e870c99c6ea27107faff77146d871044;ds=sidebyside Adding additional sources --- diff --git a/data/scripture_bg.png b/data/scripture_bg.png new file mode 100644 index 0000000..9c103ab Binary files /dev/null and b/data/scripture_bg.png differ diff --git a/data/scriptures_bg.png b/data/scriptures_bg.png deleted file mode 100644 index 9c103ab..0000000 Binary files a/data/scriptures_bg.png and /dev/null differ diff --git a/src/imagestore.py b/src/imagestore.py index bf228f5..c62cb63 100644 --- a/src/imagestore.py +++ b/src/imagestore.py @@ -32,7 +32,7 @@ class ImageStore(object): "radio_header": "radio_header.png", "conference_background": "conference_bg.png", "magazine_background": "magazine_bg.png", - "scriptures_background": "scripture_bg.png", + "scripture_background": "scripture_bg.png", "conferences": "conference.png", "magazines": "magazines.png", diff --git a/src/mormonchannel_gtk.py b/src/mormonchannel_gtk.py index 967db54..fb2132d 100755 --- a/src/mormonchannel_gtk.py +++ b/src/mormonchannel_gtk.py @@ -2,7 +2,6 @@ # -*- coding: utf-8 -*- """ -@todo Add additional sources @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/stream_index.py b/src/stream_index.py index 492de77..d638e71 100644 --- a/src/stream_index.py +++ b/src/stream_index.py @@ -79,6 +79,12 @@ class AudioIndex(object): elif source == SOURCE_CONFERENCES: assert langId is not None node = ConferencesNode(self._connection, langId) + elif source == SOURCE_MAGAZINES: + assert langId is not None + node = MagazinesNode(self._connection, langId) + elif source == SOURCE_SCRIPTURES: + assert langId is not None + node = ScripturesNode(self._connection, langId) else: raise NotImplementedError(source) self._sources[key] = node @@ -379,6 +385,152 @@ class TalkNode(LeafNode): return self._data["url"] +class MagazinesNode(ParentNode): + + def __init__(self, connection, langId): + ParentNode.__init__(self, connection, None, {}, SOURCE_MAGAZINES) + self._langId = langId + + @property + def title(self): + return "Magazines" + + def _get_func(self): + return "get_magazines", (self._langId, ), {} + + def _create_child(self, data, id): + return MagazineNode(self._connection, self, data, id) + + +class MagazineNode(ParentNode): + + def __init__(self, connection, parent, data, id): + ParentNode.__init__(self, connection, parent, data, id) + + @property + def title(self): + return self._data["title"] + + def _get_func(self): + return "get_magazine_issues", (self._data["id"], ), {} + + def _create_child(self, data, id): + return IssueNode(self._connection, self, data, id) + + +class IssueNode(ParentNode): + + def __init__(self, connection, parent, data, id): + ParentNode.__init__(self, connection, parent, data, id) + + @property + def title(self): + return self._data["title"] + + def _get_func(self): + return "get_magazine_articles", (self._data["id"], ), {} + + def _create_child(self, data, id): + return ArticleNode(self._connection, self, data, id) + + +class ArticleNode(LeafNode): + + def __init__(self, connection, parent, data, id): + LeafNode.__init__(self, connection, parent, data, id) + + @property + def can_navigate(self): + return True + + @property + def title(self): + return self._data["title"] + + @property + def subtitle(self): + speaker = self._data["author"] + if speaker is not None: + return speaker + else: + return "" + + @property + def uri(self): + return self._data["url"] + + +class ScripturesNode(ParentNode): + + def __init__(self, connection, langId): + ParentNode.__init__(self, connection, None, {}, SOURCE_SCRIPTURES) + self._langId = langId + + @property + def title(self): + return "Scriptures" + + def _get_func(self): + return "get_scriptures", (self._langId, ), {} + + def _create_child(self, data, id): + return ScriptureNode(self._connection, self, data, id) + + +class ScriptureNode(ParentNode): + + def __init__(self, connection, parent, data, id): + ParentNode.__init__(self, connection, parent, data, id) + + @property + def title(self): + return self._data["title"] + + def _get_func(self): + return "get_scripture_books", (self._data["id"], ), {} + + def _create_child(self, data, id): + return BookNode(self._connection, self, data, id) + + +class BookNode(ParentNode): + + def __init__(self, connection, parent, data, id): + ParentNode.__init__(self, connection, parent, data, id) + + @property + def title(self): + return self._data["title"] + + def _get_func(self): + return "get_scripture_chapters", (self._data["id"], ), {} + + def _create_child(self, data, id): + return ChapterNode(self._connection, self, data, id) + + +class ChapterNode(LeafNode): + + def __init__(self, connection, parent, data, id): + LeafNode.__init__(self, connection, parent, data, id) + + @property + def can_navigate(self): + return True + + @property + def title(self): + return self._data["title"] + + @property + def subtitle(self): + return "" + + @property + def uri(self): + return self._data["url"] + + def walk_ancestors(node): while True: yield node diff --git a/src/windows/__init__.py b/src/windows/__init__.py index 7010b93..4acbe57 100644 --- a/src/windows/__init__.py +++ b/src/windows/__init__.py @@ -2,3 +2,5 @@ import _base import source import radio import conferences +import magazines +import scriptures diff --git a/src/windows/magazines.py b/src/windows/magazines.py new file mode 100644 index 0000000..aa6bead --- /dev/null +++ b/src/windows/magazines.py @@ -0,0 +1,401 @@ +import logging + +import gobject +import gtk + +import hildonize +import util.go_utils as go_utils +import util.misc as misc_utils +import banners +import presenter +import stream_index + +import windows + + +_moduleLogger = logging.getLogger(__name__) + + +class MagazinesWindow(windows._base.ListWindow): + + def __init__(self, player, store, node): + windows._base.ListWindow.__init__(self, player, store, node) + self._window.set_title(self._node.title) + + @classmethod + def _get_columns(cls): + yield gobject.TYPE_PYOBJECT, None + + textrenderer = gtk.CellRendererText() + column = gtk.TreeViewColumn("Magazine") + column.pack_start(textrenderer, expand=True) + column.add_attribute(textrenderer, "text", 1) + yield gobject.TYPE_STRING, column + + def _refresh(self): + windows._base.ListWindow._refresh(self) + self._node.get_children( + self._on_magazines, + self._on_error, + ) + + @misc_utils.log_exception(_moduleLogger) + def _on_magazines(self, programs): + if self._isDestroyed: + _moduleLogger.info("Download complete but window destroyed") + return + + self._hide_loading() + for programNode in programs: + program = programNode.get_properties() + row = programNode, program["title"] + self._model.append(row) + + self._select_row() + + @misc_utils.log_exception(_moduleLogger) + def _on_error(self, exception): + self._hide_loading() + self._errorBanner.push_message(str(exception)) + + def _window_from_node(self, node): + issuesWindow = MagazineIssuesWindow(self._player, self._store, node) + issuesWindow.window.set_modal(True) + issuesWindow.window.set_transient_for(self._window) + issuesWindow.window.set_default_size(*self._window.get_size()) + issuesWindow.connect("quit", self._on_quit) + issuesWindow.connect("home", self._on_home) + issuesWindow.connect("jump-to", self._on_jump) + issuesWindow.show() + return issuesWindow + + @misc_utils.log_exception(_moduleLogger) + def _on_row_activated(self, view, path, column): + itr = self._model.get_iter(path) + node = self._model.get_value(itr, 0) + self._window_from_node(node) + + +gobject.type_register(MagazinesWindow) + + +class MagazineIssuesWindow(windows._base.ListWindow): + + def __init__(self, player, store, node): + windows._base.ListWindow.__init__(self, player, store, node) + self._window.set_title(self._node.title) + + @classmethod + def _get_columns(cls): + yield gobject.TYPE_PYOBJECT, None + + textrenderer = gtk.CellRendererText() + column = gtk.TreeViewColumn("Issue") + column.pack_start(textrenderer, expand=True) + column.add_attribute(textrenderer, "text", 1) + yield gobject.TYPE_STRING, column + + def _refresh(self): + windows._base.ListWindow._refresh(self) + self._node.get_children( + self._on_magazine_issues, + self._on_error, + ) + + @misc_utils.log_exception(_moduleLogger) + def _on_magazine_issues(self, programs): + if self._isDestroyed: + _moduleLogger.info("Download complete but window destroyed") + return + + self._hide_loading() + for programNode in programs: + program = programNode.get_properties() + row = programNode, program["title"] + self._model.append(row) + + self._select_row() + + @misc_utils.log_exception(_moduleLogger) + def _on_error(self, exception): + self._hide_loading() + self._errorBanner.push_message(str(exception)) + + def _window_from_node(self, node): + issuesWindow = MagazineArticlesWindow(self._player, self._store, node) + issuesWindow.window.set_modal(True) + issuesWindow.window.set_transient_for(self._window) + issuesWindow.window.set_default_size(*self._window.get_size()) + issuesWindow.connect("quit", self._on_quit) + issuesWindow.connect("home", self._on_home) + issuesWindow.connect("jump-to", self._on_jump) + issuesWindow.show() + return issuesWindow + + @misc_utils.log_exception(_moduleLogger) + def _on_row_activated(self, view, path, column): + itr = self._model.get_iter(path) + node = self._model.get_value(itr, 0) + self._window_from_node(node) + + +gobject.type_register(MagazineIssuesWindow) + + +class MagazineArticlesWindow(windows._base.ListWindow): + + def __init__(self, player, store, node): + windows._base.ListWindow.__init__(self, player, store, node) + self._window.set_title(self._node.title) + + @classmethod + def _get_columns(cls): + yield gobject.TYPE_PYOBJECT, None + + textrenderer = gtk.CellRendererText() + column = gtk.TreeViewColumn("Article") + column.pack_start(textrenderer, expand=True) + column.add_attribute(textrenderer, "text", 1) + yield gobject.TYPE_STRING, column + + def _refresh(self): + windows._base.ListWindow._refresh(self) + self._node.get_children( + self._on_magazine_articles, + self._on_error, + ) + + @misc_utils.log_exception(_moduleLogger) + def _on_magazine_articles(self, programs): + if self._isDestroyed: + _moduleLogger.info("Download complete but window destroyed") + return + + self._hide_loading() + for programNode in programs: + program = programNode.get_properties() + row = programNode, "%s\n%s" % (program["title"], program["author"]) + self._model.append(row) + + self._select_row() + + @misc_utils.log_exception(_moduleLogger) + def _on_error(self, exception): + self._hide_loading() + self._errorBanner.push_message(str(exception)) + + def _window_from_node(self, node): + issuesWindow = MagazineArticleWindow(self._player, self._store, node) + issuesWindow.window.set_modal(True) + issuesWindow.window.set_transient_for(self._window) + issuesWindow.window.set_default_size(*self._window.get_size()) + issuesWindow.connect("quit", self._on_quit) + issuesWindow.connect("home", self._on_home) + issuesWindow.connect("jump-to", self._on_jump) + issuesWindow.show() + return issuesWindow + + @misc_utils.log_exception(_moduleLogger) + def _on_row_activated(self, view, path, column): + itr = self._model.get_iter(path) + node = self._model.get_value(itr, 0) + self._window_from_node(node) + + +gobject.type_register(MagazineArticlesWindow) + + +class MagazineArticleWindow(windows._base.BasicWindow): + + def __init__(self, player, store, node): + windows._base.BasicWindow.__init__(self, player, store) + 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) + self.connect_auto(self._player, "error", self._on_player_error) + + self._loadingBanner = banners.GenericBanner() + + self._presenter = presenter.StreamPresenter(self._store) + self._presenter.set_context( + self._store.STORE_LOOKUP["magazine_background"], + self._node.title, + self._node.subtitle, + ) + self._presenterNavigation = presenter.NavigationBox() + self._presenterNavigation.toplevel.add(self._presenter.toplevel) + 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) + + def show(self): + windows._base.BasicWindow.show(self) + self._window.show_all() + 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 + + @property + def _active(self): + return self._playerNode is self._node + + def _show_loading(self): + animationPath = self._store.STORE_LOOKUP["loading"] + animation = self._store.get_pixbuf_animation_from_store(animationPath) + self._loadingBanner.show(animation, "Loading...") + + def _hide_loading(self): + self._loadingBanner.hide() + + def _set_context(self, state): + if state == self._player.STATE_PLAY: + if self._active: + self._presenter.set_state(self._store.STORE_LOOKUP["pause"]) + else: + self._presenter.set_state(self._store.STORE_LOOKUP["play"]) + elif state == self._player.STATE_PAUSE: + self._presenter.set_state(self._store.STORE_LOOKUP["play"]) + elif state == self._player.STATE_STOP: + self._presenter.set_state(self._store.STORE_LOOKUP["play"]) + else: + _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._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 + + if not self._presenterNavigation.is_active(): + self._set_context(newState) + + @misc_utils.log_exception(_moduleLogger) + def _on_player_title_change(self, player, node): + if not self._active or node in [None, self._node]: + self._playerNode = player.node + return + self._playerNode = player.node + self.emit("jump-to", node) + self._window.destroy() + + @misc_utils.log_exception(_moduleLogger) + def _on_player_error(self, player, err, debug): + _moduleLogger.error("%r - %r" % (err, debug)) + + @misc_utils.log_exception(_moduleLogger) + def _on_navigating(self, widget, navState): + if navState == "clicking": + if self._player.state == self._player.STATE_PLAY: + if self._active: + imageName = "pause_pressed" + else: + imageName = "play_pressed" + elif self._player.state == self._player.STATE_PAUSE: + imageName = "play_pressed" + elif self._player.state == self._player.STATE_STOP: + imageName = "play_pressed" + else: + _moduleLogger.info("Unhandled player state %s" % self._player.state) + elif navState == "down": + imageName = "home" + elif navState == "up": + if self._player.state == self._player.STATE_PLAY: + if self._active: + imageName = "pause" + else: + imageName = "play" + elif self._player.state == self._player.STATE_PAUSE: + imageName = "play" + elif self._player.state == self._player.STATE_STOP: + imageName = "play" + else: + _moduleLogger.info("Unhandled player state %s" % self._player.state) + elif navState == "left": + imageName = "next" + elif navState == "right": + imageName = "prev" + + self._presenter.set_state(self._store.STORE_LOOKUP[imageName]) + + @misc_utils.log_exception(_moduleLogger) + def _on_nav_action(self, widget, navState): + self._set_context(self._player.state) + + if navState == "clicking": + if self._player.state == self._player.STATE_PLAY: + if self._active: + self._player.pause() + else: + self._player.set_piece_by_node(self._node) + self._player.play() + elif self._player.state == self._player.STATE_PAUSE: + self._player.play() + elif self._player.state == self._player.STATE_STOP: + self._player.set_piece_by_node(self._node) + self._player.play() + else: + _moduleLogger.info("Unhandled player state %s" % self._player.state) + elif navState == "down": + self.emit("home") + self._window.destroy() + elif navState == "up": + pass + elif navState == "left": + if self._active: + self._player.next() + else: + assert self._nextSearch is None + self._nextSearch = stream_index.AsyncWalker(stream_index.get_next) + self._nextSearch.start(self._node, self._on_next_node, self._on_node_search_error) + elif navState == "right": + if self._active: + self._player.back() + else: + assert self._nextSearch is None + self._nextSearch = stream_index.AsyncWalker(stream_index.get_previous) + self._nextSearch.start(self._node, self._on_next_node, self._on_node_search_error) + + @misc_utils.log_exception(_moduleLogger) + def _on_next_node(self, node): + self._nextSearch = None + self.emit("jump-to", node) + self._window.destroy() + + @misc_utils.log_exception(_moduleLogger) + def _on_node_search_error(self, e): + self._nextSearch = None + self._errorBanner.push_message(str(e)) + + +gobject.type_register(MagazineArticleWindow) diff --git a/src/windows/scriptures.py b/src/windows/scriptures.py new file mode 100644 index 0000000..57b509c --- /dev/null +++ b/src/windows/scriptures.py @@ -0,0 +1,401 @@ +import logging + +import gobject +import gtk + +import hildonize +import util.go_utils as go_utils +import util.misc as misc_utils +import banners +import presenter +import stream_index + +import windows + + +_moduleLogger = logging.getLogger(__name__) + + +class ScripturesWindow(windows._base.ListWindow): + + def __init__(self, player, store, node): + windows._base.ListWindow.__init__(self, player, store, node) + self._window.set_title(self._node.title) + + @classmethod + def _get_columns(cls): + yield gobject.TYPE_PYOBJECT, None + + textrenderer = gtk.CellRendererText() + column = gtk.TreeViewColumn("Scripture") + column.pack_start(textrenderer, expand=True) + column.add_attribute(textrenderer, "text", 1) + yield gobject.TYPE_STRING, column + + def _refresh(self): + windows._base.ListWindow._refresh(self) + self._node.get_children( + self._on_scriptures, + self._on_error, + ) + + @misc_utils.log_exception(_moduleLogger) + def _on_scriptures(self, programs): + if self._isDestroyed: + _moduleLogger.info("Download complete but window destroyed") + return + + self._hide_loading() + for programNode in programs: + program = programNode.get_properties() + row = programNode, program["title"] + self._model.append(row) + + self._select_row() + + @misc_utils.log_exception(_moduleLogger) + def _on_error(self, exception): + self._hide_loading() + self._errorBanner.push_message(str(exception)) + + def _window_from_node(self, node): + booksWindow = ScriptureBooksWindow(self._player, self._store, node) + booksWindow.window.set_modal(True) + booksWindow.window.set_transient_for(self._window) + booksWindow.window.set_default_size(*self._window.get_size()) + booksWindow.connect("quit", self._on_quit) + booksWindow.connect("home", self._on_home) + booksWindow.connect("jump-to", self._on_jump) + booksWindow.show() + return booksWindow + + @misc_utils.log_exception(_moduleLogger) + def _on_row_activated(self, view, path, column): + itr = self._model.get_iter(path) + node = self._model.get_value(itr, 0) + self._window_from_node(node) + + +gobject.type_register(ScripturesWindow) + + +class ScriptureBooksWindow(windows._base.ListWindow): + + def __init__(self, player, store, node): + windows._base.ListWindow.__init__(self, player, store, node) + self._window.set_title(self._node.title) + + @classmethod + def _get_columns(cls): + yield gobject.TYPE_PYOBJECT, None + + textrenderer = gtk.CellRendererText() + column = gtk.TreeViewColumn("Book") + column.pack_start(textrenderer, expand=True) + column.add_attribute(textrenderer, "text", 1) + yield gobject.TYPE_STRING, column + + def _refresh(self): + windows._base.ListWindow._refresh(self) + self._node.get_children( + self._on_scripture_books, + self._on_error, + ) + + @misc_utils.log_exception(_moduleLogger) + def _on_scripture_books(self, programs): + if self._isDestroyed: + _moduleLogger.info("Download complete but window destroyed") + return + + self._hide_loading() + for programNode in programs: + program = programNode.get_properties() + row = programNode, program["title"] + self._model.append(row) + + self._select_row() + + @misc_utils.log_exception(_moduleLogger) + def _on_error(self, exception): + self._hide_loading() + self._errorBanner.push_message(str(exception)) + + def _window_from_node(self, node): + booksWindow = ScriptureChaptersWindow(self._player, self._store, node) + booksWindow.window.set_modal(True) + booksWindow.window.set_transient_for(self._window) + booksWindow.window.set_default_size(*self._window.get_size()) + booksWindow.connect("quit", self._on_quit) + booksWindow.connect("home", self._on_home) + booksWindow.connect("jump-to", self._on_jump) + booksWindow.show() + return booksWindow + + @misc_utils.log_exception(_moduleLogger) + def _on_row_activated(self, view, path, column): + itr = self._model.get_iter(path) + node = self._model.get_value(itr, 0) + self._window_from_node(node) + + +gobject.type_register(ScriptureBooksWindow) + + +class ScriptureChaptersWindow(windows._base.ListWindow): + + def __init__(self, player, store, node): + windows._base.ListWindow.__init__(self, player, store, node) + self._window.set_title(self._node.title) + + @classmethod + def _get_columns(cls): + yield gobject.TYPE_PYOBJECT, None + + textrenderer = gtk.CellRendererText() + column = gtk.TreeViewColumn("Chapter") + column.pack_start(textrenderer, expand=True) + column.add_attribute(textrenderer, "text", 1) + yield gobject.TYPE_STRING, column + + def _refresh(self): + windows._base.ListWindow._refresh(self) + self._node.get_children( + self._on_scripture_chapters, + self._on_error, + ) + + @misc_utils.log_exception(_moduleLogger) + def _on_scripture_chapters(self, programs): + if self._isDestroyed: + _moduleLogger.info("Download complete but window destroyed") + return + + self._hide_loading() + for programNode in programs: + program = programNode.get_properties() + row = programNode, program["title"] + self._model.append(row) + + self._select_row() + + @misc_utils.log_exception(_moduleLogger) + def _on_error(self, exception): + self._hide_loading() + self._errorBanner.push_message(str(exception)) + + def _window_from_node(self, node): + booksWindow = ScriptureChapterWindow(self._player, self._store, node) + booksWindow.window.set_modal(True) + booksWindow.window.set_transient_for(self._window) + booksWindow.window.set_default_size(*self._window.get_size()) + booksWindow.connect("quit", self._on_quit) + booksWindow.connect("home", self._on_home) + booksWindow.connect("jump-to", self._on_jump) + booksWindow.show() + return booksWindow + + @misc_utils.log_exception(_moduleLogger) + def _on_row_activated(self, view, path, column): + itr = self._model.get_iter(path) + node = self._model.get_value(itr, 0) + self._window_from_node(node) + + +gobject.type_register(ScriptureChaptersWindow) + + +class ScriptureChapterWindow(windows._base.BasicWindow): + + def __init__(self, player, store, node): + windows._base.BasicWindow.__init__(self, player, store) + 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) + self.connect_auto(self._player, "error", self._on_player_error) + + self._loadingBanner = banners.GenericBanner() + + self._presenter = presenter.StreamPresenter(self._store) + self._presenter.set_context( + self._store.STORE_LOOKUP["scripture_background"], + self._node.title, + self._node.subtitle, + ) + self._presenterNavigation = presenter.NavigationBox() + self._presenterNavigation.toplevel.add(self._presenter.toplevel) + 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) + + def show(self): + windows._base.BasicWindow.show(self) + self._window.show_all() + 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 + + @property + def _active(self): + return self._playerNode is self._node + + def _show_loading(self): + animationPath = self._store.STORE_LOOKUP["loading"] + animation = self._store.get_pixbuf_animation_from_store(animationPath) + self._loadingBanner.show(animation, "Loading...") + + def _hide_loading(self): + self._loadingBanner.hide() + + def _set_context(self, state): + if state == self._player.STATE_PLAY: + if self._active: + self._presenter.set_state(self._store.STORE_LOOKUP["pause"]) + else: + self._presenter.set_state(self._store.STORE_LOOKUP["play"]) + elif state == self._player.STATE_PAUSE: + self._presenter.set_state(self._store.STORE_LOOKUP["play"]) + elif state == self._player.STATE_STOP: + self._presenter.set_state(self._store.STORE_LOOKUP["play"]) + else: + _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._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 + + if not self._presenterNavigation.is_active(): + self._set_context(newState) + + @misc_utils.log_exception(_moduleLogger) + def _on_player_title_change(self, player, node): + if not self._active or node in [None, self._node]: + self._playerNode = player.node + return + self._playerNode = player.node + self.emit("jump-to", node) + self._window.destroy() + + @misc_utils.log_exception(_moduleLogger) + def _on_player_error(self, player, err, debug): + _moduleLogger.error("%r - %r" % (err, debug)) + + @misc_utils.log_exception(_moduleLogger) + def _on_navigating(self, widget, navState): + if navState == "clicking": + if self._player.state == self._player.STATE_PLAY: + if self._active: + imageName = "pause_pressed" + else: + imageName = "play_pressed" + elif self._player.state == self._player.STATE_PAUSE: + imageName = "play_pressed" + elif self._player.state == self._player.STATE_STOP: + imageName = "play_pressed" + else: + _moduleLogger.info("Unhandled player state %s" % self._player.state) + elif navState == "down": + imageName = "home" + elif navState == "up": + if self._player.state == self._player.STATE_PLAY: + if self._active: + imageName = "pause" + else: + imageName = "play" + elif self._player.state == self._player.STATE_PAUSE: + imageName = "play" + elif self._player.state == self._player.STATE_STOP: + imageName = "play" + else: + _moduleLogger.info("Unhandled player state %s" % self._player.state) + elif navState == "left": + imageName = "next" + elif navState == "right": + imageName = "prev" + + self._presenter.set_state(self._store.STORE_LOOKUP[imageName]) + + @misc_utils.log_exception(_moduleLogger) + def _on_nav_action(self, widget, navState): + self._set_context(self._player.state) + + if navState == "clicking": + if self._player.state == self._player.STATE_PLAY: + if self._active: + self._player.pause() + else: + self._player.set_piece_by_node(self._node) + self._player.play() + elif self._player.state == self._player.STATE_PAUSE: + self._player.play() + elif self._player.state == self._player.STATE_STOP: + self._player.set_piece_by_node(self._node) + self._player.play() + else: + _moduleLogger.info("Unhandled player state %s" % self._player.state) + elif navState == "down": + self.emit("home") + self._window.destroy() + elif navState == "up": + pass + elif navState == "left": + if self._active: + self._player.next() + else: + assert self._nextSearch is None + self._nextSearch = stream_index.AsyncWalker(stream_index.get_next) + self._nextSearch.start(self._node, self._on_next_node, self._on_node_search_error) + elif navState == "right": + if self._active: + self._player.back() + else: + assert self._nextSearch is None + self._nextSearch = stream_index.AsyncWalker(stream_index.get_previous) + self._nextSearch.start(self._node, self._on_next_node, self._on_node_search_error) + + @misc_utils.log_exception(_moduleLogger) + def _on_next_node(self, node): + self._nextSearch = None + self.emit("jump-to", node) + self._window.destroy() + + @misc_utils.log_exception(_moduleLogger) + def _on_node_search_error(self, e): + self._nextSearch = None + self._errorBanner.push_message(str(e)) + + +gobject.type_register(ScriptureChapterWindow) diff --git a/src/windows/source.py b/src/windows/source.py index 39ffb44..acf964e 100644 --- a/src/windows/source.py +++ b/src/windows/source.py @@ -114,9 +114,9 @@ class SourceSelector(windows._base.BasicWindow): elif node.id == stream_index.SOURCE_CONFERENCES: Source = windows.conferences.ConferencesWindow elif node.id == stream_index.SOURCE_MAGAZINES: - pass + Source = windows.magazines.MagazinesWindow elif node.id == stream_index.SOURCE_SCRIPTURES: - pass + Source = windows.scriptures.ScripturesWindow sourceWindow = Source(self._player, self._store, node) sourceWindow.window.set_modal(True) sourceWindow.window.set_transient_for(self._window)