Commit 5b3ae2f6 authored by Philip Withnall's avatar Philip Withnall

Added (disabled) support for radio channels

Full radio support is there, but commented out for two reasons:
* Does radio really belong in Totem, a movie player?
* With radio and TV channels being queried concurrently at startup, bad
things happen, which will require extra code to fix.
parent 941a9a2f
...@@ -52,6 +52,7 @@ src/plugins/gromit/totem-gromit.c ...@@ -52,6 +52,7 @@ src/plugins/gromit/totem-gromit.c
[type: gettext/ini]src/plugins/iplayer/iplayer.totem-plugin.in [type: gettext/ini]src/plugins/iplayer/iplayer.totem-plugin.in
src/plugins/iplayer/iplayer.py src/plugins/iplayer/iplayer.py
src/plugins/iplayer/iplayer2.py src/plugins/iplayer/iplayer2.py
[type: gettext/glade]src/plugins/iplayer/iplayer.ui
[type: gettext/glade]src/plugins/jamendo/jamendo.ui [type: gettext/glade]src/plugins/jamendo/jamendo.ui
[type: gettext/ini]src/plugins/jamendo/jamendo.totem-plugin.in [type: gettext/ini]src/plugins/jamendo/jamendo.totem-plugin.in
src/plugins/jamendo/jamendo.py src/plugins/jamendo/jamendo.py
......
...@@ -17,8 +17,12 @@ class IplayerPlugin (totem.Plugin): ...@@ -17,8 +17,12 @@ class IplayerPlugin (totem.Plugin):
# Build the interface # Build the interface
builder = self.load_interface ("iplayer.ui", True, totem_object.get_main_window (), self) builder = self.load_interface ("iplayer.ui", True, totem_object.get_main_window (), self)
container = builder.get_object ('iplayer_vbox') container = builder.get_object ('iplayer_vbox')
self.tree_store = builder.get_object ('iplayer_programme_store')
self.tv_tree_store = builder.get_object ('iplayer_programme_store')
#self.radio_tree_store = builder.get_object ('iplayer_radio_programme_store')
#for object_id in ('iplayer_programme_list', 'iplayer_radio_programme_list')
programme_list = builder.get_object ('iplayer_programme_list') programme_list = builder.get_object ('iplayer_programme_list')
# programme_list.set_data ('id', object_id)
programme_list.connect ('row-expanded', self._row_expanded_cb) programme_list.connect ('row-expanded', self._row_expanded_cb)
programme_list.connect ('row-activated', self._row_activated_cb) programme_list.connect ('row-activated', self._row_activated_cb)
...@@ -26,38 +30,44 @@ class IplayerPlugin (totem.Plugin): ...@@ -26,38 +30,44 @@ class IplayerPlugin (totem.Plugin):
container.show_all () container.show_all ()
self.tv = iplayer2.feed ('tv') self.tv = iplayer2.feed ('tv')
# TODO: Radio support #self.radio = iplayer2.feed ('radio')
#self.radio = feed ('radio')
# Add the interface to Totem's sidebar
self.totem.add_sidebar_page ("iplayer", _("BBC iPlayer"), container) self.totem.add_sidebar_page ("iplayer", _("BBC iPlayer"), container)
self.populate_channel_list () # Get the channel category listings
self.populate_channel_list (self.tv, self.tv_tree_store)
#self.populate_channel_list (self.radio, self.radio_tree_store)
def deactivate (self, totem_object): def deactivate (self, totem_object):
totem_object.remove_sidebar_page ("iplayer") totem_object.remove_sidebar_page ("iplayer")
def populate_channel_list (self): def populate_channel_list (self, feed, tree_store):
if self.debug: if self.debug:
print "Populating channel list…" print "Populating channel list…"
# Add all the channels as top-level rows in the tree store # Add all the channels as top-level rows in the tree store
channels = self.tv.channels () channels = feed.channels ()
for channel_id, title in channels.items (): for channel_id, title in channels.items ():
parent_iter = self.tree_store.append (None, [title, channel_id, None]) parent_iter = tree_store.append (None, [title, channel_id, None])
# Add the channels' categories in a thread, since they each require a network request # Add the channels' categories in a thread, since they each require a network request
parent_path = self.tree_store.get_path (parent_iter) parent_path = tree_store.get_path (parent_iter)
thread = PopulateChannelsThread (self, parent_path, self.tv, self.tree_store) thread = PopulateChannelsThread (self, parent_path, feed, tree_store)
thread.start () thread.start ()
def _populate_channel_list_cb (self, parent_path, values): def _populate_channel_list_cb (self, tree_store, parent_path, values):
# Callback from PopulateChannelsThread to add stuff to the tree store # Callback from PopulateChannelsThread to add stuff to the tree store
parent_iter = self.tree_store.get_iter (parent_path) if values == None:
category_iter = self.tree_store.append (parent_iter, values) self.totem.action_error (_('Error Listing Channel Categories'), 'TODO')
return False
parent_iter = tree_store.get_iter (parent_path)
category_iter = tree_store.append (parent_iter, values)
# Append a dummy child row so that the expander's visible; we can # Append a dummy child row so that the expander's visible; we can
# then queue off the expander to load the programme listing for this category # then queue off the expander to load the programme listing for this category
self.tree_store.append (category_iter, [_('Loading…'), None, None]) tree_store.append (category_iter, [_('Loading…'), None, None])
return False return False
...@@ -73,36 +83,46 @@ class IplayerPlugin (totem.Plugin): ...@@ -73,36 +83,46 @@ class IplayerPlugin (totem.Plugin):
return return
# Populate it with programmes asynchronously # Populate it with programmes asynchronously
self.populate_programme_list (row_iter) #if tree_view.get_data ('id') == 'iplayer_programme_store':
feed = self.tv
#else:
# feed = self.radio
self.populate_programme_list (feed, tree_model, row_iter)
def _row_activated_cb (self, tree_view, path, view_column): def _row_activated_cb (self, tree_view, path, view_column):
tree_iter = self.tree_store.get_iter (path) tree_store = tree_view.get_model ()
mrl = self.tree_store.get_value (tree_iter, 2) tree_iter = tree_store.get_iter (path)
mrl = tree_store.get_value (tree_iter, 2)
# Only allow programme rows to be activated, not channel or category rows # Only allow programme rows to be activated, not channel or category rows
if mrl == None: if mrl == None:
return return
# Add the programme to the playlist and play it # Add the programme to the playlist and play it
self.totem.add_to_playlist_and_play (mrl, self.tree_store.get_value (tree_iter, 0), True) self.totem.add_to_playlist_and_play (mrl, tree_store.get_value (tree_iter, 0), True)
def populate_programme_list (self, category_iter): def populate_programme_list (self, feed, tree_store, category_iter):
if self.debug: if self.debug:
print "Populating programme list…" print "Populating programme list…"
category_path = self.tree_store.get_path (category_iter) category_path = tree_store.get_path (category_iter)
thread = PopulateProgrammesThread (self, self.tv, self.tree_store, category_path) thread = PopulateProgrammesThread (self, feed, tree_store, category_path)
thread.start () thread.start ()
def _populate_programme_list_cb (self, category_path, values, remove_placeholder): def _populate_programme_list_cb (self, tree_store, category_path, values, remove_placeholder):
# Callback from PopulateProgrammesThread to add stuff to the tree store # Callback from PopulateProgrammesThread to add stuff to the tree store
category_iter = self.tree_store.get_iter (category_path) if values == None:
totem.action_error (_('Error getting programme feed'), _('There was an unknown error getting the list of programmes for this channel and category combination.'))
return False
category_iter = tree_store.get_iter (category_path)
self.tree_store.append (category_iter, values) tree_store.append (category_iter, values)
# Remove the placeholder row # Remove the placeholder row
if remove_placeholder: if remove_placeholder:
self.tree_store.remove (self.tree_store.iter_children (category_iter)) tree_store.remove (tree_store.iter_children (category_iter))
return False return False
...@@ -130,12 +150,15 @@ class PopulateChannelsThread (threading.Thread): ...@@ -130,12 +150,15 @@ class PopulateChannelsThread (threading.Thread):
channel_id = self.tree_model.get_value (tree_iter, 1) channel_id = self.tree_model.get_value (tree_iter, 1)
parent_path = self.tree_model.get_path (tree_iter) parent_path = self.tree_model.get_path (tree_iter)
# Add this channel's categories as sub-rows try:
# We have to pass a path because the model could theoretically be modified # Add this channel's categories as sub-rows
# while the idle function is waiting in the queue, invalidating an iter # We have to pass a path because the model could theoretically be modified
for name, count in self.feed.get (channel_id).categories (): # while the idle function is waiting in the queue, invalidating an iter
category_id = category_name_to_id (name) for name, count in self.feed.get (channel_id).categories ():
gobject.idle_add (self.plugin._populate_channel_list_cb, parent_path, [name, category_id, None]) category_id = category_name_to_id (name)
gobject.idle_add (self.plugin._populate_channel_list_cb, self.tree_model, parent_path, [name, category_id, None])
except:
gobject.idle_add (self.plugin._populate_channel_list_cb, self.tree_model, parent_path, None)
tree_iter = self.tree_model.iter_next (tree_iter) tree_iter = self.tree_model.iter_next (tree_iter)
...@@ -158,13 +181,19 @@ class PopulateProgrammesThread (threading.Thread): ...@@ -158,13 +181,19 @@ class PopulateProgrammesThread (threading.Thread):
# Retrieve the programmes and return them # Retrieve the programmes and return them
feed = self.feed.get (channel_id).get (category_id) feed = self.feed.get (channel_id).get (category_id)
if feed == None: if feed == None:
totem.action_error (_('Error getting programme feed'), _('There was an unknown error getting the list of programmes for this channel and category combination.')) gobject.idle_add (self.plugin._populate_programme_list_cb, self.tree_model, self.category_path, None)
gobject.idle_add (self.plugin._populate_programme_list_cb, self.category_path, None) self.plugin.programme_download_lock.release ()
return
# Get the programmes
try:
programmes = feed.list ()
except:
gobject.idle_add (self.plugin._populate_programme_list_cb, self.tree_model, self.category_path, None)
self.plugin.programme_download_lock.release () self.plugin.programme_download_lock.release ()
return return
# Get the programmes and add them to the tree store # Add the programmes to the tree store
programmes = feed.list ()
remove_placeholder = True remove_placeholder = True
for programme in programmes: for programme in programmes:
programme_item = programme.programme programme_item = programme.programme
...@@ -174,10 +203,11 @@ class PopulateProgrammesThread (threading.Thread): ...@@ -174,10 +203,11 @@ class PopulateProgrammesThread (threading.Thread):
# which isn't currently supported by GStreamer or xine # which isn't currently supported by GStreamer or xine
media = programme_item.get_media_for ('mobile') media = programme_item.get_media_for ('mobile')
if media == None: if media == None:
# Not worth displaying an error in the interface for this
print "Programme has no HTTP streams" print "Programme has no HTTP streams"
continue continue
gobject.idle_add (self.plugin._populate_programme_list_cb, self.category_path, gobject.idle_add (self.plugin._populate_programme_list_cb, self.tree_model, self.category_path,
[programme.get_title (), programme.get_summary (), media.url], remove_placeholder) [programme.get_title (), programme.get_summary (), media.url], remove_placeholder)
remove_placeholder = False remove_placeholder = False
......
...@@ -9,32 +9,77 @@ ...@@ -9,32 +9,77 @@
</columns> </columns>
</object> </object>
<!--<object class="GtkTreeStore" id="iplayer_radio_programme_store">
<columns>
<column type="gchararray"/><!-Title->
<column type="gchararray"/><!-Summary->
<column type="gchararray"/><!-MRL->
</columns>
</object>-->
<object class="GtkVBox" id="iplayer_vbox"> <object class="GtkVBox" id="iplayer_vbox">
<property name="border-width">5</property> <property name="border-width">5</property>
<property name="homogeneous">False</property> <property name="homogeneous">False</property>
<property name="spacing">6</property> <property name="spacing">6</property>
<child> <child>
<object class="GtkScrolledWindow" id="iplayer_scrolled_window"> <!--<object class="GtkNotebook" id="iplayer_notebook">
<property name="hscrollbar-policy">GTK_POLICY_AUTOMATIC</property> <child>-->
<property name="vscrollbar-policy">GTK_POLICY_AUTOMATIC</property> <object class="GtkScrolledWindow" id="iplayer_scrolled_window">
<property name="hscrollbar-policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar-policy">GTK_POLICY_AUTOMATIC</property>
<child>
<object class="GtkTreeView" id="iplayer_programme_list">
<property name="model">iplayer_programme_store</property>
<property name="expander-column">iplayer_title_column</property>
<property name="headers-visible">False</property>
<child>
<object class="GtkTreeViewColumn" id="iplayer_title_column">
<child>
<object class="GtkCellRendererText" id="iplayer_title_cell_renderer"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
</child>
</object>
</child>
</object>
<!--</child>
<child type="tab">
<object class="GtkLabel" id="iplayer_tv_tab">
<property name="label" translatable="yes">Television</property>
</object>
</child>
<child> <child>
<object class="GtkTreeView" id="iplayer_programme_list"> <object class="GtkScrolledWindow" id="iplayer_radio_scrolled_window">
<property name="model">iplayer_programme_store</property> <property name="hscrollbar-policy">GTK_POLICY_AUTOMATIC</property>
<property name="expander-column">iplayer_title_column</property> <property name="vscrollbar-policy">GTK_POLICY_AUTOMATIC</property>
<property name="headers-visible">False</property>
<child> <child>
<object class="GtkTreeViewColumn" id="iplayer_title_column"> <object class="GtkTreeView" id="iplayer_radio_programme_list">
<property name="model">iplayer_radio_programme_store</property>
<property name="expander-column">iplayer_radio_title_column</property>
<property name="headers-visible">False</property>
<child> <child>
<object class="GtkCellRendererText" id="iplayer_title_cell_renderer"/> <object class="GtkTreeViewColumn" id="iplayer_radio_title_column">
<attributes> <child>
<attribute name="text">0</attribute> <object class="GtkCellRendererText" id="iplayer_radio_title_cell_renderer"/>
</attributes> <attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
</child> </child>
</object> </object>
</child> </child>
</object> </object>
</child> </child>
</object> <child type="tab">
<object class="GtkLabel" id="iplayer_radio_tab">
<property name="label" translatable="yes">Radio</property>
</object>
</child>
</object>-->
</child> </child>
</object> </object>
</interface> </interface>
...@@ -318,15 +318,7 @@ def httpretrieve(url, filename): ...@@ -318,15 +318,7 @@ def httpretrieve(url, filename):
f.close() f.close()
def httpget(url): def httpget(url):
resp = '' resp, data = http.request(url, 'GET')
data = ''
try:
resp, data = http.request(url, 'GET')
except:
#print "Response for status %s for %s" % (resp.status, data)
totem.action_error (_('Network Error'), _('Failed to fetch URI: %s') % url)
raise
return data return data
def parse_entry_id(entry_id): def parse_entry_id(entry_id):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment