+import weakref
import logging
+import util.misc as misc_utils
from util import go_utils
import backend
_moduleLogger = logging.getLogger(__name__)
-class AudioIndex(object):
+class Connection(object):
def __init__(self):
self._backend = backend.Backend()
def stop(self):
self._indexing.stop()
- def download(self, func, on_success, on_error, *args, **kwds):
+ def download(self, func, on_success, on_error, args = None, kwds = None):
+ if args is None:
+ args = ()
+ if kwds is None:
+ kwds = {}
+
self._indexing.clear_tasks()
self._indexing.add_task(
getattr(self._backend, func),
on_success,
on_error,
)
+
+
+class AudioIndex(object):
+
+ def __init__(self):
+ self._connection = Connection()
+ self._languages = None
+ self._languagesRequest = None
+ self._sources = {}
+
+ def start(self):
+ self._connection.start()
+
+ def stop(self):
+ self._connection.stop()
+
+ def get_languages(self, on_success, on_error):
+ if self._languages is None:
+ assert self._languagesRequest is None
+ self._languagesRequest = on_success, on_error
+ self._connection.download(
+ "get_languages",
+ self._on_get_languages,
+ self._on_languages_error
+ )
+ else:
+ on_success(self._languages)
+
+ def get_source(self, source, langId = None):
+ key = (source, langId)
+ if key in self._sources:
+ node = self._sources[key]
+ else:
+ if source == "radio":
+ node = RadioNode(self._connection)
+ elif source == "conferences":
+ assert langId is not None
+ node = ConferencesNode(self._connection, langId)
+ else:
+ raise NotImplementedError(source)
+ self._sources[key] = node
+
+ return node
+
+ @misc_utils.log_exception(_moduleLogger)
+ def _on_get_languages(self, languages):
+ assert self._languages is None
+ assert self._languagesRequest is not None
+ r = self._languagesRequest
+ self._languagesRequest = None
+ self._languages = languages
+ r[0](self._languages)
+
+ @misc_utils.log_exception(_moduleLogger)
+ def _on_languages_error(self, e):
+ assert self._languages is None
+ assert self._languagesRequest is not None
+ r = self._languagesRequest
+ self._languagesRequest = None
+ r[1](self._languages)
+
+
+class Node(object):
+
+ def __init__(self, connection, parent, data):
+ self._connection = connection
+ self._parent = weakref.ref(parent) if parent is not None else None
+ self._data = data
+ self._children = None
+
+ def get_children(self, on_success, on_error):
+ if self._children is None:
+ self._get_children(on_success, on_error)
+ else:
+ on_success(self._children)
+
+ def get_parent(self):
+ if self._parent is None:
+ raise RuntimeError("")
+ parent = self._parent()
+ return parent
+
+ def get_properties(self):
+ return self._data
+
+ def is_leaf(self):
+ raise NotImplementedError("")
+
+ def _get_children(self, on_success, on_error):
+ raise NotImplementedError("")
+
+
+class ParentNode(Node):
+
+ def __init__(self, connection, parent, data):
+ Node.__init__(self, connection, parent, data)
+ self._request = None
+
+ def is_leaf(self):
+ return False
+
+ def _get_children(self, on_success, on_error):
+ assert self._request is None
+ assert self._children is None
+ self._request = on_success, on_error
+
+ func, args, kwds = self._get_func()
+
+ self._connection.download(
+ func,
+ self._on_success,
+ self._on_error,
+ args,
+ kwds,
+ )
+
+ def _get_func(self):
+ raise NotImplementedError()
+
+ def _create_child(self, data):
+ raise NotImplementedError()
+
+ @misc_utils.log_exception(_moduleLogger)
+ def _on_success(self, data):
+ r = self._request
+ self._request = None
+ try:
+ self._children = [
+ self._create_child(child)
+ for child in data
+ ]
+ except Exception, e:
+ _moduleLogger.exception("Translating error")
+ self._children = None
+ r[1](e)
+ else:
+ r[0](self._children)
+
+ @misc_utils.log_exception(_moduleLogger)
+ def _on_error(self, error):
+ r = self._request
+ self._request = None
+ r[1](error)
+
+
+class LeafNode(Node):
+
+ def __init__(self, connection, parent, data):
+ Node.__init__(self, connection, parent, data)
+
+ def is_leaf(self):
+ return True
+
+ def _get_children(self, on_success, on_error):
+ raise RuntimeError("Not is a leaf")
+
+
+class RadioNode(ParentNode):
+
+ def __init__(self, connection):
+ ParentNode.__init__(self, connection, None, {})
+
+ def _get_func(self):
+ return "get_radio_channels", (), {}
+
+ def _create_child(self, data):
+ return RadioChannelNode(self._connection, self, data)
+
+
+class RadioChannelNode(LeafNode):
+
+ def __init__(self, connection, parent, data):
+ LeafNode.__init__(self, connection, parent, data)
+ self._extendedData = {}
+ self._request = None
+
+ def get_programming(self, date, on_success, on_error):
+ date = date.strftime("%Y-%m-%d")
+ try:
+ programming = self._extendedData[date]
+ except KeyError:
+ self._get_programming(date, on_success, on_error)
+ else:
+ on_success(programming)
+
+ def _get_programming(self, date, on_success, on_error):
+ assert self._request is None
+ assert date not in self._extendedData
+ self._request = on_success, on_error, date
+
+ self._connection.download(
+ "get_radio_channel_programming",
+ self._on_success,
+ self._on_error,
+ (self._data["id"], date),
+ {},
+ )
+
+ @misc_utils.log_exception(_moduleLogger)
+ def _on_success(self, data):
+ r = self._request
+ date = r[2]
+ self._request = None
+ try:
+ self._extendedData[date] = [
+ child
+ for child in data
+ ]
+ except Exception, e:
+ _moduleLogger.exception("Translating error")
+ del self._extendedData[date]
+ r[1](e)
+ else:
+ r[0](self._extendedData[date])
+
+ @misc_utils.log_exception(_moduleLogger)
+ def _on_error(self, error):
+ r = self._request
+ self._request = None
+ r[1](error)
+
+
+class ConferencesNode(ParentNode):
+
+ def __init__(self, connection, langId):
+ ParentNode.__init__(self, connection, None, {})
+ self._langId = langId
+
+ def _get_func(self):
+ return "get_conferences", (self._langId, ), {}
+
+ def _create_child(self, data):
+ return ConferenceNode(self._connection, self, data)
+
+
+class ConferenceNode(ParentNode):
+
+ def __init__(self, connection, parent, data):
+ ParentNode.__init__(self, connection, parent, data)
+
+ def _get_func(self):
+ return "get_conference_sessions", (self._data["id"], ), {}
+
+ def _create_child(self, data):
+ return SessionNode(self._connection, self, data)
+
+
+class SessionNode(ParentNode):
+
+ def __init__(self, connection, parent, data):
+ ParentNode.__init__(self, connection, parent, data)
+
+ def _get_func(self):
+ return "get_conference_talks", (self._data["id"], ), {}
+
+ def _create_child(self, data):
+ return TalkNode(self._connection, self, data)
+
+
+class TalkNode(LeafNode):
+
+ def __init__(self, connection, parent, data):
+ LeafNode.__init__(self, connection, parent, data)
),
}
- def __init__(self, player, store, index):
+ def __init__(self, player, store):
gobject.GObject.__init__(self)
self._isDestroyed = False
self._player = player
self._store = store
- self._index = index
self._clipboard = gtk.clipboard_get()
self._windowInFullscreen = False
class SourceSelector(BasicWindow):
def __init__(self, player, store, index):
+ BasicWindow.__init__(self, player, store)
self._languages = []
-
- BasicWindow.__init__(self, player, store, index)
+ self._index = index
self._loadingBanner = banners.GenericBanner()
self._radioButton = self._create_button("radio", "Radio")
- self._radioButton.connect("clicked", self._on_source_selected, RadioWindow)
+ self._radioButton.connect("clicked", self._on_source_selected, RadioWindow, "radio")
self._radioWrapper = gtk.VBox()
self._radioWrapper.pack_start(self._radioButton, False, True)
self._conferenceButton = self._create_button("conferences", "Conferences")
- self._conferenceButton.connect("clicked", self._on_source_selected, ConferencesWindow)
+ self._conferenceButton.connect("clicked", self._on_source_selected, ConferencesWindow, "conferences")
self._conferenceWrapper = gtk.VBox()
self._conferenceWrapper.pack_start(self._conferenceButton, False, True)
def _refresh(self):
self._show_loading()
- self._index.download(
- "get_languages",
- self._on_languages,
- self._on_error,
- )
+ self._index.get_languages(self._on_languages, self._on_error)
def _create_button(self, icon, message):
image = self._store.get_image_from_store(self._store.STORE_LOOKUP[icon])
@misc_utils.log_exception(_moduleLogger)
def _on_error(self, exception):
self._hide_loading()
- self._errorBanner.push_message(exception)
+ self._errorBanner.push_message(str(exception))
@misc_utils.log_exception(_moduleLogger)
- def _on_source_selected(self, widget, Source):
- sourceWindow = Source(self._player, self._store, self._index, self._languages[0]["id"])
+ def _on_source_selected(self, widget, Source, nodeName):
+ node = self._index.get_source(nodeName, self._languages[0]["id"])
+ sourceWindow = Source(self._player, self._store, node)
sourceWindow.window.set_modal(True)
sourceWindow.window.set_transient_for(self._window)
sourceWindow.window.set_default_size(*self._window.get_size())
class RadioWindow(BasicWindow):
- def __init__(self, player, store, index, languageId):
- BasicWindow.__init__(self, player, store, index)
+ def __init__(self, player, store, node):
+ BasicWindow.__init__(self, player, store)
+ self._node = node
+ self._childNode = None
self._player.connect("state-change", self._on_player_state_change)
self._player.connect("title-change", self._on_player_title_change)
def _refresh(self):
self._show_loading()
self._programmingModel.clear()
- self._index.download(
- "get_radio_channels",
+ self._node.get_children(
self._on_channels,
self._on_load_error,
)
_moduleLogger.info("Download complete but window destroyed")
return
- channels = list(channels)
+ channels = channels
if 1 < len(channels):
_moduleLogger.warning("More channels now available!")
- channel = channels[0]
- self._index.download(
- "get_radio_channel_programming",
+ self._childNode = channels[0]
+ self._childNode.get_programming(
+ self._dateShown,
self._on_channel,
self._on_load_error,
- channel["id"],
- self._dateShown,
)
@misc_utils.log_exception(_moduleLogger)
@misc_utils.log_exception(_moduleLogger)
def _on_load_error(self, exception):
self._hide_loading()
- self._errorBanner.push_message(exception)
+ self._errorBanner.push_message(str(exception))
@misc_utils.log_exception(_moduleLogger)
def _on_row_changed(self, selection):
class ListWindow(BasicWindow):
- def __init__(self, player, store, index):
- BasicWindow.__init__(self, player, store, index)
+ def __init__(self, player, store, node):
+ BasicWindow.__init__(self, player, store)
+ self._node = node
self._loadingBanner = banners.GenericBanner()
class ConferencesWindow(ListWindow):
- def __init__(self, player, store, index, languageId):
- self._languageId = languageId
-
- ListWindow.__init__(self, player, store, index)
+ def __init__(self, player, store, node):
+ ListWindow.__init__(self, player, store, node)
self._window.set_title("Conferences")
@classmethod
def _get_columns(cls):
- yield gobject.TYPE_STRING, None
+ yield gobject.TYPE_PYOBJECT, None
textrenderer = gtk.CellRendererText()
column = gtk.TreeViewColumn("Date")
def _refresh(self):
ListWindow._refresh(self)
- self._index.download(
- "get_conferences",
+ self._node.get_children(
self._on_conferences,
self._on_error,
- self._languageId,
)
@misc_utils.log_exception(_moduleLogger)
return
self._hide_loading()
- for program in programs:
- row = program["id"], program["title"], program["full_title"]
+ for programNode in programs:
+ program = programNode.get_properties()
+ row = programNode, program["title"], program["full_title"]
self._model.append(row)
path = (self._get_current_row(), )
@misc_utils.log_exception(_moduleLogger)
def _on_error(self, exception):
self._hide_loading()
- self._errorBanner.push_message(exception)
+ self._errorBanner.push_message(str(exception))
@misc_utils.log_exception(_moduleLogger)
def _on_row_activated(self, view, path, column):
itr = self._model.get_iter(path)
- conferenceId = self._model.get_value(itr, 0)
+ node = self._model.get_value(itr, 0)
- sessionsWindow = ConferenceSessionsWindow(self._player, self._store, self._index, conferenceId)
+ sessionsWindow = ConferenceSessionsWindow(self._player, self._store, node)
sessionsWindow.window.set_modal(True)
sessionsWindow.window.set_transient_for(self._window)
sessionsWindow.window.set_default_size(*self._window.get_size())
class ConferenceSessionsWindow(ListWindow):
- def __init__(self, player, store, index, conferenceId):
- self._conferenceId = conferenceId
-
- ListWindow.__init__(self, player, store, index)
+ def __init__(self, player, store, node):
+ ListWindow.__init__(self, player, store, node)
self._window.set_title("Sessions")
@classmethod
def _get_columns(cls):
- yield gobject.TYPE_STRING, None
+ yield gobject.TYPE_PYOBJECT, None
textrenderer = gtk.CellRendererText()
column = gtk.TreeViewColumn("Session")
def _refresh(self):
ListWindow._refresh(self)
- self._index.download(
- "get_conference_sessions",
+ self._node.get_children(
self._on_conference_sessions,
self._on_error,
- self._conferenceId,
)
@misc_utils.log_exception(_moduleLogger)
return
self._hide_loading()
- for program in programs:
- row = program["id"], program["title"]
+ for programNode in programs:
+ program = programNode.get_properties()
+ row = programNode, program["title"]
self._model.append(row)
path = (self._get_current_row(), )
@misc_utils.log_exception(_moduleLogger)
def _on_error(self, exception):
self._hide_loading()
- self._errorBanner.push_message(exception)
+ self._errorBanner.push_message(str(exception))
@misc_utils.log_exception(_moduleLogger)
def _on_row_activated(self, view, path, column):
itr = self._model.get_iter(path)
- sessionId = self._model.get_value(itr, 0)
+ node = self._model.get_value(itr, 0)
- sessionsWindow = ConferenceTalksWindow(self._player, self._store, self._index, sessionId)
+ sessionsWindow = ConferenceTalksWindow(self._player, self._store, node)
sessionsWindow.window.set_modal(True)
sessionsWindow.window.set_transient_for(self._window)
sessionsWindow.window.set_default_size(*self._window.get_size())
class ConferenceTalksWindow(ListWindow):
- def __init__(self, player, store, index, sessionId):
- self._sessionId = sessionId
-
- ListWindow.__init__(self, player, store, index)
+ def __init__(self, player, store, node):
+ ListWindow.__init__(self, player, store, node)
self._window.set_title("Talks")
@classmethod
def _get_columns(cls):
- yield gobject.TYPE_STRING, None
+ yield gobject.TYPE_PYOBJECT, None
textrenderer = gtk.CellRendererText()
column = gtk.TreeViewColumn("Talk")
def _refresh(self):
ListWindow._refresh(self)
- self._index.download(
- "get_conference_talks",
+ self._node.get_children(
self._on_conference_talks,
self._on_error,
- self._sessionId,
)
@misc_utils.log_exception(_moduleLogger)
return
self._hide_loading()
- for program in programs:
- row = program, "%s\n%s" % (program["title"], program["speaker"])
+ for programNode in programs:
+ program = programNode.get_properties()
+ row = programNode, "%s\n%s" % (program["title"], program["speaker"])
self._model.append(row)
path = (self._get_current_row(), )
@misc_utils.log_exception(_moduleLogger)
def _on_error(self, exception):
self._hide_loading()
- self._errorBanner.push_message(exception)
+ self._errorBanner.push_message(str(exception))
@misc_utils.log_exception(_moduleLogger)
def _on_row_activated(self, view, path, column):
itr = self._model.get_iter(path)
- program = self._model.get_value(itr, 0)
+ node = self._model.get_value(itr, 0)
- sessionsWindow = ConferenceTalkWindow(self._player, self._store, self._index, program)
+ sessionsWindow = ConferenceTalkWindow(self._player, self._store, node)
sessionsWindow.window.set_modal(True)
sessionsWindow.window.set_transient_for(self._window)
sessionsWindow.window.set_default_size(*self._window.get_size())
class ConferenceTalkWindow(BasicWindow):
- def __init__(self, player, store, index, talkData):
- BasicWindow.__init__(self, player, store, index)
+ def __init__(self, player, store, node):
+ BasicWindow.__init__(self, player, store)
self._player.connect("state-change", self._on_player_state_change)
self._player.connect("title-change", self._on_player_title_change)