[totem] plugins: Re-indent Python files and fix whitespace issues
- From: Philip Withnall <pwithnall src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [totem] plugins: Re-indent Python files and fix whitespace issues
- Date: Tue, 5 Apr 2011 00:14:51 +0000 (UTC)
commit e9e33d203d7f162e147f3cb9ed603fead24bc62a
Author: Philip Withnall <philip tecnocode co uk>
Date: Sun Mar 27 10:59:39 2011 +0100
plugins: Re-indent Python files and fix whitespace issues
Nothing is changed in this commit apart from whitespace and line wrapping,
as complained about by pylint.
See: http://www.python.org/dev/peps/pep-0008/
Helps: bgo#645739
src/plugins/coherence_upnp/coherence_upnp.py | 141 +++--
src/plugins/dbus-service/dbus-service.py | 554 +++++++++--------
src/plugins/iplayer/iplayer.py | 446 +++++++------
src/plugins/iplayer/iplayer2.py | 891 ++++++++++++++------------
src/plugins/iplayer/listparser.py | 93 ++--
src/plugins/jamendo/jamendo.py | 564 +++++++++--------
src/plugins/opensubtitles/hash.py | 78 ++--
src/plugins/opensubtitles/opensubtitles.py | 808 ++++++++++++-----------
src/plugins/pythonconsole/pythonconsole.py | 203 ++++---
src/plugins/sample-python/sample-python.py | 18 +-
10 files changed, 2010 insertions(+), 1786 deletions(-)
---
diff --git a/src/plugins/coherence_upnp/coherence_upnp.py b/src/plugins/coherence_upnp/coherence_upnp.py
index d785830..8863e19 100644
--- a/src/plugins/coherence_upnp/coherence_upnp.py
+++ b/src/plugins/coherence_upnp/coherence_upnp.py
@@ -10,102 +10,109 @@ from gi.repository import Gtk
from gi.repository import Totem
from coherence.ui.av_widgets import TreeWidget
-from coherence.ui.av_widgets import UDN_COLUMN,UPNP_CLASS_COLUMN,SERVICE_COLUMN
+from coherence.ui.av_widgets import UDN_COLUMN, UPNP_CLASS_COLUMN
+from coherence.ui.av_widgets import SERVICE_COLUMN
-gettext.textdomain("totem")
+gettext.textdomain ("totem")
D_ = gettext.dgettext
_ = gettext.gettext
-class UPnPClient(gobject.GObject, Peas.Activatable):
+class UPnPClient (gobject.GObject, Peas.Activatable):
__gtype_name__ = 'UPnPClient'
- object = gobject.property(type = gobject.GObject)
+ object = gobject.property (type = gobject.GObject)
def __init__ (self):
self.totem_object = None
- self.ui = TreeWidget()
- self.ui.window.set_shadow_type(gtk.SHADOW_IN)
+ self.ui = TreeWidget ()
+ self.ui.window.set_shadow_type (gtk.SHADOW_IN)
self.ui.cb_item_right_click = self.button_pressed
- self.ui.window.show_all()
- selection = self.ui.treeview.get_selection()
- selection.set_mode(gtk.SELECTION_MULTIPLE)
+ self.ui.window.show_all ()
+ selection = self.ui.treeview.get_selection ()
+ selection.set_mode (gtk.SELECTION_MULTIPLE)
- def button_pressed(self, widget, event):
+ def button_pressed (self, widget, event):
if event.button == 3:
- x = int(event.x)
- y = int(event.y)
+ x = int (event.x)
+ y = int (event.y)
try:
- row_path,column,_,_ = self.ui.treeview.get_path_at_pos(x, y)
- selection = self.ui.treeview.get_selection()
- if not selection.path_is_selected(row_path):
- self.ui.treeview.set_cursor(row_path,column,False)
+ row_path, column, _, _ = self.ui.treeview.get_path_at_pos (x, y)
+ selection = self.ui.treeview.get_selection ()
+ if not selection.path_is_selected (row_path):
+ self.ui.treeview.set_cursor (row_path, column, False)
print "button_pressed", row_path, (row_path[0],)
- iter = self.ui.store.get_iter((row_path[0],))
- udn, = self.ui.store.get(iter,UDN_COLUMN)
- iter = self.ui.store.get_iter(row_path)
- upnp_class,url = self.ui.store.get(iter,UPNP_CLASS_COLUMN,SERVICE_COLUMN)
+ iter = self.ui.store.get_iter ((row_path[0],))
+ udn, = self.ui.store.get (iter, UDN_COLUMN)
+ iter = self.ui.store.get_iter (row_path)
+ upnp_class, url = self.ui.store.get (iter, UPNP_CLASS_COLUMN,
+ SERVICE_COLUMN)
print udn, upnp_class, url
- if(not upnp_class.startswith('object.container') and
- not upnp_class == 'root'):
- self.create_item_context(has_delete=self.ui.device_has_action(udn,'ContentDirectory','DestroyObject'))
- self.context.popup(None,None,None,event.button,event.time)
+ if (not upnp_class.startswith ('object.container') and
+ not upnp_class == 'root'):
+ has_delete = self.ui.device_has_action (udn,
+ 'ContentDirectory',
+ 'DestroyObject')
+ self.create_item_context (has_delete = has_delete)
+ self.context.popup (None, None, None, event.button,
+ event.time)
return 1
except TypeError:
pass
return 1
- def create_item_context(self,has_delete=False):
+ def create_item_context (self, has_delete = False):
""" create context menu for right click in treeview item"""
- def action(menu, text):
- selection = self.ui.treeview.get_selection()
- model, selected_rows = selection.get_selected_rows()
+ def action (menu, text):
+ selection = self.ui.treeview.get_selection ()
+ model, selected_rows = selection.get_selected_rows ()
if text == 'item.delete':
for row_path in selected_rows:
- self.ui.destroy_object(row_path)
+ self.ui.destroy_object (row_path)
return
- if(len(selected_rows) > 0 and
- text ==' item.play'):
- row_path = selected_rows.pop(0)
- iter = self.ui.store.get_iter(row_path)
- url, = self.ui.store.get(iter,SERVICE_COLUMN)
- self.totem_object.action_remote(totem.REMOTE_COMMAND_REPLACE,url)
- self.totem_object.action_remote(totem.REMOTE_COMMAND_PLAY,url)
+ if (len (selected_rows) > 0 and text ==' item.play'):
+ row_path = selected_rows.pop (0)
+ iter = self.ui.store.get_iter (row_path)
+ url, = self.ui.store.get (iter, SERVICE_COLUMN)
+ self.totem_object.action_remote (totem.REMOTE_COMMAND_REPLACE,
+ url)
+ self.totem_object.action_remote (totem.REMOTE_COMMAND_PLAY, url)
for row_path in selected_rows:
- iter = self.ui.store.get_iter(row_path)
- url, = self.ui.store.get(iter,SERVICE_COLUMN)
- self.totem_object.action_remote(totem.REMOTE_COMMAND_ENQUEUE,url)
- self.totem_object.action_remote(totem.REMOTE_COMMAND_PLAY,url)
-
- if not hasattr(self, 'context_no_delete'):
- self.context_no_delete = gtk.Menu()
+ iter = self.ui.store.get_iter (row_path)
+ url, = self.ui.store.get (iter, SERVICE_COLUMN)
+ self.totem_object.action_remote (totem.REMOTE_COMMAND_ENQUEUE,
+ url)
+ self.totem_object.action_remote (totem.REMOTE_COMMAND_PLAY, url)
+
+ if not hasattr (self, 'context_no_delete'):
+ self.context_no_delete = gtk.Menu ()
# Translators: this refers to a media file
- play_menu = gtk.MenuItem(_(u"Play"))
- play_menu.connect("activate", action, 'item.play')
+ play_menu = gtk.MenuItem (_(u"Play"))
+ play_menu.connect ("activate", action, 'item.play')
# Translators: this refers to a media file
- enqueue_menu = gtk.MenuItem(_(u"Enqueue"))
- enqueue_menu.connect("activate", action, 'item.enqueue')
- self.context_no_delete.append(play_menu)
- self.context_no_delete.append(enqueue_menu)
- self.context_no_delete.show_all()
-
- if not hasattr(self, 'context_with_delete'):
- self.context_with_delete = gtk.Menu()
+ enqueue_menu = gtk.MenuItem (_(u"Enqueue"))
+ enqueue_menu.connect ("activate", action, 'item.enqueue')
+ self.context_no_delete.append (play_menu)
+ self.context_no_delete.append (enqueue_menu)
+ self.context_no_delete.show_all ()
+
+ if not hasattr (self, 'context_with_delete'):
+ self.context_with_delete = gtk.Menu ()
# Translators: this refers to a media file
- play_menu = gtk.MenuItem(_(u"Play"))
- play_menu.connect("activate", action, 'item.play')
+ play_menu = gtk.MenuItem (_(u"Play"))
+ play_menu.connect ("activate", action, 'item.play')
# Translators: this refers to a media file
- enqueue_menu = gtk.MenuItem(_(u"Enqueue"))
- enqueue_menu.connect("activate", action, 'item.enqueue')
- self.context_with_delete.append(play_menu)
- self.context_with_delete.append(enqueue_menu)
- self.context_with_delete.append(gtk.SeparatorMenuItem())
+ enqueue_menu = gtk.MenuItem (_(u"Enqueue"))
+ enqueue_menu.connect ("activate", action, 'item.enqueue')
+ self.context_with_delete.append (play_menu)
+ self.context_with_delete.append (enqueue_menu)
+ self.context_with_delete.append (gtk.SeparatorMenuItem ())
# Translators: this refers to a media file
- menu = gtk.MenuItem(_(u"Delete"))
- menu.connect("activate", action, 'item.delete')
- self.context_with_delete.append(menu)
- self.context_with_delete.show_all()
+ menu = gtk.MenuItem (_(u"Delete"))
+ menu.connect ("activate", action, 'item.delete')
+ self.context_with_delete.append (menu)
+ self.context_with_delete.show_all ()
if has_delete:
self.context = self.context_with_delete
@@ -114,9 +121,11 @@ class UPnPClient(gobject.GObject, Peas.Activatable):
def do_activate (self):
self.totem_object = self.object
- self.totem_object.add_sidebar_page ("upnp-coherence", _(u"Coherence DLNA/UPnP Client"), self.ui.window)
+ self.totem_object.add_sidebar_page ("upnp-coherence",
+ _(u"Coherence DLNA/UPnP Client"),
+ self.ui.window)
- def load_and_play(url):
+ def load_and_play (url):
self.totem_object.add_to_playlist_and_play (url, '', True)
self.ui.cb_item_dbl_click = load_and_play
diff --git a/src/plugins/dbus-service/dbus-service.py b/src/plugins/dbus-service/dbus-service.py
index 7d96ffa..69a83c9 100644
--- a/src/plugins/dbus-service/dbus-service.py
+++ b/src/plugins/dbus-service/dbus-service.py
@@ -27,265 +27,307 @@ from gi.repository import Totem
import dbus, dbus.service
from dbus.mainloop.glib import DBusGMainLoop
-class dbusservice(gobject.GObject, Peas.Activatable):
- __gtype_name__ = 'dbusservice'
+class dbusservice (gobject.GObject, Peas.Activatable):
+ __gtype_name__ = 'dbusservice'
- object = gobject.property(type = gobject.GObject)
+ object = gobject.property (type = gobject.GObject)
- def do_activate(self):
- DBusGMainLoop(set_as_default = True)
+ def do_activate (self):
+ DBusGMainLoop (set_as_default = True)
- name = dbus.service.BusName ('org.mpris.Totem', bus = dbus.SessionBus ())
- self.root = Root (name, self.object)
- self.player = Player (name, self.object)
- self.track_list = TrackList (name, self.object)
+ name = dbus.service.BusName ('org.mpris.Totem',
+ bus = dbus.SessionBus ())
+ self.root = Root (name, self.object)
+ self.player = Player (name, self.object)
+ self.track_list = TrackList (name, self.object)
- def do_deactivate(self):
- self.root.disconnect() # ensure we don't leak our paths on the bus
- self.player.disconnect()
- self.track_list.disconnect()
+ def do_deactivate (self):
+ self.root.disconnect () # ensure we don't leak our paths on the bus
+ self.player.disconnect ()
+ self.track_list.disconnect ()
class Root (dbus.service.Object):
- def __init__(self, name, totem):
- dbus.service.Object.__init__ (self, name, '/')
- self.totem = totem
-
- def disconnect(self):
- self.remove_from_connection(None, None)
-
- @dbus.service.method(dbus_interface='org.freedesktop.MediaPlayer', in_signature='', out_signature='s')
- def Identity(self):
- return self.totem.get_version()
-
- @dbus.service.method(dbus_interface='org.freedesktop.MediaPlayer', in_signature='', out_signature='')
- def Quit(self):
- self.totem.action_exit()
-
- @dbus.service.method(dbus_interface='org.freedesktop.MediaPlayer', in_signature='', out_signature='(qq)')
- def MprisVersion(self):
- return dbus.Struct((dbus.UInt16(1), dbus.UInt16(0)), signature='(qq)')
-
-class Player(dbus.service.Object):
- def __init__(self, name, totem):
- dbus.service.Object.__init__(self, name, '/Player')
- self.totem = totem
-
- self.null_metadata = {"year" : "", "tracknumber" : "", "location" : "",
- "title" : "", "album" : "", "time" : "", "genre" : "", "artist" : ""}
- self.old_metadata = self.null_metadata.copy()
- self.current_metadata = self.null_metadata.copy()
- self.old_caps = 64 # at startup, we can only support a playlist
- self.old_status = (2, 0, 0, 0) # startup state
-
- totem.connect("metadata-updated", self.do_update_metadata)
- totem.connect("notify::playing", self.do_notify)
- totem.connect("notify::seekable", self.do_notify)
- totem.connect("notify::current-mrl", self.do_notify)
-
- def do_update_metadata(self, totem, artist, title, album, num):
- self.current_metadata = self.null_metadata.copy()
- if title:
- self.current_metadata["title"] = title
- if artist:
- self.current_metadata["artist"] = artist
- if album:
- self.current_metadata["album"] = album
- if num:
- self.current_metadata["tracknumber"] = num
-
- if totem.is_playing():
- self.track_change(self.current_metadata)
-
- def do_notify(self, totem, status):
- if totem.is_playing():
- self.track_change(self.current_metadata)
- else:
- self.track_change(self.null_metadata)
-
- status = self.calculate_status()
- if status != self.old_status:
- self.status_change(status)
-
- caps = self.calculate_caps()
- if caps != self.old_caps:
- self.caps_change(caps)
-
- def calculate_status(self):
- if self.totem.is_playing():
- playing_status = 0
- elif self.totem.is_paused():
- playing_status = 1
- else:
- playing_status = 2
-
- if self.totem.action_remote_get_setting(Totem.RemoteSetting.SHUFFLE):
- shuffle_status = 1
- else:
- shuffle_status = 0
-
- if self.totem.action_remote_get_setting(Totem.RemoteSetting.REPEAT):
- repeat_status = 1
- else:
- repeat_status = 0
-
- return (
- dbus.Int32(playing_status), # 0 = Playing, 1 = Paused, 2 = Stopped
- dbus.Int32(self.totem.action_remote_get_setting(Totem.RemoteSetting.SHUFFLE)), # 0 = Playing linearly , 1 = Playing randomly
- dbus.Int32(0), # 0 = Go to the next element once the current has finished playing , 1 = Repeat the current element
- dbus.Int32(self.totem.action_remote_get_setting(Totem.RemoteSetting.REPEAT)) # 0 = Stop playing once the last element has been played, 1 = Never give up playing
- )
-
- def calculate_caps(self):
- caps = 64 # we can always have a playlist
- playlist_length = self.totem.get_playlist_length()
- playlist_pos = self.totem.get_playlist_pos()
-
- if playlist_pos < playlist_length - 1:
- caps |= 1 << 0 # go next
- if playlist_pos > 0:
- caps |= 1 << 1 # go previous
- if playlist_length > 0:
- caps |= 1 << 2 # pause
- caps |= 1 << 3 # play
- if self.totem.is_seekable():
- caps |= 1 << 4 # seek
- if self.current_metadata != self.null_metadata:
- caps |= 1 << 5 # get metadata
-
- return caps
-
- def track_change(self, metadata):
- if self.old_metadata != metadata:
- self.old_metadata = metadata.copy()
- self.TrackChange(metadata)
-
- def status_change(self, status):
- if self.old_status != status:
- self.old_status = status
- self.StatusChange(status)
-
- def caps_change(self, caps):
- if self.old_caps != caps:
- self.old_caps = caps
- self.CapsChange(caps)
-
- def disconnect(self):
- self.TrackChange(self.null_metadata)
- self.remove_from_connection(None, None)
-
- @dbus.service.signal(dbus_interface = "org.freedesktop.MediaPlayer", signature='a{sv}')
- def TrackChange(self, metadata):
- pass
-
- @dbus.service.signal(dbus_interface = "org.freedesktop.MediaPlayer", signature='(iiii)')
- def StatusChange(self, status):
- pass
-
- @dbus.service.signal(dbus_interface = "org.freedesktop.MediaPlayer", signature='i')
- def CapsChange(self, caps):
- pass
-
- @dbus.service.method(dbus_interface='org.freedesktop.MediaPlayer', in_signature='', out_signature='')
- def Next(self):
- self.totem.action_next()
-
- @dbus.service.method(dbus_interface='org.freedesktop.MediaPlayer', in_signature='', out_signature='')
- def Prev(self):
- self.totem.action_previous()
-
- @dbus.service.method(dbus_interface='org.freedesktop.MediaPlayer', in_signature='', out_signature='')
- def Pause(self):
- self.totem.action_play_pause()
-
- @dbus.service.method(dbus_interface='org.freedesktop.MediaPlayer', in_signature='', out_signature='')
- def Stop(self):
- self.totem.action_stop()
-
- @dbus.service.method(dbus_interface='org.freedesktop.MediaPlayer', in_signature='', out_signature='')
- def Play(self):
- # If playing : rewind to the beginning of current track, else : start playing.
- if self.totem.is_playing():
- self.totem.action_seek_time(0, False)
- else:
- self.totem.action_play()
-
- @dbus.service.method(dbus_interface='org.freedesktop.MediaPlayer', in_signature='b', out_signature='')
- def Repeat(self, value):
- pass # we don't support repeating individual tracks
-
- @dbus.service.method(dbus_interface='org.freedesktop.MediaPlayer', in_signature='', out_signature='(iiii)')
- def GetStatus(self):
- status = self.calculate_status()
- self.old_status = status
- return dbus.Struct(status, signature='(iiii)')
-
- @dbus.service.method(dbus_interface='org.freedesktop.MediaPlayer', in_signature='', out_signature='a{sv}')
- def GetMetadata(self):
- return self.current_metadata
-
- @dbus.service.method(dbus_interface='org.freedesktop.MediaPlayer', in_signature='', out_signature='i')
- def GetCaps(self):
- caps = self.calculate_caps()
- self.old_caps = caps
- return caps
-
- @dbus.service.method(dbus_interface='org.freedesktop.MediaPlayer', in_signature='i', out_signature='')
- def VolumeSet(self, volume):
- self.totem.action_volume(volume / 100.0)
-
- @dbus.service.method(dbus_interface='org.freedesktop.MediaPlayer', in_signature='', out_signature='i')
- def VolumeGet(self):
- return dbus.Int32(self.totem.get_volume() * 100)
-
- @dbus.service.method(dbus_interface='org.freedesktop.MediaPlayer', in_signature='i', out_signature='')
- def PositionSet(self, position):
- self.totem.action_seek_time(position, False)
-
- @dbus.service.method(dbus_interface='org.freedesktop.MediaPlayer', in_signature='', out_signature='i')
- def PositionGet(self):
- return dbus.Int32(self.totem.props.current_time)
-
-class TrackList(dbus.service.Object):
- def __init__(self, name, totem):
- dbus.service.Object.__init__(self, name, '/TrackList')
- self.totem = totem
-
- def disconnect(self):
- self.remove_from_connection(None, None)
-
- @dbus.service.signal(dbus_interface = "org.freedesktop.MediaPlayer", signature='i')
- def TrackListChange(self, length):
- # TODO: we can't implement this until TotemPlaylist is exposed in the Python API
- pass
-
- @dbus.service.method(dbus_interface='org.freedesktop.MediaPlayer', in_signature='i', out_signature='a{sv}')
- def GetMetadata(self, pos):
- # Since the API doesn't currently exist in Totem to get the rest of the metadata, we can only return the title
- return { "title" : self.totem.get_title_at_playlist_pos(pos) }
-
- @dbus.service.method(dbus_interface='org.freedesktop.MediaPlayer', in_signature='', out_signature='i')
- def GetCurrentTrack(self):
- return self.totem.get_playlist_pos()
-
- @dbus.service.method(dbus_interface='org.freedesktop.MediaPlayer', in_signature='', out_signature='i')
- def GetLength(self):
- return self.totem.get_playlist_length()
-
- @dbus.service.method(dbus_interface='org.freedesktop.MediaPlayer', in_signature='sb', out_signature='i')
- def AddTrack(self, uri, play_immediately):
- # We can't currently support !play_immediately
- self.totem.add_to_playlist_and_play(str(uri), '', True)
- return 0
-
- @dbus.service.method(dbus_interface='org.freedesktop.MediaPlayer', in_signature='i', out_signature='')
- def DelTrack(self, pos):
- # TODO: we need TotemPlaylist exposed by the Python API for this
- pass
-
- @dbus.service.method(dbus_interface='org.freedesktop.MediaPlayer', in_signature='b', out_signature='')
- def SetLoop(self, loop):
- self.totem.action_remote_set_setting(Totem.RemoteSetting.REPEAT, loop)
-
- @dbus.service.method(dbus_interface='org.freedesktop.MediaPlayer', in_signature='b', out_signature='')
- def SetRandom(self, random):
- self.totem.action_remote_set_setting(Totem.RemoteSetting.SHUFFLE, random)
+ def __init__ (self, name, totem):
+ dbus.service.Object.__init__ (self, name, '/')
+ self.totem = totem
+
+ def disconnect (self):
+ self.remove_from_connection (None, None)
+
+ @dbus.service.method (dbus_interface='org.freedesktop.MediaPlayer',
+ in_signature = '', out_signature = 's')
+ def Identity (self):
+ return self.totem.get_version ()
+
+ @dbus.service.method (dbus_interface='org.freedesktop.MediaPlayer',
+ in_signature = '', out_signature = '')
+ def Quit (self):
+ self.totem.action_exit ()
+
+ @dbus.service.method (dbus_interface='org.freedesktop.MediaPlayer',
+ in_signature = '', out_signature = '(qq)')
+ def MprisVersion (self):
+ return dbus.Struct ((dbus.UInt16 (1), dbus.UInt16 (0)),
+ signature = '(qq)')
+
+class Player (dbus.service.Object):
+ def __init__ (self, name, totem):
+ dbus.service.Object.__init__ (self, name, '/Player')
+ self.totem = totem
+
+ self.null_metadata = {
+ "year" : "", "tracknumber" : "", "location" : "",
+ "title" : "", "album" : "", "time" : "", "genre" : "",
+ "artist" : ""
+ }
+ self.old_metadata = self.null_metadata.copy ()
+ self.current_metadata = self.null_metadata.copy ()
+ self.old_caps = 64 # at startup, we can only support a playlist
+ self.old_status = (2, 0, 0, 0) # startup state
+
+ totem.connect ("metadata-updated", self.do_update_metadata)
+ totem.connect ("notify::playing", self.do_notify)
+ totem.connect ("notify::seekable", self.do_notify)
+ totem.connect ("notify::current-mrl", self.do_notify)
+
+ def do_update_metadata (self, totem, artist, title, album, num):
+ self.current_metadata = self.null_metadata.copy ()
+ if title:
+ self.current_metadata["title"] = title
+ if artist:
+ self.current_metadata["artist"] = artist
+ if album:
+ self.current_metadata["album"] = album
+ if num:
+ self.current_metadata["tracknumber"] = num
+
+ if totem.is_playing ():
+ self.track_change (self.current_metadata)
+
+ def do_notify (self, totem, status):
+ if totem.is_playing ():
+ self.track_change (self.current_metadata)
+ else:
+ self.track_change (self.null_metadata)
+
+ status = self.calculate_status ()
+ if status != self.old_status:
+ self.status_change (status)
+
+ caps = self.calculate_caps ()
+ if caps != self.old_caps:
+ self.caps_change (caps)
+
+ def calculate_status (self):
+ if self.totem.is_playing ():
+ playing_status = 0
+ elif self.totem.is_paused ():
+ playing_status = 1
+ else:
+ playing_status = 2
+
+ if self.totem.action_remote_get_setting (Totem.RemoteSetting.SHUFFLE):
+ shuffle_status = 1
+ else:
+ shuffle_status = 0
+
+ if self.totem.action_remote_get_setting (Totem.RemoteSetting.REPEAT):
+ repeat_status = 1
+ else:
+ repeat_status = 0
+
+ return (
+ # 0 = Playing, 1 = Paused, 2 = Stopped
+ dbus.Int32 (playing_status),
+ # 0 = Playing linearly , 1 = Playing randomly
+ dbus.Int32 (shuffle_status),
+ # 0 = Go to the next element once the current has finished playing,
+ # 1 = Repeat the current element
+ dbus.Int32 (0),
+ # 0 = Stop playing once the last element has been played,
+ # 1 = Never give up playing
+ dbus.Int32 (repeat_status)
+ )
+
+ def calculate_caps (self):
+ caps = 64 # we can always have a playlist
+ playlist_length = self.totem.get_playlist_length ()
+ playlist_pos = self.totem.get_playlist_pos ()
+
+ if playlist_pos < playlist_length - 1:
+ caps |= 1 << 0 # go next
+ if playlist_pos > 0:
+ caps |= 1 << 1 # go previous
+ if playlist_length > 0:
+ caps |= 1 << 2 # pause
+ caps |= 1 << 3 # play
+ if self.totem.is_seekable ():
+ caps |= 1 << 4 # seek
+ if self.current_metadata != self.null_metadata:
+ caps |= 1 << 5 # get metadata
+
+ return caps
+
+ def track_change (self, metadata):
+ if self.old_metadata != metadata:
+ self.old_metadata = metadata.copy ()
+ self.TrackChange (metadata)
+
+ def status_change (self, status):
+ if self.old_status != status:
+ self.old_status = status
+ self.StatusChange (status)
+
+ def caps_change (self, caps):
+ if self.old_caps != caps:
+ self.old_caps = caps
+ self.CapsChange (caps)
+
+ def disconnect (self):
+ self.TrackChange (self.null_metadata)
+ self.remove_from_connection (None, None)
+
+ @dbus.service.signal (dbus_interface = "org.freedesktop.MediaPlayer",
+ signature = 'a{sv}')
+ def TrackChange (self, metadata):
+ pass
+
+ @dbus.service.signal (dbus_interface = "org.freedesktop.MediaPlayer",
+ signature = '(iiii)')
+ def StatusChange (self, status):
+ pass
+
+ @dbus.service.signal (dbus_interface = "org.freedesktop.MediaPlayer",
+ signature = 'i')
+ def CapsChange (self, caps):
+ pass
+
+ @dbus.service.method (dbus_interface='org.freedesktop.MediaPlayer',
+ in_signature = '', out_signature = '')
+ def Next (self):
+ self.totem.action_next ()
+
+ @dbus.service.method (dbus_interface='org.freedesktop.MediaPlayer',
+ in_signature = '', out_signature = '')
+ def Prev (self):
+ self.totem.action_previous ()
+
+ @dbus.service.method (dbus_interface='org.freedesktop.MediaPlayer',
+ in_signature = '', out_signature = '')
+ def Pause (self):
+ self.totem.action_play_pause ()
+
+ @dbus.service.method (dbus_interface='org.freedesktop.MediaPlayer',
+ in_signature = '', out_signature = '')
+ def Stop (self):
+ self.totem.action_stop ()
+
+ @dbus.service.method (dbus_interface='org.freedesktop.MediaPlayer',
+ in_signature = '', out_signature = '')
+ def Play (self):
+ # If playing : rewind to the beginning of current track,
+ # else : start playing.
+ if self.totem.is_playing ():
+ self.totem.action_seek_time (0, False)
+ else:
+ self.totem.action_play ()
+
+ @dbus.service.method (dbus_interface='org.freedesktop.MediaPlayer',
+ in_signature = 'b', out_signature = '')
+ def Repeat (self, value):
+ pass # we don't support repeating individual tracks
+
+ @dbus.service.method (dbus_interface='org.freedesktop.MediaPlayer',
+ in_signature = '', out_signature = '(iiii)')
+ def GetStatus (self):
+ status = self.calculate_status ()
+ self.old_status = status
+ return dbus.Struct (status, signature = '(iiii)')
+
+ @dbus.service.method (dbus_interface='org.freedesktop.MediaPlayer',
+ in_signature = '', out_signature = 'a{sv}')
+ def GetMetadata (self):
+ return self.current_metadata
+
+ @dbus.service.method (dbus_interface='org.freedesktop.MediaPlayer',
+ in_signature = '', out_signature = 'i')
+ def GetCaps (self):
+ caps = self.calculate_caps ()
+ self.old_caps = caps
+ return caps
+
+ @dbus.service.method (dbus_interface='org.freedesktop.MediaPlayer',
+ in_signature = 'i', out_signature = '')
+ def VolumeSet (self, volume):
+ self.totem.action_volume (volume / 100.0)
+
+ @dbus.service.method (dbus_interface='org.freedesktop.MediaPlayer',
+ in_signature = '', out_signature = 'i')
+ def VolumeGet (self):
+ return dbus.Int32 (self.totem.get_volume () * 100)
+
+ @dbus.service.method (dbus_interface='org.freedesktop.MediaPlayer',
+ in_signature = 'i', out_signature = '')
+ def PositionSet (self, position):
+ self.totem.action_seek_time (position, False)
+
+ @dbus.service.method (dbus_interface='org.freedesktop.MediaPlayer',
+ in_signature = '', out_signature = 'i')
+ def PositionGet (self):
+ return dbus.Int32 (self.totem.props.current_time)
+
+class TrackList (dbus.service.Object):
+ def __init__ (self, name, totem):
+ dbus.service.Object.__init__ (self, name, '/TrackList')
+ self.totem = totem
+
+ def disconnect (self):
+ self.remove_from_connection (None, None)
+
+ @dbus.service.signal (dbus_interface = "org.freedesktop.MediaPlayer",
+ signature = 'i')
+ def TrackListChange (self, length):
+ # TODO: we can't implement this until TotemPlaylist is exposed in the
+ # Python API
+ pass
+
+ @dbus.service.method (dbus_interface='org.freedesktop.MediaPlayer',
+ in_signature = 'i', out_signature = 'a{sv}')
+ def GetMetadata (self, pos):
+ # Since the API doesn't currently exist in Totem to get the rest of the
+ # metadata, we can only return the title
+ return { "title" : self.totem.get_title_at_playlist_pos (pos) }
+
+ @dbus.service.method (dbus_interface='org.freedesktop.MediaPlayer',
+ in_signature = '', out_signature = 'i')
+ def GetCurrentTrack (self):
+ return self.totem.get_playlist_pos ()
+
+ @dbus.service.method (dbus_interface='org.freedesktop.MediaPlayer',
+ in_signature = '', out_signature = 'i')
+ def GetLength (self):
+ return self.totem.get_playlist_length ()
+
+ @dbus.service.method (dbus_interface='org.freedesktop.MediaPlayer',
+ in_signature = 'sb', out_signature = 'i')
+ def AddTrack (self, uri, play_immediately):
+ # We can't currently support !play_immediately
+ self.totem.add_to_playlist_and_play (str (uri), '', True)
+ return 0
+
+ @dbus.service.method (dbus_interface='org.freedesktop.MediaPlayer',
+ in_signature = 'i', out_signature = '')
+ def DelTrack (self, pos):
+ # TODO: we need TotemPlaylist exposed by the Python API for this
+ pass
+
+ @dbus.service.method (dbus_interface='org.freedesktop.MediaPlayer',
+ in_signature = 'b', out_signature = '')
+ def SetLoop (self, loop):
+ self.totem.action_remote_set_setting (Totem.RemoteSetting.REPEAT, loop)
+
+ @dbus.service.method (dbus_interface='org.freedesktop.MediaPlayer',
+ in_signature = 'b', out_signature = '')
+ def SetRandom (self, random):
+ self.totem.action_remote_set_setting (Totem.RemoteSetting.SHUFFLE,
+ random)
diff --git a/src/plugins/iplayer/iplayer.py b/src/plugins/iplayer/iplayer.py
index 26baea0..848664a 100644
--- a/src/plugins/iplayer/iplayer.py
+++ b/src/plugins/iplayer/iplayer.py
@@ -8,226 +8,254 @@ from gi.repository import Totem
import iplayer2
import threading
-gettext.textdomain("totem")
+gettext.textdomain ("totem")
D_ = gettext.dgettext
_ = gettext.gettext
class IplayerPlugin (gobject.GObject, Peas.Activatable):
- __gtype_name__ = 'IplayerPlugin'
+ __gtype_name__ = 'IplayerPlugin'
- object = gobject.property(type = gobject.GObject)
-
- def __init__ (self):
- self.debug = False
- self.totem = None
- self.programme_download_lock = threading.Lock ()
-
- def do_activate (self):
- self.totem = self.object
- # Build the interface
- builder = Totem.plugin_load_interface ("iplayer", "iplayer.ui", True, self.totem.get_main_window (), self)
- container = builder.get_object ('iplayer_vbox')
-
- self.tv_tree_store = builder.get_object ('iplayer_programme_store')
- programme_list = builder.get_object ('iplayer_programme_list')
- programme_list.connect ('row-expanded', self._row_expanded_cb)
- programme_list.connect ('row-activated', self._row_activated_cb)
-
- container.show_all ()
-
- self.tv = iplayer2.feed ('tv')
-
- # Add the interface to Totem's sidebar
- self.totem.add_sidebar_page ("iplayer", _(u"BBC iPlayer"), container)
-
- # Get the channel category listings
- self.populate_channel_list (self.tv, self.tv_tree_store)
-
- def do_deactivate (self):
- self.totem.remove_sidebar_page ("iplayer")
-
- def populate_channel_list (self, feed, tree_store):
- if self.debug:
- print "Populating channel listâ?¦"
-
- # Add all the channels as top-level rows in the tree store
- channels = feed.channels ()
- for channel_id, title in channels.items ():
- parent_iter = tree_store.append (None, (title, channel_id, None))
-
- # Add the channels' categories in a thread, since they each require a network request
- parent_path = tree_store.get_path (parent_iter)
- thread = PopulateChannelsThread (self, parent_path, feed, tree_store)
- thread.start ()
-
- def _populate_channel_list_cb (self, tree_store, parent_path, values):
- # Callback from PopulateChannelsThread to add stuff to the tree store
- if values == None:
- self.totem.action_error (_(u'Error listing channel categories'), _(u'There was an unknown error getting the list of television channels available on BBC iPlayer.'))
- 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
- # then queue off the expander to load the programme listing for this category
- tree_store.append (category_iter, [_(u'Loadingâ?¦'), None, None])
-
- return False
-
- def _row_expanded_cb (self, tree_view, row_iter, path):
- tree_model = tree_view.get_model ()
-
- if self.debug:
- print "_row_expanded_cb called."
-
- # We only care about the category level (level 1), and only when
- # it has the "Loading..." placeholder child row
- if get_iter_level (tree_model, row_iter) != 1 or tree_model.iter_n_children (row_iter) != 1:
- return
-
- # Populate it with programmes asynchronously
- self.populate_programme_list (self.tv, tree_model, row_iter)
-
- def _row_activated_cb (self, tree_view, path, view_column):
- tree_store = tree_view.get_model ()
- tree_iter = tree_store.get_iter (path)
- if tree_iter == None:
- return
-
- mrl = tree_store.get_value (tree_iter, 2)
-
- # Only allow programme rows to be activated, not channel or category rows
- if mrl == None:
- return
-
- # Add the programme to the playlist and play it
- self.totem.add_to_playlist_and_play (mrl, tree_store.get_value (tree_iter, 0), True)
-
- def populate_programme_list (self, feed, tree_store, category_iter):
- if self.debug:
- print "Populating programme listâ?¦"
-
- category_path = tree_store.get_path (category_iter)
- thread = PopulateProgrammesThread (self, feed, tree_store, category_path)
- thread.start ()
-
- def _populate_programme_list_cb (self, tree_store, category_path, values, remove_placeholder):
- # Callback from PopulateProgrammesThread to add stuff to the tree store
- if values == None:
- # Translators: the "programme feed" is the list of TV shows available to watch online
- self.totem.action_error (_(u'Error getting programme feed'), _(u'There was an error getting the list of programmes for this channel and category combination.'))
- return False
-
- category_iter = tree_store.get_iter (category_path)
- if category_iter != None:
- tree_store.append (category_iter, values)
-
- # Remove the placeholder row
- children = tree_store.iter_children (category_iter)
- if remove_placeholder and children != None:
- tree_store.remove (children)
-
- return False
+ object = gobject.property (type = gobject.GObject)
+
+ def __init__ (self):
+ self.debug = False
+ self.totem = None
+ self.programme_download_lock = threading.Lock ()
+
+ def do_activate (self):
+ self.totem = self.object
+ # Build the interface
+ builder = Totem.plugin_load_interface ("iplayer", "iplayer.ui", True,
+ self.totem.get_main_window (),
+ self)
+ container = builder.get_object ('iplayer_vbox')
+
+ self.tv_tree_store = builder.get_object ('iplayer_programme_store')
+ programme_list = builder.get_object ('iplayer_programme_list')
+ programme_list.connect ('row-expanded', self._row_expanded_cb)
+ programme_list.connect ('row-activated', self._row_activated_cb)
+
+ container.show_all ()
+
+ self.tv = iplayer2.feed ('tv')
+
+ # Add the interface to Totem's sidebar
+ self.totem.add_sidebar_page ("iplayer", _(u"BBC iPlayer"), container)
+
+ # Get the channel category listings
+ self.populate_channel_list (self.tv, self.tv_tree_store)
+
+ def do_deactivate (self):
+ self.totem.remove_sidebar_page ("iplayer")
+
+ def populate_channel_list (self, feed, tree_store):
+ if self.debug:
+ print "Populating channel listâ?¦"
+
+ # Add all the channels as top-level rows in the tree store
+ channels = feed.channels ()
+ for channel_id, title in channels.items ():
+ parent_iter = tree_store.append (None, (title, channel_id, None))
+
+ # Add the channels' categories in a thread, since they each require a
+ # network request
+ parent_path = tree_store.get_path (parent_iter)
+ thread = PopulateChannelsThread (self, parent_path, feed, tree_store)
+ thread.start ()
+
+ def _populate_channel_list_cb (self, tree_store, parent_path, values):
+ # Callback from PopulateChannelsThread to add stuff to the tree store
+ if values == None:
+ self.totem.action_error (_(u'Error listing channel categories'),
+ _(u'There was an unknown error getting '\
+ 'the list of television channels '\
+ 'available on BBC iPlayer.'))
+ 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
+ # then queue off the expander to load the programme listing for this
+ # category
+ tree_store.append (category_iter, [_(u'Loadingâ?¦'), None, None])
+
+ return False
+
+ def _row_expanded_cb (self, tree_view, row_iter, path):
+ tree_model = tree_view.get_model ()
+
+ if self.debug:
+ print "_row_expanded_cb called."
+
+ # We only care about the category level (level 1), and only when
+ # it has the "Loading..." placeholder child row
+ if (get_iter_level (tree_model, row_iter) != 1 or
+ tree_model.iter_n_children (row_iter) != 1):
+ return
+
+ # Populate it with programmes asynchronously
+ self.populate_programme_list (self.tv, tree_model, row_iter)
+
+ def _row_activated_cb (self, tree_view, path, view_column):
+ tree_store = tree_view.get_model ()
+ tree_iter = tree_store.get_iter (path)
+ if tree_iter == None:
+ return
+
+ mrl = tree_store.get_value (tree_iter, 2)
+
+ # Only allow programme rows to be activated, not channel or category
+ # rows
+ if mrl == None:
+ return
+
+ # Add the programme to the playlist and play it
+ title = tree_store.get_value (tree_iter, 0)
+ self.totem.add_to_playlist_and_play (mrl, title, True)
+
+ def populate_programme_list (self, feed, tree_store, category_iter):
+ if self.debug:
+ print "Populating programme listâ?¦"
+
+ category_path = tree_store.get_path (category_iter)
+ thread = PopulateProgrammesThread (self, feed, tree_store,
+ category_path)
+ thread.start ()
+
+ def _populate_programme_list_cb (self, tree_store, category_path, values,
+ remove_placeholder):
+ # Callback from PopulateProgrammesThread to add stuff to the tree store
+ if values == None:
+ # Translators: the "programme feed" is the list of TV shows
+ # available to watch online
+ self.totem.action_error (_(u'Error getting programme feed'),
+ _(u'There was an error getting the list '\
+ 'of programmes for this channel and '\
+ 'category combination.'))
+ return False
+
+ category_iter = tree_store.get_iter (category_path)
+ if category_iter != None:
+ tree_store.append (category_iter, values)
+
+ # Remove the placeholder row
+ children = tree_store.iter_children (category_iter)
+ if remove_placeholder and children != None:
+ tree_store.remove (children)
+
+ return False
def get_iter_level (tree_model, tree_iter):
- i = 0;
- while True:
- tree_iter = tree_model.iter_parent (tree_iter)
- if tree_iter == None:
- break
- i += 1
- return i
+ i = 0
+ while True:
+ tree_iter = tree_model.iter_parent (tree_iter)
+ if tree_iter == None:
+ break
+ i += 1
+ return i
def category_name_to_id (category_name):
- return category_name.lower ().replace (' ', '_').replace ('&', 'and')
+ return category_name.lower ().replace (' ', '_').replace ('&', 'and')
class PopulateChannelsThread (threading.Thread):
- # Class to populate the channel list from the Internet
- def __init__ (self, plugin, parent_path, feed, tree_model):
- self.plugin = plugin
- self.feed = feed
- self.tree_model = tree_model
- threading.Thread.__init__ (self)
-
- def run (self):
- shown_error = False
- tree_iter = self.tree_model.get_iter_first ()
- while (tree_iter != None):
- channel_id = self.tree_model.get_value (tree_iter, 1)
- parent_path = self.tree_model.get_path (tree_iter)
-
- try:
- # Add this channel's categories as sub-rows
- # We have to pass a path because the model could theoretically be modified
- # while the idle function is waiting in the queue, invalidating an iter
- for name, count in self.feed.get (channel_id).categories ():
- 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:
- # Only show the error once, rather than for each channel (it gets a bit grating)
- if not shown_error:
- gobject.idle_add (self.plugin._populate_channel_list_cb, self.tree_model, parent_path, None)
- shown_error = True
-
- tree_iter = self.tree_model.iter_next (tree_iter)
+ # Class to populate the channel list from the Internet
+ def __init__ (self, plugin, parent_path, feed, tree_model):
+ self.plugin = plugin
+ self.feed = feed
+ self.tree_model = tree_model
+ threading.Thread.__init__ (self)
+
+ def run (self):
+ shown_error = False
+ tree_iter = self.tree_model.get_iter_first ()
+ while (tree_iter != None):
+ channel_id = self.tree_model.get_value (tree_iter, 1)
+ parent_path = self.tree_model.get_path (tree_iter)
+
+ try:
+ # Add this channel's categories as sub-rows
+ # We have to pass a path because the model could theoretically
+ # be modified while the idle function is waiting in the queue,
+ # invalidating an iter
+ for name, count in self.feed.get (channel_id).categories ():
+ 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:
+ # Only show the error once, rather than for each channel
+ # (it gets a bit grating)
+ if not shown_error:
+ gobject.idle_add (self.plugin._populate_channel_list_cb,
+ self.tree_model, parent_path, None)
+ shown_error = True
+
+ tree_iter = self.tree_model.iter_next (tree_iter)
class PopulateProgrammesThread (threading.Thread):
- # Class to populate the programme list for a channel/category combination from the Internet
- def __init__ (self, plugin, feed, tree_model, category_path):
- self.plugin = plugin
- self.feed = feed
- self.tree_model = tree_model
- self.category_path = category_path
- threading.Thread.__init__ (self)
-
- def run (self):
- self.plugin.programme_download_lock.acquire ()
-
- category_iter = self.tree_model.get_iter (self.category_path)
- if category_iter == None:
- gobject.idle_add (self.plugin._populate_programme_list_cb, self.tree_model, self.category_path, None, False)
- self.plugin.programme_download_lock.release ()
- return
-
- category_id = self.tree_model.get_value (category_iter, 1)
- parent_iter = self.tree_model.iter_parent (category_iter)
- channel_id = self.tree_model.get_value (parent_iter, 1)
-
- # Retrieve the programmes and return them
- feed = self.feed.get (channel_id).get (category_id)
- if feed == None:
- gobject.idle_add (self.plugin._populate_programme_list_cb, self.tree_model, self.category_path, None, False)
- 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, False)
- self.plugin.programme_download_lock.release ()
- return
-
- # Add the programmes to the tree store
- remove_placeholder = True
- for programme in programmes:
- programme_item = programme.programme
-
- # Get the media, which gives the stream URI.
- # We go for mobile quality, since the higher-quality streams are RTMP-only
- # which isn't currently supported by GStreamer or xine
- # TODO: Use higher-quality streams once http://bugzilla.gnome.org/show_bug.cgi?id=566604 is fixed
- media = programme_item.get_media_for ('mobile')
- if media == None:
- # Not worth displaying an error in the interface for this
- print "Programme has no HTTP streams"
- continue
-
- 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)
- remove_placeholder = False
-
- self.plugin.programme_download_lock.release ()
+ # Class to populate the programme list for a channel/category combination
+ # from the Internet
+ def __init__ (self, plugin, feed, tree_model, category_path):
+ self.plugin = plugin
+ self.feed = feed
+ self.tree_model = tree_model
+ self.category_path = category_path
+ threading.Thread.__init__ (self)
+
+ def run (self):
+ self.plugin.programme_download_lock.acquire ()
+
+ category_iter = self.tree_model.get_iter (self.category_path)
+ if category_iter == None:
+ gobject.idle_add (self.plugin._populate_programme_list_cb,
+ self.tree_model, self.category_path, None, False)
+ self.plugin.programme_download_lock.release ()
+ return
+
+ category_id = self.tree_model.get_value (category_iter, 1)
+ parent_iter = self.tree_model.iter_parent (category_iter)
+ channel_id = self.tree_model.get_value (parent_iter, 1)
+
+ # Retrieve the programmes and return them
+ feed = self.feed.get (channel_id).get (category_id)
+ if feed == None:
+ gobject.idle_add (self.plugin._populate_programme_list_cb,
+ self.tree_model, self.category_path, None, False)
+ 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, False)
+ self.plugin.programme_download_lock.release ()
+ return
+
+ # Add the programmes to the tree store
+ remove_placeholder = True
+ for programme in programmes:
+ programme_item = programme.programme
+
+ # Get the media, which gives the stream URI.
+ # We go for mobile quality, since the higher-quality streams are
+ # RTMP-only which isn't currently supported by GStreamer or xine
+ # TODO: Use higher-quality streams once
+ # http://bugzilla.gnome.org/show_bug.cgi?id=566604 is fixed
+ media = programme_item.get_media_for ('mobile')
+ if media == None:
+ # Not worth displaying an error in the interface for this
+ print "Programme has no HTTP streams"
+ continue
+
+ 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)
+ remove_placeholder = False
+
+ self.plugin.programme_download_lock.release ()
diff --git a/src/plugins/iplayer/iplayer2.py b/src/plugins/iplayer/iplayer2.py
index 6c5f3b4..665b070 100644
--- a/src/plugins/iplayer/iplayer2.py
+++ b/src/plugins/iplayer/iplayer2.py
@@ -16,15 +16,15 @@ import feedparser
import listparser
from BeautifulSoup import BeautifulStoneSoup
-gettext.textdomain("totem")
+gettext.textdomain ("totem")
D_ = gettext.dgettext
_ = gettext.gettext
-IMG_DIR = os.path.join(os.getcwd(), 'resources', 'media')
+IMG_DIR = os.path.join (os.getcwd (), 'resources', 'media')
#try:
-# logging.basicConfig(
+# logging.basicConfig (
# filename='iplayer2.log',
# filemode='w',
# format='%(asctime)s %(levelname)4s %(message)s',
@@ -32,36 +32,36 @@ IMG_DIR = os.path.join(os.getcwd(), 'resources', 'media')
# )
#except IOError:
# #print "iplayer2 logging to stdout"
-# logging.basicConfig(
+# logging.basicConfig (
# stream=sys.stdout,
# level=logging.DEBUG,
# format='iplayer2.py: %(asctime)s %(levelname)4s %(message)s',
# )
# me want 2.5!!!
-def any(iterable):
- for element in iterable:
- if element:
- return True
- return False
+def any (iterable):
+ for element in iterable:
+ if element:
+ return True
+ return False
# http://colinm.org/blog/on-demand-loading-of-flickr-photo-metadata
# returns immediately for all previously-called functions
-def call_once(fn):
+def call_once (fn):
called_by = {}
- def result(self):
+ def result (self):
if self in called_by:
return
called_by[self] = True
- fn(self)
+ fn (self)
return result
# runs loader before decorated function
-def loaded_by(loader):
- def decorator(fn):
- def result(self, *args, **kwargs):
- loader(self)
- return fn(self, *args, **kwargs)
+def loaded_by (loader):
+ def decorator (fn):
+ def result (self, *args, **kwargs):
+ loader (self)
+ return fn (self, *args, **kwargs)
return result
return decorator
@@ -77,7 +77,7 @@ channels_tv_list = [
('bbc_hd', 'BBC HD'),
('bbc_alba', 'BBC Alba'),
]
-channels_tv = dict(channels_tv_list)
+channels_tv = dict (channels_tv_list)
channels_national_radio_list = [
('bbc_radio_one', 'Radio 1'),
('bbc_1xtra', '1 Xtra'),
@@ -139,56 +139,64 @@ channels_local_radio_list = [
('bbc_radio_guernsey', 'BBC Guernsey'),
('bbc_radio_jersey', 'BBC Jersey')
]
+
+LOGO_URI = 'http://www.bbc.co.uk/englandcms/'
channels_logos = {
- 'bbc_radio_cumbria': 'http://www.bbc.co.uk/englandcms/localradio/images/cumbria.gif',
- 'bbc_radio_newcastle': 'http://www.bbc.co.uk/englandcms/localradio/images/newcastle.gif',
- 'bbc_tees': 'http://www.bbc.co.uk/englandcms/localradio/images/tees.gif',
- 'bbc_radio_lancashire': 'http://www.bbc.co.uk/englandcms/images/rh_nav170_lancs.gif',
- 'bbc_radio_merseyside': 'http://www.bbc.co.uk/englandcms/localradio/images/merseyside.gif',
- 'bbc_radio_manchester': os.path.join(IMG_DIR, 'bbc_local_radio.png'),
- 'bbc_radio_leeds': 'http://www.bbc.co.uk/englandcms/images/rh_nav170_leeds.gif',
- 'bbc_radio_sheffield': 'http://www.bbc.co.uk/englandcms/images/rh_nav170_sheffield.gif',
- 'bbc_radio_york': 'http://www.bbc.co.uk/englandcms/localradio/images/york.gif',
- 'bbc_radio_humberside': 'http://www.bbc.co.uk/radio/images/home/r-home-nation-regions.gif',
- 'bbc_radio_lincolnshire': 'http://www.bbc.co.uk/englandcms/localradio/images/lincs.gif',
- 'bbc_radio_nottingham': os.path.join(IMG_DIR, 'bbc_local_radio.png'),
- 'bbc_radio_leicester': 'http://www.bbc.co.uk/englandcms/localradio/images/leicester.gif',
- 'bbc_radio_derby': 'http://www.bbc.co.uk/englandcms/derby/images/rh_nav170_derby.gif',
- 'bbc_radio_stoke': 'http://www.bbc.co.uk/englandcms/localradio/images/stoke.gif',
- 'bbc_radio_shropshire': 'http://www.bbc.co.uk/englandcms/localradio/images/shropshire.gif',
- 'bbc_wm': os.path.join(IMG_DIR, 'bbc_local_radio.png'),
- 'bbc_radio_coventry_warwickshire': 'http://www.bbc.co.uk/englandcms/localradio/images/cov_warks.gif',
- 'bbc_radio_hereford_worcester': 'http://www.bbc.co.uk/englandcms/localradio/images/hereford_worcester.gif',
- 'bbc_radio_northampton': 'http://www.bbc.co.uk/englandcms/localradio/images/northampton.gif',
- 'bbc_three_counties_radio': 'http://www.bbc.co.uk/englandcms/images/rh_nav170_3counties.gif',
- 'bbc_radio_cambridge': 'http://www.bbc.co.uk/englandcms/localradio/images/cambridgeshire.gif',
- 'bbc_radio_norfolk': 'http://www.bbc.co.uk/englandcms/localradio/images/norfolk.gif',
- 'bbc_radio_suffolk': 'http://www.bbc.co.uk/englandcms/localradio/images/suffolk.gif',
- 'bbc_radio_essex': 'http://www.bbc.co.uk/englandcms/images/rh_nav170_essex.gif',
- 'bbc_london': os.path.join(IMG_DIR, 'bbc_local_radio.png'),
- 'bbc_radio_kent': 'http://www.bbc.co.uk/radio/images/home/r-home-nation-regions.gif',
- 'bbc_southern_counties_radio': os.path.join(IMG_DIR, 'bbc_local_radio.png'),
- 'bbc_radio_oxford': 'http://www.bbc.co.uk/englandcms/images/rh_nav170_oxford.gif',
- 'bbc_radio_berkshire': 'http://www.bbc.co.uk/englandcms/images/rh_nav170_berks.gif',
- 'bbc_radio_solent': 'http://www.bbc.co.uk/englandcms/localradio/images/solent.gif',
- 'bbc_radio_gloucestershire': 'http://www.bbc.co.uk/englandcms/localradio/images/gloucestershire.gif',
- 'bbc_radio_swindon': os.path.join(IMG_DIR, 'bbc_local_radio.png'),
- 'bbc_radio_wiltshire': os.path.join(IMG_DIR, 'bbc_local_radio.png'),
- 'bbc_radio_bristol': 'http://www.bbc.co.uk/englandcms/localradio/images/bristol.gif',
- 'bbc_radio_somerset_sound': 'http://www.bbc.co.uk/englandcms/images/rh_nav170_somerset.gif',
- 'bbc_radio_devon': 'http://www.bbc.co.uk/englandcms/images/rh_nav170_devon.gif',
- 'bbc_radio_cornwall': 'http://www.bbc.co.uk/englandcms/localradio/images/cornwall.gif',
- 'bbc_radio_guernsey': 'http://www.bbc.co.uk/englandcms/localradio/images/guernsey.gif',
- 'bbc_radio_jersey': 'http://www.bbc.co.uk/englandcms/localradio/images/jersey.gif'
+ 'bbc_radio_cumbria': LOGO_URI + 'localradio/images/cumbria.gif',
+ 'bbc_radio_newcastle': LOGO_URI + 'localradio/images/newcastle.gif',
+ 'bbc_tees': LOGO_URI + 'localradio/images/tees.gif',
+ 'bbc_radio_lancashire': LOGO_URI + 'images/rh_nav170_lancs.gif',
+ 'bbc_radio_merseyside': LOGO_URI + 'localradio/images/merseyside.gif',
+ 'bbc_radio_manchester': os.path.join (IMG_DIR, 'bbc_local_radio.png'),
+ 'bbc_radio_leeds': LOGO_URI + 'images/rh_nav170_leeds.gif',
+ 'bbc_radio_sheffield': LOGO_URI + 'images/rh_nav170_sheffield.gif',
+ 'bbc_radio_york': LOGO_URI + 'localradio/images/york.gif',
+ 'bbc_radio_humberside': 'http://www.bbc.co.uk/radio/images/home/'\
+ 'r-home-nation-regions.gif',
+ 'bbc_radio_lincolnshire': LOGO_URI + 'localradio/images/lincs.gif',
+ 'bbc_radio_nottingham': os.path.join (IMG_DIR, 'bbc_local_radio.png'),
+ 'bbc_radio_leicester': LOGO_URI + 'localradio/images/leicester.gif',
+ 'bbc_radio_derby': LOGO_URI + 'derby/images/rh_nav170_derby.gif',
+ 'bbc_radio_stoke': LOGO_URI + 'localradio/images/stoke.gif',
+ 'bbc_radio_shropshire': LOGO_URI + 'localradio/images/shropshire.gif',
+ 'bbc_wm': os.path.join (IMG_DIR, 'bbc_local_radio.png'),
+ 'bbc_radio_coventry_warwickshire': LOGO_URI + 'localradio/images/'\
+ 'cov_warks.gif',
+ 'bbc_radio_hereford_worcester': LOGO_URI + 'localradio/images/'\
+ 'hereford_worcester.gif',
+ 'bbc_radio_northampton': LOGO_URI + 'localradio/images/northampton.gif',
+ 'bbc_three_counties_radio': LOGO_URI + 'images/rh_nav170_3counties.gif',
+ 'bbc_radio_cambridge': LOGO_URI + 'localradio/images/cambridgeshire.gif',
+ 'bbc_radio_norfolk': LOGO_URI + 'localradio/images/norfolk.gif',
+ 'bbc_radio_suffolk': LOGO_URI + 'localradio/images/suffolk.gif',
+ 'bbc_radio_essex': LOGO_URI + 'images/rh_nav170_essex.gif',
+ 'bbc_london': os.path.join (IMG_DIR, 'bbc_local_radio.png'),
+ 'bbc_radio_kent': 'http://www.bbc.co.uk/radio/images/home/'\
+ 'r-home-nation-regions.gif',
+ 'bbc_southern_counties_radio': os.path.join (IMG_DIR,
+ 'bbc_local_radio.png'),
+ 'bbc_radio_oxford': LOGO_URI + 'images/rh_nav170_oxford.gif',
+ 'bbc_radio_berkshire': LOGO_URI + 'images/rh_nav170_berks.gif',
+ 'bbc_radio_solent': LOGO_URI + 'localradio/images/solent.gif',
+ 'bbc_radio_gloucestershire': LOGO_URI + 'localradio/images/'\
+ 'gloucestershire.gif',
+ 'bbc_radio_swindon': os.path.join (IMG_DIR, 'bbc_local_radio.png'),
+ 'bbc_radio_wiltshire': os.path.join (IMG_DIR, 'bbc_local_radio.png'),
+ 'bbc_radio_bristol': LOGO_URI + 'localradio/images/bristol.gif',
+ 'bbc_radio_somerset_sound': LOGO_URI + 'images/rh_nav170_somerset.gif',
+ 'bbc_radio_devon': LOGO_URI + 'images/rh_nav170_devon.gif',
+ 'bbc_radio_cornwall': LOGO_URI + 'localradio/images/cornwall.gif',
+ 'bbc_radio_guernsey': LOGO_URI + 'localradio/images/guernsey.gif',
+ 'bbc_radio_jersey': LOGO_URI + 'localradio/images/jersey.gif'
}
-channels_national_radio = dict(channels_national_radio_list)
-channels_local_radio = dict(channels_local_radio_list)
+channels_national_radio = dict (channels_national_radio_list)
+channels_local_radio = dict (channels_local_radio_list)
channels_radio_list = channels_national_radio_list + channels_local_radio_list
-channels_radio = dict(channels_radio_list)
+channels_radio = dict (channels_radio_list)
-channels = dict(channels_tv_list + channels_radio_list)
+channels = dict (channels_tv_list + channels_radio_list)
categories_list = [
('childrens', 'Children\'s'),
('comedy', 'Comedy'),
@@ -205,158 +213,171 @@ categories_list = [
('scotland', 'Scotland'),
('wales', 'Wales')
]
-categories = dict(categories_list)
-
-live_radio_stations = {'Radio 1': 'http://www.bbc.co.uk/radio1/wm_asx/aod/radio1_hi.asx',
- '1 Xtra': 'http://www.bbc.co.uk/1xtra/realmedia/1xtra_hi.asx',
- 'Radio 2': 'http://www.bbc.co.uk/radio2/wm_asx/aod/radio2_hi.asx',
- 'BBC 3': 'http://www.bbc.co.uk/radio3/wm_asx/aod/radio3_hi.asx',
- 'BBC 4': 'http://www.bbc.co.uk/radio4/wm_asx/aod/radio4.asx',
- '5 Live': 'http://www.bbc.co.uk/fivelive/live/live.asx',
- '5 Live Sports Extra': 'http://www.bbc.co.uk/fivelive/live/live_sportsextra.asx',
- '6 Music': 'http://www.bbc.co.uk/6music/ram/6music_hi.asx',
- 'BBC 7': 'http://www.bbc.co.uk/bbc7/realplayer/bbc7_hi.asx',
- 'Asian Network': 'http://www.bbc.co.uk/asiannetwork/rams/asiannet_hi.asx',
- 'Radio Scotland': 'http://www.bbc.co.uk/scotland/radioscotland/media/radioscotland.ram',
- 'World Service': 'http://www.bbc.co.uk/worldservice/meta/tx/nb/live_eneuk_au_nb.asx',
- 'BBC nan Gaidheal': 'http://www.bbc.co.uk/scotland/alba/media/live/radio_ng.ram',
- 'BBC London': 'http://www.bbc.co.uk/england/realmedia/live/localradio/london.ram',
- 'BBC Berkshire': 'http://www.bbc.co.uk/england/realmedia/live/localradio/radioberkshire.ram',
- 'BBC Bristol': 'http://www.bbc.co.uk/england/realmedia/live/localradio/bristol.ram',
- 'BBC Cambridgeshire': 'http://www.bbc.co.uk/england/realmedia/live/localradio/cambridgeshire.ram',
- 'BBC Cornwall': 'http://www.bbc.co.uk/england/realmedia/live/localradio/cornwall.ram',
- 'BBC Cumbria': 'http://www.bbc.co.uk/england/realmedia/live/localradio/cumbria.ram',
- 'BBC Derby': 'http://www.bbc.co.uk/england/realmedia/live/localradio/derby.ram',
- 'BBC Devon': 'http://www.bbc.co.uk/england/realmedia/live/localradio/devon.ram',
- 'BBC Essex': 'http://www.bbc.co.uk/england/realmedia/live/localradio/essex.ram',
- 'BBC Gloucestershire': 'http://www.bbc.co.uk/england/realmedia/live/localradio/gloucestershire.ram',
- 'BBC Guernsey': 'http://www.bbc.co.uk/england/realmedia/live/localradio/guernsey.ram',
- 'BBC Hereford/Worcester': 'http://www.bbc.co.uk/england/realmedia/live/localradio/herefordandworcester.ram',
- 'BBC Humberside': 'http://www.bbc.co.uk/england/realmedia/live/localradio/humberside.ram',
- 'BBC Jersey': 'http://www.bbc.co.uk/england/realmedia/live/localradio/jersey.ram',
- 'BBC Kent': 'http://www.bbc.co.uk/england/realmedia/live/localradio/kent.ram',
- 'BBC Lancashire': 'http://www.bbc.co.uk/england/realmedia/live/localradio/lancashire.ram',
- 'BBC Leeds': 'http://www.bbc.co.uk/england/realmedia/live/localradio/leeds.ram',
- 'BBC Leicester': 'http://www.bbc.co.uk/england/realmedia/live/localradio/leicester.ram',
- 'BBC Lincolnshire': 'http://www.bbc.co.uk/england/realmedia/live/localradio/lincolnshire.ram',
- 'BBC Manchester': 'http://www.bbc.co.uk/england/realmedia/live/localradio/manchester.ram',
- 'BBC Merseyside': 'http://www.bbc.co.uk/england/realmedia/live/localradio/merseyside.ram',
- 'BBC Newcastle': 'http://www.bbc.co.uk/england/realmedia/live/localradio/newcastle.ram',
- 'BBC Norfolk': 'http://www.bbc.co.uk/england/realmedia/live/localradio/norfolk.ram',
- 'BBC Northampton': 'http://www.bbc.co.uk/england/realmedia/live/localradio/northampton.ram',
- 'BBC Nottingham': 'http://www.bbc.co.uk/england/realmedia/live/localradio/nottingham.ram',
- 'BBC Oxford': 'http://www.bbc.co.uk/england/realmedia/live/localradio/radiooxford.ram',
- 'BBC Sheffield': 'http://www.bbc.co.uk/england/realmedia/live/localradio/sheffield.ram',
- 'BBC Shropshire': 'http://www.bbc.co.uk/england/realmedia/live/localradio/shropshire.ram',
- 'BBC Solent': 'http://www.bbc.co.uk/england/realmedia/live/localradio/solent.ram',
- 'BBC Somerset Sound': 'http://www.bbc.co.uk/england/realmedia/live/localradio/somerset.ram',
- 'BBC Southern Counties Radio': 'http://www.bbc.co.uk/england/realmedia/live/localradio/southerncounties.ram',
- 'BBC Stoke': 'http://www.bbc.co.uk/england/realmedia/live/localradio/stoke.ram',
- 'BBC Suffolk': 'http://www.bbc.co.uk/england/realmedia/live/localradio/suffolk.ram',
- 'BBC Swindon': 'http://www.bbc.co.uk/england/realmedia/live/localradio/swindon.ram',
- 'BBC Three Counties Radio': 'http://www.bbc.co.uk/england/realmedia/live/localradio/threecounties.ram',
- 'BBC Wiltshire': 'http://www.bbc.co.uk/england/realmedia/live/localradio/wiltshire.ram',
- 'BBC York': 'http://www.bbc.co.uk/england/realmedia/live/localradio/york.ram',
- 'BBC WM': 'http://www.bbc.co.uk/england/realmedia/live/localradio/wm.ram',
- 'BBC Cymru': 'http://www.bbc.co.uk/cymru/live/rc-live.ram',
- 'Radio Foyle': 'http://www.bbc.co.uk/northernireland/realmedia/rf-live.ram',
- 'BBC Scotland': 'http://www.bbc.co.uk/scotland/radioscotland/media/radioscotland.ram',
- 'BBC nan Gaidheal': 'http://www.bbc.co.uk/scotland/alba/media/live/radio_ng.ram',
- 'BBC Ulster': 'http://www.bbc.co.uk/ni/realmedia/ru-live.ram',
- 'BBC Wales': 'http://www.bbc.co.uk/wales/live/rwg2.ram',
- 'BBC Tees': 'http://www.bbc.co.uk/england/realmedia/live/localradio/cleveland.ram',
- }
-live_webcams = {'Radio 1': 'http://www.bbc.co.uk/radio1/webcam/images/live/webcam.jpg',
- '1 Xtra': 'http://www.bbc.co.uk/1xtra/webcam/live/1xtraa.jpg',
- 'Radio 2': 'http://www.bbc.co.uk/radio2/webcam/live/radio2.jpg',
- '5 Live': 'http://www.bbc.co.uk/fivelive/inside/webcam/5Lwebcam1.jpg',
- '6 Music': 'http://www.bbc.co.uk/6music/webcam/live/6music.jpg',
- 'Asian Network': 'http://www.bbc.co.uk/asiannetwork/webcams/birmingham.jpg'}
+categories = dict (categories_list)
+
+ENGLAND_RADIO_URI = 'http://www.bbc.co.uk/england/'
+live_radio_stations = {
+ 'Radio 1': 'http://www.bbc.co.uk/radio1/wm_asx/aod/radio1_hi.asx',
+ '1 Xtra': 'http://www.bbc.co.uk/1xtra/realmedia/1xtra_hi.asx',
+ 'Radio 2': 'http://www.bbc.co.uk/radio2/wm_asx/aod/radio2_hi.asx',
+ 'BBC 3': 'http://www.bbc.co.uk/radio3/wm_asx/aod/radio3_hi.asx',
+ 'BBC 4': 'http://www.bbc.co.uk/radio4/wm_asx/aod/radio4.asx',
+ '5 Live': 'http://www.bbc.co.uk/fivelive/live/live.asx',
+ '5 Live Sports Extra': 'http://www.bbc.co.uk/fivelive/live/'\
+ 'live_sportsextra.asx',
+ '6 Music': 'http://www.bbc.co.uk/6music/ram/6music_hi.asx',
+ 'BBC 7': 'http://www.bbc.co.uk/bbc7/realplayer/bbc7_hi.asx',
+ 'Asian Network': 'http://www.bbc.co.uk/asiannetwork/rams/asiannet_hi.asx',
+ 'Radio Scotland': 'http://www.bbc.co.uk/scotland/radioscotland/media/'\
+ 'radioscotland.ram',
+ 'World Service': 'http://www.bbc.co.uk/worldservice/meta/tx/nb/'\
+ 'live_eneuk_au_nb.asx',
+ 'BBC nan Gaidheal': 'http://www.bbc.co.uk/scotland/alba/media/live/'\
+ 'radio_ng.ram',
+ 'BBC London': ENGLAND_RADIO_URI + 'london.ram',
+ 'BBC Berkshire': ENGLAND_RADIO_URI + 'radioberkshire.ram',
+ 'BBC Bristol': ENGLAND_RADIO_URI + 'bristol.ram',
+ 'BBC Cambridgeshire': ENGLAND_RADIO_URI + 'cambridgeshire.ram',
+ 'BBC Cornwall': ENGLAND_RADIO_URI + 'cornwall.ram',
+ 'BBC Cumbria': ENGLAND_RADIO_URI + 'cumbria.ram',
+ 'BBC Derby': ENGLAND_RADIO_URI + 'derby.ram',
+ 'BBC Devon': ENGLAND_RADIO_URI + 'devon.ram',
+ 'BBC Essex': ENGLAND_RADIO_URI + 'essex.ram',
+ 'BBC Gloucestershire': ENGLAND_RADIO_URI + 'gloucestershire.ram',
+ 'BBC Guernsey': ENGLAND_RADIO_URI + 'guernsey.ram',
+ 'BBC Hereford/Worcester': ENGLAND_RADIO_URI + 'herefordandworcester.ram',
+ 'BBC Humberside': ENGLAND_RADIO_URI + 'humberside.ram',
+ 'BBC Jersey': ENGLAND_RADIO_URI + 'jersey.ram',
+ 'BBC Kent': ENGLAND_RADIO_URI + 'kent.ram',
+ 'BBC Lancashire': ENGLAND_RADIO_URI + 'lancashire.ram',
+ 'BBC Leeds': ENGLAND_RADIO_URI + 'leeds.ram',
+ 'BBC Leicester': ENGLAND_RADIO_URI + 'leicester.ram',
+ 'BBC Lincolnshire': ENGLAND_RADIO_URI + 'lincolnshire.ram',
+ 'BBC Manchester': ENGLAND_RADIO_URI + 'manchester.ram',
+ 'BBC Merseyside': ENGLAND_RADIO_URI + 'merseyside.ram',
+ 'BBC Newcastle': ENGLAND_RADIO_URI + 'newcastle.ram',
+ 'BBC Norfolk': ENGLAND_RADIO_URI + 'norfolk.ram',
+ 'BBC Northampton': ENGLAND_RADIO_URI + 'northampton.ram',
+ 'BBC Nottingham': ENGLAND_RADIO_URI + 'nottingham.ram',
+ 'BBC Oxford': ENGLAND_RADIO_URI + 'radiooxford.ram',
+ 'BBC Sheffield': ENGLAND_RADIO_URI + 'sheffield.ram',
+ 'BBC Shropshire': ENGLAND_RADIO_URI + 'shropshire.ram',
+ 'BBC Solent': ENGLAND_RADIO_URI + 'solent.ram',
+ 'BBC Somerset Sound': ENGLAND_RADIO_URI + 'somerset.ram',
+ 'BBC Southern Counties Radio': ENGLAND_RADIO_URI + 'southerncounties.ram',
+ 'BBC Stoke': ENGLAND_RADIO_URI + 'stoke.ram',
+ 'BBC Suffolk': ENGLAND_RADIO_URI + 'suffolk.ram',
+ 'BBC Swindon': ENGLAND_RADIO_URI + 'swindon.ram',
+ 'BBC Three Counties Radio': ENGLAND_RADIO_URI + 'threecounties.ram',
+ 'BBC Wiltshire': ENGLAND_RADIO_URI + 'wiltshire.ram',
+ 'BBC York': ENGLAND_RADIO_URI + 'york.ram',
+ 'BBC WM': ENGLAND_RADIO_URI + 'wm.ram',
+ 'BBC Cymru': 'http://www.bbc.co.uk/cymru/live/rc-live.ram',
+ 'Radio Foyle': 'http://www.bbc.co.uk/northernireland/realmedia/rf-live.ram',
+ 'BBC Scotland': 'http://www.bbc.co.uk/scotland/radioscotland/media/'\
+ 'radioscotland.ram',
+ 'BBC nan Gaidheal': 'http://www.bbc.co.uk/scotland/alba/media/live/'\
+ 'radio_ng.ram',
+ 'BBC Ulster': 'http://www.bbc.co.uk/ni/realmedia/ru-live.ram',
+ 'BBC Wales': 'http://www.bbc.co.uk/wales/live/rwg2.ram',
+ 'BBC Tees': ENGLAND_RADIO_URI + 'cleveland.ram',
+}
+
+live_webcams = {
+ 'Radio 1': 'http://www.bbc.co.uk/radio1/webcam/images/live/webcam.jpg',
+ '1 Xtra': 'http://www.bbc.co.uk/1xtra/webcam/live/1xtraa.jpg',
+ 'Radio 2': 'http://www.bbc.co.uk/radio2/webcam/live/radio2.jpg',
+ '5 Live': 'http://www.bbc.co.uk/fivelive/inside/webcam/5Lwebcam1.jpg',
+ '6 Music': 'http://www.bbc.co.uk/6music/webcam/live/6music.jpg',
+ 'Asian Network': 'http://www.bbc.co.uk/asiannetwork/webcams/birmingham.jpg'
+}
rss_cache = {}
self_closing_tags = ['alternate', 'mediator']
-http = httplib2.Http()
+http = httplib2.Http ()
-re_selfclose = re.compile('<([a-zA-Z0-9]+)( ?.*)/>', re.M | re.S)
+re_selfclose = re.compile ('< ([a-zA-Z0-9]+) ( ?.*)/>', re.M | re.S)
-def fix_selfclosing(xml):
- return re_selfclose.sub('<\\1\\2></\\1>', xml)
+def fix_selfclosing (xml):
+ return re_selfclose.sub ('<\\1\\2></\\1>', xml)
-def set_http_cache_dir(d):
- fc = httplib2.FileCache(d)
+def set_http_cache_dir (d):
+ fc = httplib2.FileCache (d)
http.cache = fc
-def set_http_cache(c):
+def set_http_cache (c):
http.cache = c
-class NoItemsError(Exception):
- def __init__(self, reason=None):
+class NoItemsError (Exception):
+ def __init__ (self, reason=None):
self.reason = reason
- def __str__(self):
+ def __str__ (self):
reason = self.reason or _(u'<no reason given>')
return _(u'Programme unavailable ("%s")') % (reason)
-class memoize(object):
- def __init__(self, func):
+class memoize (object):
+ def __init__ (self, func):
self.func = func
self._cache = {}
- def __call__(self, *args, **kwds):
+ def __call__ (self, *args, **kwds):
key = args
if kwds:
- items = kwds.items()
- items.sort()
- key = key + tuple(items)
+ items = kwds.items ()
+ items.sort ()
+ key = key + tuple (items)
if key in self._cache:
return self._cache[key]
- self._cache[key] = result = self.func(*args, **kwds)
+ self._cache[key] = result = self.func (*args, **kwds)
return result
-def httpretrieve(url, filename):
- _, data = http.request(url, 'GET')
- f = open(filename, 'wb')
- f.write(data)
- f.close()
+def httpretrieve (url, filename):
+ _, data = http.request (url, 'GET')
+ f = open (filename, 'wb')
+ f.write (data)
+ f.close ()
-def httpget(url):
- resp, data = http.request(url, 'GET')
+def httpget (url):
+ resp, data = http.request (url, 'GET')
return data
-def parse_entry_id(entry_id):
+def parse_entry_id (entry_id):
# tag:bbc.co.uk,2008:PIPS:b00808sc
- r = re.compile('PIPS:([0-9a-z]{8})')
- matches = r.findall(entry_id)
- if not matches: return None
+ r = re.compile ('PIPS: ([0-9a-z]{8})')
+ matches = r.findall (entry_id)
+ if not matches:
+ return None
return matches[0]
-class media(object):
- def __init__(self, item, media_node):
+class media (object):
+ def __init__ (self, item, media_node):
self.item = item
self.href = None
self.kind = None
self.method = None
self.width, self.height = None, None
- self.read_media_node(media_node)
+ self.read_media_node (media_node)
@property
- def url(self):
+ def url (self):
if self.connection_method == 'resolve':
- #logging.info("Resolving URL %s", self.connection_href)
- page = urllib2.urlopen(self.connection_href)
- page.close()
- url = page.geturl()
- #logging.info("URL resolved to %s", url)
- return page.geturl()
+ #logging.info ("Resolving URL %s", self.connection_href)
+ page = urllib2.urlopen (self.connection_href)
+ page.close ()
+ url = page.geturl ()
+ #logging.info ("URL resolved to %s", url)
+ return page.geturl ()
else:
return self.connection_href
@property
- def application(self):
+ def application (self):
"""
The type of stream represented as a string.
- i.e. 'captions', 'flashhigh', 'flashmed', 'flashwii', 'mobile', 'mp3' or 'real'
+ i.e. 'captions', 'flashhigh', 'flashmed', 'flashwii', 'mobile', 'mp3'
+ or 'real'
"""
tep = {}
tep['captions', 'application/ttaf+xml', None, 'http'] = 'captions'
@@ -367,28 +388,28 @@ class media(object):
tep['audio', 'audio/mpeg', 'mp3', 'rtmp'] = 'mp3'
tep['audio', 'audio/real', 'real', 'http'] = 'real'
me = (self.kind, self.mimetype, self.encoding, self.connection_protocol)
- return tep.get(me, None)
+ return tep.get (me, None)
- def read_media_node(self, media, resolve=False):
+ def read_media_node (self, media, resolve=False):
"""
Reads media info from a media XML node
media: media node from BeautifulStoneSoup
"""
- self.kind = media.get('kind')
- self.mimetype = media.get('type')
- self.encoding = media.get('encoding')
- self.width, self.height = media.get('width'), media.get('height')
- self.live = media.get('live') == 'true'
-
- conn = media.find('connection')
- self.connection_kind = conn.get('kind')
- self.connection_live = conn.get('live') == 'true'
+ self.kind = media.get ('kind')
+ self.mimetype = media.get ('type')
+ self.encoding = media.get ('encoding')
+ self.width, self.height = media.get ('width'), media.get ('height')
+ self.live = media.get ('live') == 'true'
+
+ conn = media.find ('connection')
+ self.connection_kind = conn.get ('kind')
+ self.connection_live = conn.get ('live') == 'true'
self.connection_protocol = None
self.connection_href = None
self.connection_method = None
if self.connection_kind in ['http', 'sis']: # http
- self.connection_href = conn.get('href')
+ self.connection_href = conn.get ('href')
self.connection_protocol = 'http'
if self.mimetype == 'video/mp4' and self.encoding == 'h264':
# iPhone, don't redirect or it goes to license failure page
@@ -399,35 +420,41 @@ class media(object):
self.connection_method = 'resolve'
elif self.connection_kind in ['level3', 'akamai']: #rtmp
self.connection_protocol = 'rtmp'
- server = conn.get('server')
- identifier = conn.get('identifier')
+ server = conn.get ('server')
+ identifier = conn.get ('identifier')
if not self.connection_live:
- #logging.error("No support for live streams!")
- auth = conn.get('authstring')
- params = dict(ip=server, server=server, auth=auth, identifier=identifier)
- self.connection_href = "rtmp://%(ip)s:1935/ondemand?_fcs_vhost=%(server)s&auth=%(auth)s&aifp=v001&slist=%(identifier)s" % params
+ #logging.error ("No support for live streams!")
+ auth = conn.get ('authstring')
+ params = dict (ip=server, server=server, auth=auth,
+ identifier=identifier)
+ self.connection_href = "rtmp://%(ip)s:1935/ondemand"\
+ "?_fcs_vhost=%(server)s&auth=%(auth)s"\
+ "&aifp=v001"\
+ "&slist=%(identifier)s" % params
#else:
- # logging.error("connectionkind %s unknown", self.connection_kind)
+ # logging.error ("connectionkind %s unknown", self.connection_kind)
#if self.connection_protocol:
- # logging.info("conn protocol: %s - conn kind: %s - media type: %s - media encoding: %s" %
- # (self.connection_protocol, self.connection_kind, self.mimetype, self.encoding))
- # logging.info("conn href: %s", self.connection_href)
+ # logging.info ("conn protocol: %s - conn kind: %s - media type: "\
+ # "%s - media encoding: %s" %
+ # (self.connection_protocol, self.connection_kind,
+ # self.mimetype, self.encoding))
+ # logging.info ("conn href: %s", self.connection_href)
@property
- def programme(self):
+ def programme (self):
return self.item.programme
-class item(object):
+class item (object):
"""
- Represents an iPlayer programme item. Most programmes consist of 2 such items,
- (1) the ident, and (2) the actual programme. The item specifies the properties
- of the media available, such as whether it's a radio/TV programme, if it's live,
- signed, etc.
+ Represents an iPlayer programme item. Most programmes consist of 2 such
+ items, (1) the ident, and (2) the actual programme. The item specifies the
+ properties of the media available, such as whether it's a radio/TV
+ programme, if it's live, signed, etc.
"""
- def __init__(self, programme, item_node):
+ def __init__ (self, programme, item_node):
"""
programme: a programme object that represents the 'parent' of this item.
item_node: an XML <item> node representing this item.
@@ -440,77 +467,80 @@ class item(object):
self.alternate = None
self.duration = ''
self.medias = None
- self.read_item_node(item_node)
+ self.read_item_node (item_node)
- def read_item_node(self, node):
+ def read_item_node (self, node):
"""
Reads the specified XML <item> node and sets this instance's
properties.
"""
- self.kind = node.get('kind')
- self.identifier = node.get('identifier')
- #logging.info('Found item: %s, %s', self.kind, self.identifier)
+ self.kind = node.get ('kind')
+ self.identifier = node.get ('identifier')
+ #logging.info ('Found item: %s, %s', self.kind, self.identifier)
if self.kind in ['programme', 'radioProgramme']:
- self.live = node.get('live') == 'true'
- #self.title = node.get('title')
- self.group = node.get('group')
- self.duration = node.get('duration')
+ self.live = node.get ('live') == 'true'
+ #self.title = node.get ('title')
+ self.group = node.get ('group')
+ self.duration = node.get ('duration')
#self.broadcast = node.broadcast
- self.service = node.service and node.service.get('id')
- self.masterbrand = node.masterbrand and node.masterbrand.get('id')
- self.alternate = node.alternate and node.alternate.get('id')
+ self.service = node.service and node.service.get ('id')
+ self.masterbrand = node.masterbrand and node.masterbrand.get ('id')
+ self.alternate = node.alternate and node.alternate.get ('id')
self.guidance = node.guidance
@property
- def is_radio(self):
+ def is_radio (self):
""" True if this stream is a radio programme. """
return self.kind == 'radioProgramme'
@property
- def is_tv(self):
+ def is_tv (self):
""" True if this stream is a TV programme. """
return self.kind == 'programme'
@property
- def is_ident(self):
+ def is_ident (self):
""" True if this stream is an ident. """
return self.kind == 'ident'
@property
- def is_programme(self):
+ def is_programme (self):
""" True if this stream is a programme (TV or Radio). """
return self.is_radio or self.is_tv
@property
- def is_live(self):
+ def is_live (self):
""" True if this stream is being broadcast live. """
return self.live
@property
- def is_signed(self):
+ def is_signed (self):
""" True if this stream is 'signed' for the hard-of-hearing. """
return self.alternate == 'signed'
@property
- def mediaselector_url(self):
- return "http://www.bbc.co.uk/mediaselector/4/mtis/stream/%s" % self.identifier
+ def mediaselector_url (self):
+ url = "http://www.bbc.co.uk/mediaselector/4/mtis/stream/%s"
+ return url % self.identifier
@property
- def media(self):
+ def media (self):
"""
Returns a list of all the media available for this item.
"""
- if self.medias: return self.medias
+ if self.medias:
+ return self.medias
url = self.mediaselector_url
- #logging.info("Stream XML URL: %s", str(url))
- _, xml = http.request(url)
- soup = BeautifulStoneSoup(xml, convertEntities = BeautifulStoneSoup.XML_ENTITIES)
- medias = [media(self, m) for m in soup('media')]
- #logging.info('Found media: %s', pformat(medias, indent=8))
+ #logging.info ("Stream XML URL: %s", str (url))
+ _, xml = http.request (url)
+ entities = BeautifulStoneSoup.XML_ENTITIES
+ soup = BeautifulStoneSoup (xml, convertEntities = entities)
+ medias = [media (self, m) for m in soup ('media')]
+ #logging.info ('Found media: %s', pformat (medias, indent=8))
self.medias = medias
return medias
- def get_media_for(self, application):
+ def get_media_for (self, application):
"""
Returns a media object for the given application type.
"""
@@ -519,155 +549,160 @@ class item(object):
return None
return medias[0]
- def get_medias_for(self, applications):
+ def get_medias_for (self, applications):
"""
Returns a dictionary of media objects for the given application types.
"""
medias = [m for m in self.media if m.application in applications]
- d = {}.fromkeys(applications)
+ d = {}.fromkeys (applications)
for m in medias:
d[m.application] = m
return d
-class programme(object):
+class programme (object):
"""
- Represents an individual iPlayer programme, as identified by an 8-letter PID,
- and contains the programme title, subtitle, broadcast time and list of playlist
- items (e.g. ident and then the actual programme.)
+ Represents an individual iPlayer programme, as identified by an 8-letter
+ PID, and contains the programme title, subtitle, broadcast time and list of
+ playlist items (e.g. ident and then the actual programme.)
"""
- def __init__(self, pid):
+ def __init__ (self, pid):
self.pid = pid
self.meta = {}
self._items = []
self._related = []
@call_once
- def read_playlist(self):
- #logging.info('Read playlist for %s...', self.pid)
- self.parse_playlist(self.playlist)
+ def read_playlist (self):
+ #logging.info ('Read playlist for %s...', self.pid)
+ self.parse_playlist (self.playlist)
- def get_playlist_xml(self):
+ def get_playlist_xml (self):
""" Downloads and returns the XML for a PID from the iPlayer site. """
try:
url = self.playlist_url
- #logging.info("Getting XML playlist at URL: %s", url)
- r, xml = http.request(url, 'GET')
+ #logging.info ("Getting XML playlist at URL: %s", url)
+ r, xml = http.request (url, 'GET')
return xml
except SocketTimeoutError:
- #logging.error("Timed out trying to download programme XML")
+ #logging.error ("Timed out trying to download programme XML")
raise
- def parse_playlist(self, xml):
- #logging.info('Parsing playlist XML... %s', xml)
- #xml.replace('<summary/>', '<summary></summary>')
- #xml = fix_selfclosing(xml)
+ def parse_playlist (self, xml):
+ #logging.info ('Parsing playlist XML... %s', xml)
+ #xml.replace ('<summary/>', '<summary></summary>')
+ #xml = fix_selfclosing (xml)
- soup = BeautifulStoneSoup(xml, selfClosingTags=self_closing_tags, convertEntities = BeautifulStoneSoup.XML_ENTITIES)
+ entities = BeautifulStoneSoup.XML_ENTITIES
+ soup = BeautifulStoneSoup (xml, selfClosingTags=self_closing_tags,
+ convertEntities = entities)
self.meta = {}
self._items = []
self._related = []
- #logging.info(' Found programme: %s', soup.playlist.title.string)
+ #logging.info (' Found programme: %s', soup.playlist.title.string)
self.meta['title'] = soup.playlist.title.string.encode ('utf-8')
self.meta['summary'] = soup.playlist.summary.string.encode ('utf-8')
self.meta['updated'] = soup.playlist.updated.string
if soup.playlist.noitems:
- #logging.info('No playlist items: %s', soup.playlist.noitems.get('reason'))
- self.meta['reason'] = soup.playlist.noitems.get('reason')
+ #logging.info ('No playlist items: %s',
+ # soup.playlist.noitems.get ('reason'))
+ self.meta['reason'] = soup.playlist.noitems.get ('reason')
- self._items = [item(self, i) for i in soup('item')]
+ self._items = [item (self, i) for i in soup ('item')]
#for i in self._items:
# print i, i.alternate , " ",
#print
- rId = re.compile('concept_pid:([a-z0-9]{8})')
- for link in soup('relatedlink'):
+ rId = re.compile ('concept_pid: ([a-z0-9]{8})')
+ for link in soup ('relatedlink'):
i = {}
i['title'] = link.title.string
#i['summary'] = item.summary # FIXME looks like a bug in BSS
- i['pid'] = (rId.findall(link.id.string) or [None])[0]
- i['programme'] = programme(i['pid'])
- self._related.append(i)
+ i['pid'] = (rId.findall (link.id.string) or [None])[0]
+ i['programme'] = programme (i['pid'])
+ self._related.append (i)
- def get_thumbnail(self, size='large', tvradio='tv'):
+ def get_thumbnail (self, size='large', tvradio='tv'):
"""
Returns the URL of a thumbnail.
size: '640x360'/'biggest'/'largest' or '512x288'/'big'/'large' or None
"""
+ url_format = "http://www.bbc.co.uk/iplayer/images/episode/%s_%d_%d.jpg"
+
if size in ['640x360', '640x', 'x360', 'biggest', 'largest']:
- return "http://www.bbc.co.uk/iplayer/images/episode/%s_640_360.jpg" % (self.pid)
+ return url_format % (self.pid, 640, 360)
elif size in ['512x288', '512x', 'x288', 'big', 'large']:
- return "http://www.bbc.co.uk/iplayer/images/episode/%s_512_288.jpg" % (self.pid)
+ return url_format % (self.pid, 512, 288)
elif size in ['178x100', '178x', 'x100', 'small']:
- return "http://www.bbc.co.uk/iplayer/images/episode/%s_178_100.jpg" % (self.pid)
+ return url_format % (self.pid, 178, 100)
elif size in ['150x84', '150x', 'x84', 'smallest']:
- return "http://www.bbc.co.uk/iplayer/images/episode/%s_150_84.jpg" % (self.pid)
+ return url_format % (self.pid, 150, 84)
else:
- return os.path.join(IMG_DIR, '%s.png' % tvradio)
+ return os.path.join (IMG_DIR, '%s.png' % tvradio)
- def get_url(self):
+ def get_url (self):
"""
Returns the programmes episode page.
"""
return "http://www.bbc.co.uk/iplayer/episode/%s" % (self.pid)
@property
- def playlist_url(self):
+ def playlist_url (self):
return "http://www.bbc.co.uk/iplayer/playlist/%s" % self.pid
@property
- def playlist(self):
- return self.get_playlist_xml()
+ def playlist (self):
+ return self.get_playlist_xml ()
- def get_updated(self):
+ def get_updated (self):
return self.meta['updated']
- @loaded_by(read_playlist)
- def get_title(self):
+ @loaded_by (read_playlist)
+ def get_title (self):
return self.meta['title']
- @loaded_by(read_playlist)
- def get_summary(self):
+ @loaded_by (read_playlist)
+ def get_summary (self):
return self.meta['summary']
- @loaded_by(read_playlist)
- def get_related(self):
+ @loaded_by (read_playlist)
+ def get_related (self):
return self._related
- @loaded_by(read_playlist)
- def get_items(self):
+ @loaded_by (read_playlist)
+ def get_items (self):
if not self._items:
- raise NoItemsError(self.meta['reason'])
+ raise NoItemsError (self.meta['reason'])
return self._items
@property
- def programme(self):
+ def programme (self):
for i in self.items:
if i.is_programme:
return i
return None
- title = property(get_title)
- summary = property(get_summary)
- updated = property(get_updated)
- thumbnail = property(get_thumbnail)
- related = property(get_related)
- items = property(get_items)
+ title = property (get_title)
+ summary = property (get_summary)
+ updated = property (get_updated)
+ thumbnail = property (get_thumbnail)
+ related = property (get_related)
+ items = property (get_items)
-#programme = memoize(programme)
+#programme = memoize (programme)
-class programme_simple(object):
+class programme_simple (object):
"""
- Represents an individual iPlayer programme, as identified by an 8-letter PID,
- and contains the programme pid, title, subtitle etc
+ Represents an individual iPlayer programme, as identified by an 8-letter
+ PID, and contains the programme pid, title, subtitle etc
"""
- def __init__(self, pid, entry):
+ def __init__ (self, pid, entry):
self.pid = pid
self.meta = {}
self.meta['title'] = entry.title
@@ -676,93 +711,96 @@ class programme_simple(object):
self.categories = []
for c in entry.categories:
if c != 'TV':
- self.categories.append(c.rstrip())
+ self.categories.append (c.rstrip ())
self._items = []
self._related = []
@call_once
- def read_playlist(self):
+ def read_playlist (self):
pass
- def get_playlist_xml(self):
+ def get_playlist_xml (self):
pass
- def parse_playlist(self, xml):
+ def parse_playlist (self, xml):
pass
- def get_thumbnail(self, size='large', tvradio='tv'):
+ def get_thumbnail (self, size='large', tvradio='tv'):
"""
Returns the URL of a thumbnail.
size: '640x360'/'biggest'/'largest' or '512x288'/'big'/'large' or None
"""
+ url_format = "http://www.bbc.co.uk/iplayer/images/episode/%s_%d_%d.jpg"
if size in ['640x360', '640x', 'x360', 'biggest', 'largest']:
- return "http://www.bbc.co.uk/iplayer/images/episode/%s_640_360.jpg" % (self.pid)
+ return url_format % (self.pid, 640, 360)
elif size in ['512x288', '512x', 'x288', 'big', 'large']:
- return "http://www.bbc.co.uk/iplayer/images/episode/%s_512_288.jpg" % (self.pid)
+ return url_format % (self.pid, 512, 288)
elif size in ['178x100', '178x', 'x100', 'small']:
- return "http://www.bbc.co.uk/iplayer/images/episode/%s_178_100.jpg" % (self.pid)
+ return url_format % (self.pid, 178, 100)
elif size in ['150x84', '150x', 'x84', 'smallest']:
- return "http://www.bbc.co.uk/iplayer/images/episode/%s_150_84.jpg" % (self.pid)
+ return url_format % (self.pid, 150, 84)
else:
- return os.path.join(IMG_DIR, '%s.png' % tvradio)
+ return os.path.join (IMG_DIR, '%s.png' % tvradio)
- def get_url(self):
+ def get_url (self):
"""
Returns the programmes episode page.
"""
return "http://www.bbc.co.uk/iplayer/episode/%s" % (self.pid)
@property
- def playlist_url(self):
+ def playlist_url (self):
return "http://www.bbc.co.uk/iplayer/playlist/%s" % self.pid
@property
- def playlist(self):
- return self.get_playlist_xml()
+ def playlist (self):
+ return self.get_playlist_xml ()
- def get_updated(self):
+ def get_updated (self):
return self.meta['updated']
- @loaded_by(read_playlist)
- def get_title(self):
+ @loaded_by (read_playlist)
+ def get_title (self):
return self.meta['title']
- @loaded_by(read_playlist)
- def get_summary(self):
+ @loaded_by (read_playlist)
+ def get_summary (self):
return self.meta['summary']
- @loaded_by(read_playlist)
- def get_related(self):
+ @loaded_by (read_playlist)
+ def get_related (self):
return self._related
- @loaded_by(read_playlist)
- def get_items(self):
+ @loaded_by (read_playlist)
+ def get_items (self):
if not self._items:
- raise NoItemsError(self.meta['reason'])
+ raise NoItemsError (self.meta['reason'])
return self._items
@property
- def programme(self):
+ def programme (self):
for i in self.items:
if i.is_programme:
return i
return None
- title = property(get_title)
- summary = property(get_summary)
- updated = property(get_updated)
- thumbnail = property(get_thumbnail)
- related = property(get_related)
- items = property(get_items)
+ title = property (get_title)
+ summary = property (get_summary)
+ updated = property (get_updated)
+ thumbnail = property (get_thumbnail)
+ related = property (get_related)
+ items = property (get_items)
-class feed(object):
- def __init__(self, tvradio=None, channel=None, category=None, subcategory=None, atoz=None, searchterm=None):
+class feed (object):
+ def __init__ (self, tvradio = None, channel = None, category = None,
+ subcategory = None, atoz = None, searchterm = None):
"""
Creates a feed for the specified channel/category/whatever.
- tvradio: type of channel - 'tv' or 'radio'. If a known channel is specified, use 'auto'.
+ tvradio: type of channel - 'tv' or 'radio'. If a known channel is
+ specified, use 'auto'.
channel: name of channel, e.g. 'bbc_one'
category: category name, e.g. 'drama'
subcategory: subcategory name, e.g. 'period_drama'
@@ -770,13 +808,15 @@ class feed(object):
"""
if tvradio == 'auto':
if not channel and not searchterm:
- raise Exception, "Must specify channel or searchterm when using 'auto'"
+ raise Exception, "Must specify channel or searchterm when "\
+ "using 'auto'"
elif channel in channels_tv:
self.tvradio = 'tv'
elif channel in channels_radio:
self.tvradio = 'radio'
else:
- raise Exception, "TV channel '%s' not recognised." % self.channel
+ raise Exception, "TV channel '%s' not "\
+ "recognised." % self.channel
elif tvradio in ['tv', 'radio']:
self.tvradio = tvradio
@@ -788,12 +828,15 @@ class feed(object):
self.atoz = atoz
self.searchterm = searchterm
- def create_url(self, listing):
+ def create_url (self, listing):
"""
<channel>/['list'|'popular'|'highlights']
- 'categories'/<category>(/<subcategory>)(/['tv'/'radio'])/['list'|'popular'|'highlights']
+ 'categories'/<category> (/<subcategory>) \
+ (/['tv'/'radio'])/['list'|'popular'|'highlights']
"""
- assert listing in ['list', 'popular', 'highlights'], "Unknown listing type"
+ assert listing in ['list', 'popular', 'highlights'], "Unknown "\
+ "listing type"
+
if self.searchterm:
path = ['search']
if self.tvradio:
@@ -819,205 +862,219 @@ class feed(object):
assert listing != 'list', "Can't list at tv/radio level'"
path = [listing, self.tvradio]
- return "http://feeds.bbc.co.uk/iplayer/" + '/'.join(path)
+ return "http://feeds.bbc.co.uk/iplayer/" + '/'.join (path)
- def get_name(self, separator=' '):
+ def get_name (self, separator=' '):
"""
- A readable title for this feed, e.g. 'BBC One' or 'TV Drama' or 'BBC One Drama'
- separator: string to separate name parts with, defaults to ' '. Use None to return a list (e.g. ['TV', 'Drama']).
+ A readable title for this feed, e.g. 'BBC One' or 'TV Drama' or
+ 'BBC One Drama'
+ separator: string to separate name parts with, defaults to ' '.
+ Use None to return a list (e.g. ['TV', 'Drama']).
"""
path = []
# TODO: This is not i18n-friendly whatsoever
# if got a channel, don't need tv/radio distinction
if self.channel:
- assert self.channel in channels_tv or self.channel in channels_radio, 'Unknown channel'
+ assert (self.channel in channels_tv or
+ self.channel in channels_radio), 'Unknown channel'
#print self.tvradio
if self.tvradio == 'tv':
- path.append(channels_tv.get(self.channel, '(TV)'))
+ path.append (channels_tv.get (self.channel, ' (TV)'))
else:
- path.append(channels_radio.get(self.channel, '(Radio)'))
+ path.append (channels_radio.get (self.channel, ' (Radio)'))
elif self.tvradio:
# no channel
medium = 'TV'
- if self.tvradio == 'radio': medium = 'Radio'
- path.append(medium)
+ if self.tvradio == 'radio':
+ medium = 'Radio'
+ path.append (medium)
if self.searchterm:
path += ['Search results for %s' % self.searchterm]
if self.category:
assert self.category in categories, 'Unknown category'
- path.append(categories.get(self.category, '(Category)'))
+ path.append (categories.get (self.category, ' (Category)'))
if self.atoz:
- path.append("beginning with %s" % self.atoz.upper())
+ path.append ("beginning with %s" % self.atoz.upper ())
if separator != None:
- return separator.join(path)
+ return separator.join (path)
else:
return path
- def channels(self):
+ def channels (self):
"""
Return a list of available channels.
"""
- if self.channel: return None
- if self.tvradio == 'tv': return channels_tv
- if self.tvradio == 'radio': return channels_radio
+ if self.channel:
+ return None
+ if self.tvradio == 'tv':
+ return channels_tv
+ if self.tvradio == 'radio':
+ return channels_radio
return None
- def channels_feed(self):
+ def channels_feed (self):
"""
Return a list of available channels as a list of feeds.
"""
if self.channel:
- #logging.warning("%s doesn\'t have any channels!", self.channel)
+ #logging.warning ("%s doesn\'t have any channels!", self.channel)
return None
if self.tvradio == 'tv':
- return [feed('tv', channel=ch) for (ch, title) in channels_tv_list]
+ return [feed ('tv', channel = ch)
+ for (ch, title) in channels_tv_list]
if self.tvradio == 'radio':
- return [feed('radio', channel=ch) for (ch, title) in channels_radio_list]
+ return [feed ('radio', channel = ch)
+ for (ch, title) in channels_radio_list]
return None
- def subcategories(self):
- raise NotImplementedError('Sub-categories not yet supported')
+ def subcategories (self):
+ raise NotImplementedError ('Sub-categories not yet supported')
@classmethod
- def is_atoz(self, letter):
+ def is_atoz (self, letter):
"""
- Return False if specified letter is not a valid 'A to Z' directory entry.
- Otherwise returns the directory name.
+ Return False if specified letter is not a valid 'A to Z' directory
+ entry. Otherwise returns the directory name.
- >>> feed.is_atoz('a'), feed.is_atoz('z')
+ >>> feed.is_atoz ('a'), feed.is_atoz ('z')
('a', 'z')
- >>> feed.is_atoz('0'), feed.is_atoz('9')
+ >>> feed.is_atoz ('0'), feed.is_atoz ('9')
('0-9', '0-9')
- >>> feed.is_atoz('123'), feed.is_atoz('abc')
+ >>> feed.is_atoz ('123'), feed.is_atoz ('abc')
(False, False)
- >>> feed.is_atoz('big british castle'), feed.is_atoz('')
+ >>> feed.is_atoz ('big british castle'), feed.is_atoz ('')
(False, False)
"""
- l = letter.lower()
- if len(l) != 1 and l != '0-9':
+ l = letter.lower ()
+ if len (l) != 1 and l != '0-9':
return False
- if l in '0123456789': l = "0-9"
+ if l in '0123456789':
+ l = "0-9"
if l not in 'abcdefghijklmnopqrstuvwxyz0-9':
return False
return l
- def sub(self, *args, **kwargs):
+ def sub (self, *args, **kwargs):
"""
Clones this feed, altering the specified parameters.
- >>> feed('tv').sub(channel='bbc_one').channel
+ >>> feed ('tv').sub (channel='bbc_one').channel
'bbc_one'
- >>> feed('tv', channel='bbc_one').sub(channel='bbc_two').channel
+ >>> feed ('tv', channel='bbc_one').sub (channel='bbc_two').channel
'bbc_two'
- >>> feed('tv', channel='bbc_one').sub(category='drama').category
+ >>> feed ('tv', channel='bbc_one').sub (category='drama').category
'drama'
- >>> feed('tv', channel='bbc_one').sub(channel=None).channel
+ >>> feed ('tv', channel='bbc_one').sub (channel=None).channel
>>>
"""
- d = self.__dict__.copy()
- d.update(kwargs)
- return feed(**d)
+ d = self.__dict__.copy ()
+ d.update (kwargs)
+ return feed (**d)
- def get(self, subfeed):
+ def get (self, subfeed):
"""
Returns a child/subfeed of this feed.
child: can be channel/cat/subcat/letter, e.g. 'bbc_one'
"""
if self.channel:
- return self.sub(category=subfeed)
+ return self.sub (category = subfeed)
elif self.category:
# no children: TODO support subcategories
return None
- elif self.is_atoz(subfeed):
- return self.sub(atoz=self.is_atoz(subfeed))
+ elif self.is_atoz (subfeed):
+ return self.sub (atoz=self.is_atoz (subfeed))
else:
- if subfeed in channels_tv: return feed('tv', channel=subfeed)
- if subfeed in channels_radio: return feed('radio', channel=subfeed)
+ if subfeed in channels_tv:
+ return feed ('tv', channel = subfeed)
+ if subfeed in channels_radio:
+ return feed ('radio', channel = subfeed)
# TODO handle properly oh pants
return None
@classmethod
- def read_rss(self, url):
- #logging.info('Read RSS: %s', url)
+ def read_rss (self, url):
+ #logging.info ('Read RSS: %s', url)
if url not in rss_cache:
- #logging.info('Feed URL not in cache, requesting...')
- xml = httpget(url)
- progs = listparser.parse(xml)
- if not progs: return []
+ #logging.info ('Feed URL not in cache, requesting...')
+ xml = httpget (url)
+ progs = listparser.parse (xml)
+ if not progs:
+ return []
d = []
for entry in progs.entries:
- pid = parse_entry_id(entry.id)
- p = programme(pid)
- d.append(p)
- #logging.info('Found %d entries', len(d))
+ pid = parse_entry_id (entry.id)
+ p = programme (pid)
+ d.append (p)
+ #logging.info ('Found %d entries', len (d))
rss_cache[url] = d
#else:
- # logging.info('RSS found in cache')
+ # logging.info ('RSS found in cache')
return rss_cache[url]
- def popular(self):
- return self.read_rss(self.create_url('popular'))
+ def popular (self):
+ return self.read_rss (self.create_url ('popular'))
- def highlights(self):
- return self.read_rss(self.create_url('highlights'))
+ def highlights (self):
+ return self.read_rss (self.create_url ('highlights'))
- def list(self):
- return self.read_rss(self.create_url('list'))
+ def list (self):
+ return self.read_rss (self.create_url ('list'))
- def categories(self):
+ def categories (self):
# quick and dirty category extraction and count
- xmlURL = self.create_url('list')
- xml = httpget(xmlURL)
- cat = re.findall( "<category .*term=\"(.*?)\"", xml )
+ xmlURL = self.create_url ('list')
+ xml = httpget (xmlURL)
+ cat = re.findall ("<category .*term=\" (.*?)\"", xml)
categories = {}
for c in cat:
if c != 'TV':
- if not categories.has_key(c): categories[c] = 0
+ if not categories.has_key (c):
+ categories[c] = 0
categories[c] += 1
- alist=[]
- k = categories.keys()
- k.sort()
+ alist = []
+ k = categories.keys ()
+ k.sort ()
for c in k:
n = categories[c]
- c = c.replace('&', '&')
- c = c.replace('>', '>')
- c = c.replace('<', '<')
- alist.append((c, n))
+ c = c.replace ('&', '&')
+ c = c.replace ('>', '>')
+ c = c.replace ('<', '<')
+ alist.append ( (c, n))
return alist
@property
- def is_radio(self):
+ def is_radio (self):
""" True if this feed is for radio. """
return self.tvradio == 'radio'
@property
- def is_tv(self):
+ def is_tv (self):
""" True if this feed is for tv. """
return self.tvradio == 'tv'
- name = property(get_name)
+ name = property (get_name)
-tv = feed('tv')
-radio = feed('radio')
+tv = feed ('tv')
+radio = feed ('radio')
-def test():
- tv = feed('tv')
- print tv.popular()
- print tv.channels()
- print tv.get('bbc_one')
- print tv.get('bbc_one').list()
- for c in tv.get('bbc_one').categories():
+def test ():
+ tv = feed ('tv')
+ print tv.popular ()
+ print tv.channels ()
+ print tv.get ('bbc_one')
+ print tv.get ('bbc_one').list ()
+ for c in tv.get ('bbc_one').categories ():
print c
- #print tv.get('bbc_one').channels()
- #print tv.categories()
- #print tv.get('drama').list()
- #print tv.get('drama').get_subcategory('period').list()
+ #print tv.get ('bbc_one').channels ()
+ #print tv.categories ()
+ #print tv.get ('drama').list ()
+ #print tv.get ('drama').get_subcategory ('period').list ()
if __name__ == '__main__':
- test()
+ test ()
diff --git a/src/plugins/iplayer/listparser.py b/src/plugins/iplayer/listparser.py
index 526224a..b96f80c 100644
--- a/src/plugins/iplayer/listparser.py
+++ b/src/plugins/iplayer/listparser.py
@@ -4,49 +4,62 @@
import re
-def xmlunescape(data):
- data = data.replace('&', '&')
- data = data.replace('>', '>')
- data = data.replace('<', '<')
+def xmlunescape (data):
+ data = data.replace ('&', '&')
+ data = data.replace ('>', '>')
+ data = data.replace ('<', '<')
return data
-class listentry(object):
- def __init__(self, title=None, id=None, updated=None, summary=None, categories=None):
- self.title = title
- self.id = id
- self.updated = updated
- self.summary = summary
- self.categories = categories
-
-class listentries(object):
- def __init__(self):
- self.entries = []
-
-def parse(xmlSource):
+class listentry (object):
+ def __init__ (self, title = None, id = None, updated = None, summary = None,
+ categories = None):
+ self.title = title
+ self.id = id
+ self.updated = updated
+ self.summary = summary
+ self.categories = categories
+
+class listentries (object):
+ def __init__ (self):
+ self.entries = []
+
+def parse (xmlSource):
try:
- encoding = re.findall( "<\?xml version=\"[^\"]*\" encoding=\"([^\"]*)\"\?>", xmlSource )[ 0 ]
- except: return None
- elist=listentries()
- # gather all list entries
- entriesSrc = re.findall( "<entry>(.*?)</entry>", xmlSource, re.DOTALL)
- datematch = re.compile(':\s+([0-9]+)/([0-9]+)/([0-9]{4})')
-
+ regexp = "<\?xml version=\"[^\"]*\" encoding=\" ([^\"]*)\"\?>"
+ encoding = re.findall (regexp, xmlSource)[0]
+ except:
+ return None
+
+ elist = listentries ()
+ # gather all list entries
+ entriesSrc = re.findall ("<entry> (.*?)</entry>", xmlSource, re.DOTALL)
+ datematch = re.compile (':\s+ ([0-9]+)/ ([0-9]+)/ ([0-9]{4})')
+
# enumerate thru the element list and gather info
for entrySrc in entriesSrc:
- entry={}
- title = re.findall( "<title[^>]*>(.*?)</title>", entrySrc, re.DOTALL )[0]
- id = re.findall( "<id[^>]*>(.*?)</id>", entrySrc, re.DOTALL )[0]
- updated = re.findall( "<updated[^>]*>(.*?)</updated>", entrySrc, re.DOTALL )[0]
- summary = re.findall( "<content[^>]*>(.*?)</content>", entrySrc, re.DOTALL )[0].splitlines()[-3]
- categories = re.findall( "<category[^>]*term=\"(.*?)\"[^>]*>", entrySrc, re.DOTALL )
-
- match = datematch.search(title)
+ entry = {}
+ title = re.findall ("<title[^>]*> (.*?)</title>",
+ entrySrc, re.DOTALL)[0]
+ id = re.findall ("<id[^>]*> (.*?)</id>", entrySrc, re.DOTALL)[0]
+ updated = re.findall ("<updated[^>]*> (.*?)</updated>",
+ entrySrc, re.DOTALL)[0]
+ summary = re.findall ("<content[^>]*> (.*?)</content>",
+ entrySrc, re.DOTALL)[0].splitlines ()[-3]
+ categories = re.findall ("<category[^>]*term=\" (.*?)\"[^>]*>",
+ entrySrc, re.DOTALL)
+
+ match = datematch.search (title)
if match:
- # if the title contains a data at the end use that as the updated date YYYY-MM-DD
- updated = "%s-%s-%s" % ( match.group(3), match.group(2), match.group(1) )
-
- e_categories=[]
- for c in categories: e_categories.append(xmlunescape(c))
- elist.entries.append(listentry(xmlunescape(title), xmlunescape(id), xmlunescape(updated), xmlunescape(summary), e_categories))
-
- return elist
+ # if the title contains a data at the end use that as the updated
+ # date YYYY-MM-DD
+ updated = "%s-%s-%s" % (match.group (3), match.group (2),
+ match.group (1))
+
+ e_categories = []
+ for c in categories:
+ e_categories.append (xmlunescape (c))
+ elist.entries.append (listentry (xmlunescape (title), xmlunescape (id),
+ xmlunescape (updated),
+ xmlunescape (summary), e_categories))
+
+ return elist
diff --git a/src/plugins/jamendo/jamendo.py b/src/plugins/jamendo/jamendo.py
index 76e28a1..e27850b 100644
--- a/src/plugins/jamendo/jamendo.py
+++ b/src/plugins/jamendo/jamendo.py
@@ -14,7 +14,7 @@
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
@@ -51,22 +51,22 @@ except ImportError:
try:
import simplejson as json
except ImportError:
- dlg = Gtk.MessageDialog(
+ dlg = Gtk.MessageDialog (
message_type=Gtk.MessageType.ERROR,
buttons=Gtk.ButtonsType.OK
)
- dlg.set_markup(_(u'You need to install the Python simplejson module.'))
- dlg.run()
- dlg.destroy()
+ dlg.set_markup (_(u'You need to install the Python simplejson module.'))
+ dlg.run ()
+ dlg.destroy ()
raise
-socket.setdefaulttimeout(30)
-gobject.threads_init()
+socket.setdefaulttimeout (30)
+gobject.threads_init ()
-class JamendoPlugin(gobject.GObject, Peas.Activatable, PeasGtk.Configurable):
+class JamendoPlugin (gobject.GObject, Peas.Activatable, PeasGtk.Configurable):
__gtype_name__ = 'JamendoPlugin'
- object = gobject.property(type = gobject.GObject)
+ object = gobject.property (type = gobject.GObject)
"""
Jamendo totem plugin GUI.
@@ -77,83 +77,97 @@ class JamendoPlugin(gobject.GObject, Peas.Activatable, PeasGtk.Configurable):
TAB_POPULAR = 1
TAB_LATEST = 2
- def __init__(self):
+ def __init__ (self):
self.debug = True
self.gstreamer_plugins_present = True
self.totem = None
self.settings = Gio.Settings.new ('org.gnome.totem.plugins.jamendo')
self.settings.connect ('changed::format', self.on_format_changed)
- self.settings.connect ('changed::num-per-page', self.on_num_per_page_changed)
+ self.settings.connect ('changed::num-per-page',
+ self.on_num_per_page_changed)
- def do_activate(self):
+ def do_activate (self):
"""
Plugin activation.
"""
self.totem = self.object
# Initialise the interface
- builder = Totem.plugin_load_interface ("jamendo", "jamendo.ui", True, self.totem.get_main_window (), self)
- self.popup = builder.get_object('popup_menu')
- container = builder.get_object('container')
- self.notebook = builder.get_object('notebook')
- self.search_entry = builder.get_object('search_entry')
- self.search_combo = builder.get_object('search_combo')
- self.search_combo.set_active(0)
- self.album_button = builder.get_object('album_button')
- self.previous_button = builder.get_object('previous_button')
- self.next_button = builder.get_object('next_button')
+ builder = Totem.plugin_load_interface ("jamendo", "jamendo.ui", True,
+ self.totem.get_main_window (),
+ self)
+ self.popup = builder.get_object ('popup_menu')
+ container = builder.get_object ('container')
+ self.notebook = builder.get_object ('notebook')
+ self.search_entry = builder.get_object ('search_entry')
+ self.search_combo = builder.get_object ('search_combo')
+ self.search_combo.set_active (0)
+ self.album_button = builder.get_object ('album_button')
+ self.previous_button = builder.get_object ('previous_button')
+ self.next_button = builder.get_object ('next_button')
self.progressbars = [
- builder.get_object('results_progressbar'),
- builder.get_object('popular_progressbar'),
- builder.get_object('latest_progressbar'),
+ builder.get_object ('results_progressbar'),
+ builder.get_object ('popular_progressbar'),
+ builder.get_object ('latest_progressbar'),
]
self.treeviews = [
- builder.get_object('results_treeview'),
- builder.get_object('popular_treeview'),
- builder.get_object('latest_treeview'),
+ builder.get_object ('results_treeview'),
+ builder.get_object ('popular_treeview'),
+ builder.get_object ('latest_treeview'),
]
- self.setup_treeviews()
+ self.setup_treeviews ()
# Set up signals
- builder.get_object('search_button').connect('clicked', self.on_search_button_clicked)
- self.search_entry.connect('activate', self.on_search_entry_activate)
- self.notebook.connect('switch-page', self.on_notebook_switch_page)
- self.previous_button.connect('clicked', self.on_previous_button_clicked)
- self.next_button.connect('clicked', self.on_next_button_clicked)
- self.album_button.connect('clicked', self.on_album_button_clicked)
- builder.get_object('add_to_playlist').connect('activate', self.on_add_to_playlist_activate)
- builder.get_object('jamendo_album_page').connect('activate', self.on_open_jamendo_album_page_activate)
-
- self.reset()
- container.show_all()
- self.totem.add_sidebar_page("jamendo", _(u"Jamendo"), container)
-
- def do_deactivate(self):
+ search_button = builder.get_object ('search_button')
+ search_button.connect ('clicked', self.on_search_button_clicked)
+ self.search_entry.connect ('activate', self.on_search_entry_activate)
+ self.notebook.connect ('switch-page', self.on_notebook_switch_page)
+ self.previous_button.connect ('clicked',
+ self.on_previous_button_clicked)
+ self.next_button.connect ('clicked', self.on_next_button_clicked)
+ self.album_button.connect ('clicked', self.on_album_button_clicked)
+ add_to_playlist = builder.get_object ('add_to_playlist')
+ add_to_playlist.connect ('activate', self.on_add_to_playlist_activate)
+ album_page_button = builder.get_object ('jamendo_album_page')
+ album_page_button.connect ('activate',
+ self.on_open_jamendo_album_page_activate)
+
+ self.reset ()
+ container.show_all ()
+ self.totem.add_sidebar_page ("jamendo", _(u"Jamendo"), container)
+
+ def do_deactivate (self):
"""
Plugin deactivation.
"""
- self.totem.remove_sidebar_page("jamendo")
+ self.totem.remove_sidebar_page ("jamendo")
- def do_create_configure_widget(self):
+ def do_create_configure_widget (self):
"""
Plugin config widget.
- This code must be independent from the rest of the plugin. FIXME: bgo#624073
+ This code must be independent from the rest of the plugin.
+ FIXME: bgo#624073
"""
- builder = Totem.plugin_load_interface ('jamendo', 'jamendo.ui', True, None, self)
+ builder = Totem.plugin_load_interface ('jamendo', 'jamendo.ui', True,
+ None, self)
config_widget = builder.get_object ('config_widget')
config_widget.connect ('destroy', self.on_config_widget_destroy)
format = self.settings.get_enum ('format')
num_per_page = self.settings.get_value ('num-per-page').get_uint32 ()
- # Set up the "format" combo box. We can't use g_settings_bind() here, as it won't automatically convert between enums and ints. To do so,
- # we'd need to use g_settings_bind_with_mapping(), but that isn't introspectable. We have to handle the binding manually.
- combo = builder.get_object('preferred_format_combo')
- combo.set_active(format)
+ # Set up the "format" combo box. We can't use g_settings_bind () here,
+ # as it won't automatically convert between enums and ints. To do so,
+ # we'd need to use g_settings_bind_with_mapping (), but that isn't
+ # introspectable. We have to handle the binding manually.
+ combo = builder.get_object ('preferred_format_combo')
+ combo.set_active (format)
combo.connect ('changed', self.on_format_combo_changed)
- self.settings.connect ('changed::format', self.on_format_setting_changed, combo)
+ self.settings.connect ('changed::format',
+ self.on_format_setting_changed, combo)
- spinbutton = builder.get_object('album_num_spinbutton')
- spinbutton.set_value(num_per_page)
- self.settings.bind ('num-per-page', spinbutton, 'value', Gio.SettingsBindFlags.DEFAULT)
+ spinbutton = builder.get_object ('album_num_spinbutton')
+ spinbutton.set_value (num_per_page)
+ self.settings.bind ('num-per-page', spinbutton, 'value',
+ Gio.SettingsBindFlags.DEFAULT)
return config_widget
@@ -165,7 +179,8 @@ class JamendoPlugin(gobject.GObject, Peas.Activatable, PeasGtk.Configurable):
def on_format_setting_changed (self, settings, key, combo):
"""
- Called for the "format" preference combo box when the corresponding GSettings value is changed.
+ Called for the "format" preference combo box when the corresponding
+ GSettings value is changed.
"""
combo.set_active (self.settings.get_enum ('format'))
@@ -177,11 +192,11 @@ class JamendoPlugin(gobject.GObject, Peas.Activatable, PeasGtk.Configurable):
def on_config_widget_destroy (self, widget):
try:
- self.reset()
+ self.reset ()
except:
pass
- def reset(self):
+ def reset (self):
"""
XXX this will be refactored asap.
"""
@@ -202,295 +217,303 @@ class JamendoPlugin(gobject.GObject, Peas.Activatable, PeasGtk.Configurable):
}
self.album_count = [0, 0, 0]
for tv in self.treeviews:
- tv.get_model().clear()
- self._update_buttons_state()
+ tv.get_model ().clear ()
+ self._update_buttons_state ()
- def setup_treeviews(self):
+ def setup_treeviews (self):
"""
Setup the 3 treeview: result, popular and latest
"""
self.current_treeview = self.treeviews[0]
for w in self.treeviews:
selection = w.get_selection ()
- selection.set_mode(Gtk.SelectionMode.MULTIPLE)
+ selection.set_mode (Gtk.SelectionMode.MULTIPLE)
selection.connect ('changed', self.on_treeview_selection_changed)
# build pixbuf column
- cell = Gtk.CellRendererPixbuf()
- col = Gtk.TreeViewColumn(cell_renderer=cell, pixbuf=1)
+ cell = Gtk.CellRendererPixbuf ()
+ col = Gtk.TreeViewColumn (cell_renderer=cell, pixbuf=1)
- w.append_column(col)
+ w.append_column (col)
# build description column
- cell = Gtk.CellRendererText()
- cell.set_property('ellipsize', Pango.EllipsizeMode.END)
- col = Gtk.TreeViewColumn(cell_renderer=cell, markup=2)
- col.set_expand(True)
- w.append_column(col)
+ cell = Gtk.CellRendererText ()
+ cell.set_property ('ellipsize', Pango.EllipsizeMode.END)
+ col = Gtk.TreeViewColumn (cell_renderer=cell, markup=2)
+ col.set_expand (True)
+ w.append_column (col)
# duration column
- cell = Gtk.CellRendererText()
- cell.set_property('xalign', 1.0)
- cell.set_property('size-points', 8)
- col = Gtk.TreeViewColumn(cell_renderer=cell, markup=3)
- col.set_alignment(1.0)
- w.append_column(col)
+ cell = Gtk.CellRendererText ()
+ cell.set_property ('xalign', 1.0)
+ cell.set_property ('size-points', 8)
+ col = Gtk.TreeViewColumn (cell_renderer=cell, markup=3)
+ col.set_alignment (1.0)
+ w.append_column (col)
# configure the treeview
- w.set_show_expanders(False) # we manage internally expand/collapse
- w.set_tooltip_column(4) # set the tooltip column
+ w.set_show_expanders (False) # we manage internally expand/collapse
+ w.set_tooltip_column (4) # set the tooltip column
# Connect signals
- w.connect("button-press-event", self.on_treeview_row_clicked)
- w.connect("row-activated", self.on_treeview_row_activated)
+ w.connect ("button-press-event", self.on_treeview_row_clicked)
+ w.connect ("row-activated", self.on_treeview_row_activated)
- def add_treeview_item(self, treeview, album):
- if not isinstance(album['image'], GdkPixbuf.Pixbuf):
+ def add_treeview_item (self, treeview, album):
+ if not isinstance (album['image'], GdkPixbuf.Pixbuf):
# album image pixbuf is not yet built
try:
- pb = GdkPixbuf.Pixbuf.new_from_file(album['image'])
- os.unlink(album['image'])
+ pb = GdkPixbuf.Pixbuf.new_from_file (album['image'])
+ os.unlink (album['image'])
album['image'] = pb
except:
# do not fail for this, just display a dummy pixbuf
- album['image'] = GdkPixbuf.Pixbuf.new(GdkPixbuf.Colorspace.RGB, True,
- 8, 1, 1)
+ album['image'] = GdkPixbuf.Pixbuf.new (GdkPixbuf.Colorspace.RGB,
+ True, 8, 1, 1)
# format title
- title = '<b>%s</b>\n' % self._format_str(album['name'])
- title += _(u'Artist: %s') % self._format_str(album['artist_name'])
+ title = '<b>%s</b>\n' % self._format_str (album['name'])
+ title += _(u'Artist: %s') % self._format_str (album['artist_name'])
# format duration
- dur = self._format_duration(album['duration'])
+ dur = self._format_duration (album['duration'])
# format tooltip
try:
- # Translators: this is the release date of an album in Python strptime format
- release = time.strptime(album['dates']['release'][0:10], _(u'%Y-%m-%d'))
- # Translators: this is the release time of an album in Python strftime format
- release = time.strftime(_(u'%x'), release)
+ # Translators: this is the release date of an album in Python
+ # strptime format
+ release = time.strptime (album['dates']['release'][0:10],
+ _(u'%Y-%m-%d'))
+ # Translators: this is the release time of an album in Python
+ # strftime format
+ release = time.strftime (_(u'%x'), release)
except:
release = ''
- tip = '\n'.join([
- '<b>%s</b>' % self._format_str(album['name']),
- _(u'Artist: %s') % self._format_str(album['artist_name']),
- _(u'Genre: %s') % self._format_str(album['genre']),
+ tip = '\n'.join ([
+ '<b>%s</b>' % self._format_str (album['name']),
+ _(u'Artist: %s') % self._format_str (album['artist_name']),
+ _(u'Genre: %s') % self._format_str (album['genre']),
_(u'Released on: %s') % release,
- _(u'License: %s') % self._format_str(album['license'][0]),
+ _(u'License: %s') % self._format_str (album['license'][0]),
])
# append album row
- parent = treeview.get_model().append(None,
+ parent = treeview.get_model ().append (None,
[album, album['image'], title, dur, tip]
)
# append track rows
- icon = GdkPixbuf.Pixbuf.new(GdkPixbuf.Colorspace.RGB, True, 8, 1, 1)
- for i, track in enumerate(album['tracks']):
+ icon = GdkPixbuf.Pixbuf.new (GdkPixbuf.Colorspace.RGB, True, 8, 1, 1)
+ for i, track in enumerate (album['tracks']):
# track title
# Translators: this is the title of a track in Python format
# (first argument is the track number, second is the track title)
tt = (u'<small>%s</small>' % _(u'%02d. %s')) % \
- (i+1, self._format_str(track['name']))
+ (i+1, self._format_str (track['name']))
# track duration
- td = self._format_duration(track['duration'])
+ td = self._format_duration (track['duration'])
# track tooltip
- tip = '\n'.join([
- '<b>%s</b>' % self._format_str(track['name']),
- _(u'Album: %s') % self._format_str(album['name']),
- _(u'Artist: %s') % self._format_str(album['artist_name']),
+ tip = '\n'.join ([
+ '<b>%s</b>' % self._format_str (track['name']),
+ _(u'Album: %s') % self._format_str (album['name']),
+ _(u'Artist: %s') % self._format_str (album['artist_name']),
_(u'Duration: %s') % td,
])
# append track
- treeview.get_model().append(parent, [track, icon, tt, td, tip])
+ treeview.get_model ().append (parent, [track, icon, tt, td, tip])
# update current album count
- pindex = self.treeviews.index(treeview)
+ pindex = self.treeviews.index (treeview)
self.album_count[pindex] += 1
- def add_album_to_playlist(self, mode, album):
+ def add_album_to_playlist (self, mode, album):
"""
Add an album to the playlist, mode can be: replace, enqueue or
enqueue_and_play.
"""
- for i, track in enumerate(album['tracks']):
+ for i, track in enumerate (album['tracks']):
if mode in ('replace', 'enqueue_and_play'):
if i == 0:
# play first track
- self.add_track_to_playlist(mode, track)
+ self.add_track_to_playlist (mode, track)
else:
# and enqueue other tracks
- self.add_track_to_playlist('enqueue', track)
+ self.add_track_to_playlist ('enqueue', track)
else:
- self.add_track_to_playlist('enqueue', track)
+ self.add_track_to_playlist ('enqueue', track)
- def add_track_to_playlist(self, mode, t):
+ def add_track_to_playlist (self, mode, t):
"""
Add a track to the playlist, mode can be: replace, enqueue or
enqueue_and_play.
"""
if mode == 'replace':
- self.totem.action_remote(Totem.RemoteCommand.REPLACE, t['stream'].encode ('UTF-8'))
+ self.totem.action_remote (Totem.RemoteCommand.REPLACE,
+ t['stream'].encode ('UTF-8'))
elif mode == 'enqueue':
- self.totem.action_remote(Totem.RemoteCommand.ENQUEUE, t['stream'].encode ('UTF-8'))
+ self.totem.action_remote (Totem.RemoteCommand.ENQUEUE,
+ t['stream'].encode ('UTF-8'))
- def fetch_albums(self, pn=1):
+ def fetch_albums (self, pn=1):
"""
Initialize the fetch thread.
"""
- tab_index = self.treeviews.index(self.current_treeview)
+ tab_index = self.treeviews.index (self.current_treeview)
if tab_index == self.TAB_POPULAR:
params = {'order': 'rating_desc'}
elif tab_index == self.TAB_LATEST:
params = {'order': 'date_desc'}
else:
- value = self.search_entry.get_text()
+ value = self.search_entry.get_text ()
if not value:
return
- prop = self.SEARCH_CRITERIA[self.search_combo.get_active()]
+ prop = self.SEARCH_CRITERIA[self.search_combo.get_active ()]
params = {'order': 'date_desc', prop: value}
params['pn'] = pn
- self.current_treeview.get_model().clear()
- self.previous_button.set_sensitive(False)
- self.next_button.set_sensitive(False)
- self.album_button.set_sensitive(False)
- self.progressbars[tab_index].show()
- self.progressbars[tab_index].set_fraction(0.0)
- self.progressbars[tab_index].set_text(
+ self.current_treeview.get_model ().clear ()
+ self.previous_button.set_sensitive (False)
+ self.next_button.set_sensitive (False)
+ self.album_button.set_sensitive (False)
+ self.progressbars[tab_index].show ()
+ self.progressbars[tab_index].set_fraction (0.0)
+ self.progressbars[tab_index].set_text (
_(u'Fetching albums, please waitâ?¦')
)
lcb = (self.on_fetch_albums_loop, self.current_treeview)
dcb = (self.on_fetch_albums_done, self.current_treeview)
ecb = (self.on_fetch_albums_error, self.current_treeview)
- thread = JamendoService(params, lcb, dcb, ecb)
- thread.start()
+ thread = JamendoService (params, lcb, dcb, ecb)
+ thread.start ()
self.running_threads[tab_index] = True
- def on_fetch_albums_loop(self, treeview, album):
+ def on_fetch_albums_loop (self, treeview, album):
"""
Add an album item and its tracks to the current treeview.
"""
- self.add_treeview_item(treeview, album)
+ self.add_treeview_item (treeview, album)
# pulse progressbar
- pindex = self.treeviews.index(treeview)
- self.progressbars[pindex].set_fraction(
- float(self.album_count[pindex]) / float(JamendoService.NUM_PER_PAGE)
+ pindex = self.treeviews.index (treeview)
+ album_count = self.album_count[pindex]
+ self.progressbars[pindex].set_fraction (
+ float (album_count) / float (JamendoService.NUM_PER_PAGE)
)
- def on_fetch_albums_done(self, treeview, albums, save_state=True):
+ def on_fetch_albums_done (self, treeview, albums, save_state=True):
"""
Called when the thread finished fetching albums.
"""
- pindex = self.treeviews.index(treeview)
- model = treeview.get_model()
- if save_state and len(albums):
- self.pages[pindex].append(albums)
- self.current_page[pindex] = len(self.pages[pindex])
- self._update_buttons_state()
- self.progressbars[pindex].set_fraction(0.0)
- self.progressbars[pindex].hide()
+ pindex = self.treeviews.index (treeview)
+ model = treeview.get_model ()
+ if save_state and len (albums):
+ self.pages[pindex].append (albums)
+ self.current_page[pindex] = len (self.pages[pindex])
+ self._update_buttons_state ()
+ self.progressbars[pindex].set_fraction (0.0)
+ self.progressbars[pindex].hide ()
self.album_count[pindex] = 0
self.running_threads[pindex] = False
- def on_fetch_albums_error(self, treeview, exc):
+ def on_fetch_albums_error (self, treeview, exc):
"""
Called when an error occured in the thread.
"""
- self.reset()
- pindex = self.treeviews.index(treeview)
- self.progressbars[pindex].set_fraction(0.0)
- self.progressbars[pindex].hide()
+ self.reset ()
+ pindex = self.treeviews.index (treeview)
+ self.progressbars[pindex].set_fraction (0.0)
+ self.progressbars[pindex].hide ()
self.running_threads[pindex] = False
- # managing exceptions with urllib is a real PITA... :(
- if hasattr(exc, 'reason'):
+ # managing exceptions with urllib is a real PITA... : (
+ if hasattr (exc, 'reason'):
try:
reason = exc.reason[1]
except:
try:
reason = exc.reason[0]
except:
- reason = str(exc)
- reason = reason.capitalize()
+ reason = str (exc)
+ reason = reason.capitalize ()
msg = _(u'Failed to connect to Jamendo server.\n%s.') % reason
- elif hasattr(exc, 'code'):
+ elif hasattr (exc, 'code'):
msg = _(u'The Jamendo server returned code %s.') % exc.code
else:
- msg = str(exc)
+ msg = str (exc)
- self.totem.action_error(_(u'An error occurred while fetching albums.'), msg)
+ self.totem.action_error (_(u'An error occurred while fetching albums.'),
+ msg)
- def on_search_entry_activate(self, *args):
+ def on_search_entry_activate (self, *args):
"""
Called when the user typed <enter> in the search entry.
"""
- return self.on_search_button_clicked()
+ return self.on_search_button_clicked ()
- def on_search_button_clicked(self, *args):
+ def on_search_button_clicked (self, *args):
"""
Called when the user clicked on the search button.
"""
- if not self.search_entry.get_text():
+ if not self.search_entry.get_text ():
return
if self.current_treeview != self.treeviews[self.TAB_RESULTS]:
self.current_treeview = self.treeviews[self.TAB_RESULTS]
- self.notebook.set_current_page(self.TAB_RESULTS)
+ self.notebook.set_current_page (self.TAB_RESULTS)
else:
- self.on_notebook_switch_page(new_search=True)
+ self.on_notebook_switch_page (new_search=True)
- def on_notebook_switch_page(self, nb=None, tab=None, tab_num=0,
+ def on_notebook_switch_page (self, nb=None, tab=None, tab_num=0,
new_search=False):
"""
Called when the changed a notebook page.
"""
- self.current_treeview = self.treeviews[int(tab_num)]
- self._update_buttons_state()
- model = self.current_treeview.get_model()
+ self.current_treeview = self.treeviews[int (tab_num)]
+ self._update_buttons_state ()
+ model = self.current_treeview.get_model ()
# fetch popular and latest albums only once
- if self.running_threads[int(tab_num)] == True or \
- (not new_search and len(model)):
+ if self.running_threads[int (tab_num)] == True or \
+ (not new_search and len (model)):
return
if new_search:
self.current_page[self.TAB_RESULTS] = 1
self.pages[self.TAB_RESULTS] = []
self.album_count[self.TAB_RESULTS] = 0
- self._update_buttons_state()
- model.clear()
- self.fetch_albums()
+ self._update_buttons_state ()
+ model.clear ()
+ self.fetch_albums ()
- def on_treeview_row_activated(self, tv, path, column):
+ def on_treeview_row_activated (self, tv, path, column):
"""
Called when the user double-clicked on a treeview element.
"""
try:
- item = self._get_selection()[0] # first item selected
+ item = self._get_selection ()[0] # first item selected
except:
return
if path.get_depth () == 1:
- self.add_album_to_playlist('replace', item)
+ self.add_album_to_playlist ('replace', item)
else:
- self.add_track_to_playlist('replace', item)
+ self.add_track_to_playlist ('replace', item)
- def on_treeview_row_clicked(self, tv, evt):
+ def on_treeview_row_clicked (self, tv, evt):
"""
Called when the user clicked on a treeview element.
"""
try:
if evt.button == 3:
- (path, _, _, _) = tv.get_path_at_pos(int(evt.x), int(evt.y))
- sel = tv.get_selection()
- (_, rows) = sel.get_selected_rows()
+ (path, _, _, _) = tv.get_path_at_pos (int (evt.x), int (evt.y))
+ sel = tv.get_selection ()
+ (_, rows) = sel.get_selected_rows ()
if path not in rows:
- sel.unselect_all()
- sel.select_path(path)
- tv.grab_focus()
- self.popup.popup_for_device(None, None, None, None, None, evt.button, evt.time)
+ sel.unselect_all ()
+ sel.select_path (path)
+ tv.grab_focus ()
+ self.popup.popup_for_device (None, None, None, None, None,
+ evt.button, evt.time)
return True
- (event_x, event_y) = evt.get_coords()
- (path, c, x, y) = tv.get_path_at_pos(int(event_x), int(event_y))
- if (path.get_depth() == 1):
- if tv.row_expanded(path):
- tv.collapse_row(path)
+ (event_x, event_y) = evt.get_coords ()
+ (path, c, x, y) = tv.get_path_at_pos (int (event_x), int (event_y))
+ if path.get_depth () == 1:
+ if tv.row_expanded (path):
+ tv.collapse_row (path)
else:
- tv.expand_row(path, False)
+ tv.expand_row (path, False)
except:
pass
@@ -498,77 +521,77 @@ class JamendoPlugin(gobject.GObject, Peas.Activatable, PeasGtk.Configurable):
(_, rows) = selection.get_selected_rows ()
self.album_button.set_sensitive (len (rows) > 0)
- def on_previous_button_clicked(self, *args):
+ def on_previous_button_clicked (self, *args):
"""
Called when the user clicked the previous button.
"""
- self._update_buttons_state()
- model = self.current_treeview.get_model()
- model.clear()
- pindex = self.treeviews.index(self.current_treeview)
+ self._update_buttons_state ()
+ model = self.current_treeview.get_model ()
+ model.clear ()
+ pindex = self.treeviews.index (self.current_treeview)
self.current_page[pindex] -= 1
albums = self.pages[pindex][self.current_page[pindex]-1]
for album in albums:
- self.add_treeview_item(self.current_treeview, album)
- self.on_fetch_albums_done(self.current_treeview, albums, False)
+ self.add_treeview_item (self.current_treeview, album)
+ self.on_fetch_albums_done (self.current_treeview, albums, False)
- def on_next_button_clicked(self, *args):
+ def on_next_button_clicked (self, *args):
"""
Called when the user clicked the next button.
"""
- self._update_buttons_state()
- model = self.current_treeview.get_model()
- model.clear()
- pindex = self.treeviews.index(self.current_treeview)
- if self.current_page[pindex] == len(self.pages[pindex]):
- self.fetch_albums(self.current_page[pindex]+1)
+ self._update_buttons_state ()
+ model = self.current_treeview.get_model ()
+ model.clear ()
+ pindex = self.treeviews.index (self.current_treeview)
+ if self.current_page[pindex] == len (self.pages[pindex]):
+ self.fetch_albums (self.current_page[pindex]+1)
else:
self.current_page[pindex] += 1
albums = self.pages[pindex][self.current_page[pindex]-1]
for album in albums:
- self.add_treeview_item(self.current_treeview, album)
- self.on_fetch_albums_done(self.current_treeview, albums, False)
+ self.add_treeview_item (self.current_treeview, album)
+ self.on_fetch_albums_done (self.current_treeview, albums, False)
- def on_album_button_clicked(self, *args):
+ def on_album_button_clicked (self, *args):
"""
Called when the user clicked on the album button.
"""
try:
- url = self._get_selection(True)[0]['url']
- os.spawnlp(os.P_NOWAIT, "xdg-open", "xdg-open", url)
+ url = self._get_selection (True)[0]['url']
+ os.spawnlp (os.P_NOWAIT, "xdg-open", "xdg-open", url)
except:
pass
- def on_add_to_playlist_activate(self, *args):
+ def on_add_to_playlist_activate (self, *args):
"""
Called when the user clicked on the add to playlist button of the
popup menu.
"""
- items = self._get_selection()
+ items = self._get_selection ()
for item in items:
if 'tracks' in item:
# we have an album
- self.add_album_to_playlist('enqueue', item)
+ self.add_album_to_playlist ('enqueue', item)
else:
# we have a track
- self.add_track_to_playlist('enqueue', item)
+ self.add_track_to_playlist ('enqueue', item)
- def on_open_jamendo_album_page_activate(self, *args):
+ def on_open_jamendo_album_page_activate (self, *args):
"""
Called when the user clicked on the jamendo album page button of the
popup menu.
"""
- return self.on_album_button_clicked()
+ return self.on_album_button_clicked ()
- def _get_selection(self, root=False):
+ def _get_selection (self, root=False):
"""
Shortcut method to retrieve the treeview items selected.
"""
ret = []
- sel = self.current_treeview.get_selection()
- (model, rows) = sel.get_selected_rows()
+ sel = self.current_treeview.get_selection ()
+ (model, rows) = sel.get_selected_rows ()
for row in rows:
- it = model.get_iter(row)
+ it = model.get_iter (row)
# Return the parent node if root == true
if root:
@@ -576,28 +599,29 @@ class JamendoPlugin(gobject.GObject, Peas.Activatable, PeasGtk.Configurable):
if parent_iter != None:
it = parent_iter
- elt = model.get_value(it, 0)
+ elt = model.get_value (it, 0)
if elt not in ret:
- ret.append(elt)
+ ret.append (elt)
return ret
- def _update_buttons_state(self):
+ def _update_buttons_state (self):
"""
Update the state of the previous and next buttons.
"""
- sel = self.current_treeview.get_selection()
- (model, rows) = sel.get_selected_rows()
+ sel = self.current_treeview.get_selection ()
+ (model, rows) = sel.get_selected_rows ()
try:
- it = model.get_iter(rows[0])
+ it = model.get_iter (rows[0])
except:
it = None
- pindex = self.treeviews.index(self.current_treeview)
- self.previous_button.set_sensitive(self.current_page[pindex] > 1)
- self.next_button.set_sensitive(len(model)==JamendoService.NUM_PER_PAGE)
- self.album_button.set_sensitive(it is not None)
+ pindex = self.treeviews.index (self.current_treeview)
+ self.previous_button.set_sensitive (self.current_page[pindex] > 1)
+ more_results = len (model) == JamendoService.NUM_PER_PAGE
+ self.next_button.set_sensitive (more_results)
+ self.album_button.set_sensitive (it is not None)
- def _format_str(self, st, truncate=False):
+ def _format_str (self, st, truncate=False):
"""
Escape entities for pango markup and force the string to utf-8.
"""
@@ -608,24 +632,24 @@ class JamendoPlugin(gobject.GObject, Peas.Activatable, PeasGtk.Configurable):
except:
return st
- def _format_duration(self, secs):
+ def _format_duration (self, secs):
"""
Format the given number of seconds to a human readable duration.
"""
try:
- secs = int(secs)
+ secs = int (secs)
if secs >= 3600:
- # Translators: time formatting (in Python strftime format) for the Jamendo plugin
- # for times longer than an hour
- return time.strftime(_(u'%H:%M:%S'), time.gmtime(secs))
- # Translators: time formatting (in Python strftime format) for the Jamendo plugin
- # for times shorter than an hour
- return time.strftime(_(u'%M:%S'), time.gmtime(secs))
+ # Translators: time formatting (in Python strftime format) for
+ # the Jamendo plugin for times longer than an hour
+ return time.strftime (_(u'%H:%M:%S'), time.gmtime (secs))
+ # Translators: time formatting (in Python strftime format) for the
+ # Jamendo plugin for times shorter than an hour
+ return time.strftime (_(u'%M:%S'), time.gmtime (secs))
except:
return ''
-class JamendoService(threading.Thread):
+class JamendoService (threading.Thread):
"""
Class that requests the jamendo REST service.
"""
@@ -634,54 +658,60 @@ class JamendoService(threading.Thread):
AUDIO_FORMAT = 'ogg2'
NUM_PER_PAGE = 10
- def __init__(self, params, loop_cb, done_cb, error_cb):
+ def __init__ (self, params, loop_cb, done_cb, error_cb):
self.params = params
self.loop_cb = loop_cb
self.done_cb = done_cb
self.error_cb = error_cb
- self.lock = threading.Lock()
- threading.Thread.__init__(self)
+ self.lock = threading.Lock ()
+ threading.Thread.__init__ (self)
- def run(self):
+ def run (self):
url = '%s/id+name+duration+image+genre+dates+url+artist_id+' \
'artist_name+artist_url/album/json/?n=%s&imagesize=50' % \
(self.API_URL, self.NUM_PER_PAGE)
- if len(self.params):
- url += '&%s' % urllib.urlencode(self.params)
+ if len (self.params):
+ url += '&%s' % urllib.urlencode (self.params)
try:
- self.lock.acquire()
- albums = json.loads(self._request(url))
+ self.lock.acquire ()
+ albums = json.loads (self._request (url))
ret = []
- for i, album in enumerate(albums):
- fname, headers = urllib.urlretrieve(album['image'])
+ for i, album in enumerate (albums):
+ fname, headers = urllib.urlretrieve (album['image'])
album['image'] = fname
- album['tracks'] = json.loads(self._request(
+ album['tracks'] = json.loads (self._request (
'%s/id+name+duration+stream/track/json/?album_id=%s'\
- '&order=numalbum_asc&streamencoding=%s' % (self.API_URL, album['id'], self.AUDIO_FORMAT)
+ '&order=numalbum_asc'\
+ '&streamencoding=%s' % (self.API_URL, album['id'],
+ self.AUDIO_FORMAT)
))
- album['license'] = json.loads(self._request(
+ album['license'] = json.loads (self._request (
'%s/name/license/json/album_license/?album_id=%s'\
% (self.API_URL, album['id'])
))
- # Translators: If Jamendo supports your language, replace "en" with the language code, enclosed
- # in slashes, used to view pages in your language on the Jamendo website. e.g. For French, "en"
- # would be translated to "fr", as Jamendo uses that in its URLs:
+ url = album['url']
+ # Translators: If Jamendo supports your language, replace "en"
+ # with the language code, enclosed in slashes, used to view
+ # pages in your language on the Jamendo website. e.g. For
+ # French, "en" would be translated to "fr", as Jamendo uses that
+ # in its URLs:
# http://www.jamendo.com/fr/album/4818
# Compared to:
# http://www.jamendo.com/en/album/4818
- # If Jamendo doesn't support your language, *do not translate this string*!
- album['url'] = album['url'].replace('/en/', '/' + _('en') + '/')
- gobject.idle_add(self.loop_cb[0], self.loop_cb[1], album)
- gobject.idle_add(self.done_cb[0], self.done_cb[1], albums)
+ # If Jamendo doesn't support your language, *do not translate
+ # this string*!
+ album['url'] = url.replace ('/en/', '/' + _('en') + '/')
+ gobject.idle_add (self.loop_cb[0], self.loop_cb[1], album)
+ gobject.idle_add (self.done_cb[0], self.done_cb[1], albums)
except Exception as exc:
- gobject.idle_add(self.error_cb[0], self.error_cb[1], exc)
+ gobject.idle_add (self.error_cb[0], self.error_cb[1], exc)
finally:
- self.lock.release()
-
- def _request(self, url):
- opener = urllib2.build_opener()
- opener.addheaders = [('User-agent', 'Totem Jamendo plugin')]
- handle = opener.open(url)
- data = handle.read()
- handle.close()
+ self.lock.release ()
+
+ def _request (self, url):
+ opener = urllib2.build_opener ()
+ opener.addheaders = [ ('User-agent', 'Totem Jamendo plugin')]
+ handle = opener.open (url)
+ data = handle.read ()
+ handle.close ()
return data
diff --git a/src/plugins/opensubtitles/hash.py b/src/plugins/opensubtitles/hash.py
index 7e1189d..b8135b4 100644
--- a/src/plugins/opensubtitles/hash.py
+++ b/src/plugins/opensubtitles/hash.py
@@ -5,43 +5,43 @@ import gio
SIZE_ERROR = -1
SEEK_ERROR = -2
-def hashFile(name):
- """ FIXME Need to handle exceptions !! """
-
-
- longlongformat = 'q' # long long
- bytesize = struct.calcsize(longlongformat)
-
- fp = gio.File(name)
-
- filesize = fp.query_info('standard::size', 0).get_attribute_uint64('standard::size')
-
- hash = filesize
-
- if filesize < 65536 * 2:
- return SIZE_ERROR, 0
-
- data = fp.read()
-
- if data.can_seek() != True:
- return SEEK_ERROR, 0
-
- for x in range(65536/bytesize):
- buffer = data.read(bytesize)
- (l_value,)= struct.unpack(longlongformat, buffer)
- hash += l_value
- hash = hash & 0xFFFFFFFFFFFFFFFF #to remain as 64bit number
-
- if data.seek(max(0,filesize-65536),1) != True:
- return SEEK_ERROR, 0
-
- for x in range(65536/bytesize):
- buffer = data.read(bytesize)
- (l_value,)= struct.unpack(longlongformat, buffer)
- hash += l_value
- hash = hash & 0xFFFFFFFFFFFFFFFF
-
- data.close()
- returnedhash = "%016x" % hash
- return returnedhash, filesize
+def hashFile (name):
+ """ FIXME Need to handle exceptions !! """
+
+ longlongformat = 'q' # long long
+ bytesize = struct.calcsize (longlongformat)
+
+ fp = gio.File (name)
+
+ file_info = fp.query_info ('standard::size', 0)
+ filesize = file_info.get_attribute_uint64 ('standard::size')
+
+ hash = filesize
+
+ if filesize < 65536 * 2:
+ return SIZE_ERROR, 0
+
+ data = fp.read ()
+
+ if data.can_seek () != True:
+ return SEEK_ERROR, 0
+
+ for x in range (65536/bytesize):
+ buffer = data.read (bytesize)
+ (l_value,) = struct.unpack (longlongformat, buffer)
+ hash += l_value
+ hash = hash & 0xFFFFFFFFFFFFFFFF #to remain as 64bit number
+
+ if data.seek (max (0, filesize-65536), 1) != True:
+ return SEEK_ERROR, 0
+
+ for x in range (65536/bytesize):
+ buffer = data.read (bytesize)
+ (l_value,) = struct.unpack (longlongformat, buffer)
+ hash += l_value
+ hash = hash & 0xFFFFFFFFFFFFFFFF
+
+ data.close ()
+ returnedhash = "%016x" % hash
+ return returnedhash, filesize
diff --git a/src/plugins/opensubtitles/opensubtitles.py b/src/plugins/opensubtitles/opensubtitles.py
index 1c4edb8..63281c4 100644
--- a/src/plugins/opensubtitles/opensubtitles.py
+++ b/src/plugins/opensubtitles/opensubtitles.py
@@ -7,7 +7,7 @@ from gi.repository import Gio
from gi.repository import Pango
from gi.repository import Totem
import gobject
-gobject.threads_init()
+gobject.threads_init ()
import xmlrpclib
import threading
import xdg.BaseDirectory
@@ -16,7 +16,7 @@ import gettext
from hash import hashFile
-gettext.textdomain("totem")
+gettext.textdomain ("totem")
D_ = gettext.dgettext
_ = gettext.gettext
@@ -26,197 +26,201 @@ OK200 = '200 OK'
TOTEM_REMOTE_COMMAND_REPLACE = 14
SUBTITLES_EXT = [
- "asc",
- "txt",
- "sub",
- "srt",
- "smi",
- "ssa",
- "ass",
+ "asc",
+ "txt",
+ "sub",
+ "srt",
+ "smi",
+ "ssa",
+ "ass",
]
-# Map of the language codes used by opensubtitles.org's API to their human-readable name
-LANGUAGES_STR = [(D_('iso_639_3', 'Albanian'), 'sq'),
- (D_('iso_639_3', 'Arabic'), 'ar'),
- (D_('iso_639_3', 'Armenian'), 'hy'),
- (D_('iso_639_3', 'Neo-Aramaic, Assyrian'), 'ay'),
- (D_('iso_639_3', 'Bosnian'), 'bs'),
- (_('Brasilian Portuguese'), 'pb'),
- (D_('iso_639_3', 'Bulgarian'), 'bg'),
- (D_('iso_639_3', 'Catalan'), 'ca'),
- (D_('iso_639_3', 'Chinese'), 'zh'),
- (D_('iso_639_3', 'Croatian'), 'hr'),
- (D_('iso_639_3', 'Czech'), 'cs'),
- (D_('iso_639_3', 'Danish'), 'da'),
- (D_('iso_639_3', 'Dutch'), 'nl'),
- (D_('iso_639_3', 'English'), 'en'),
- (D_('iso_639_3', 'Esperanto'), 'eo'),
- (D_('iso_639_3', 'Estonian'), 'et'),
- (D_('iso_639_3', 'Finnish'), 'fi'),
- (D_('iso_639_3', 'French'), 'fr'),
- (D_('iso_639_3', 'Galician'), 'gl'),
- (D_('iso_639_3', 'Georgian'), 'ka'),
- (D_('iso_639_3', 'German'), 'de'),
- (D_('iso_639_3', 'Greek, Modern (1453-)'), 'el'),
- (D_('iso_639_3', 'Hebrew'), 'he'),
- (D_('iso_639_3', 'Hindi'), 'hi'),
- (D_('iso_639_3', 'Hungarian'), 'hu'),
- (D_('iso_639_3', 'Icelandic'), 'is'),
- (D_('iso_639_3', 'Indonesian'), 'id'),
- (D_('iso_639_3', 'Italian'), 'it'),
- (D_('iso_639_3', 'Japanese'), 'ja'),
- (D_('iso_639_3', 'Kazakh'), 'kk'),
- (D_('iso_639_3', 'Korean'), 'ko'),
- (D_('iso_639_3', 'Latvian'), 'lv'),
- (D_('iso_639_3', 'Lithuanian'), 'lt'),
- (D_('iso_639_3', 'Luxembourgish'), 'lb'),
- (D_('iso_639_3', 'Macedonian'), 'mk'),
- (D_('iso_639_3', 'Malay (macrolanguage)'), 'ms'),
- (D_('iso_639_3', 'Norwegian'), 'no'),
- (D_('iso_639_3', 'Occitan (post 1500)'), 'oc'),
- (D_('iso_639_3', 'Persian'), 'fa'),
- (D_('iso_639_3', 'Polish'), 'pl'),
- (D_('iso_639_3', 'Portuguese'), 'pt'),
- (D_('iso_639_3', 'Romanian'), 'ro'),
- (D_('iso_639_3', 'Russian'), 'ru'),
- (D_('iso_639_3', 'Serbian'), 'sr'),
- (D_('iso_639_3', 'Slovak'), 'sk'),
- (D_('iso_639_3', 'Slovenian'), 'sl'),
- (D_('iso_639_3', 'Spanish'), 'es'),
- (D_('iso_639_3', 'Swedish'), 'sv'),
- (D_('iso_639_3', 'Thai'), 'th'),
- (D_('iso_639_3', 'Turkish'), 'tr'),
- (D_('iso_639_3', 'Ukrainian'), 'uk'),
- (D_('iso_639_3', 'Vietnamese'), 'vi'),]
+# Map of the language codes used by opensubtitles.org's API to their
+# human-readable name
+LANGUAGES_STR = [ (D_('iso_639_3', 'Albanian'), 'sq'),
+ (D_('iso_639_3', 'Arabic'), 'ar'),
+ (D_('iso_639_3', 'Armenian'), 'hy'),
+ (D_('iso_639_3', 'Neo-Aramaic, Assyrian'), 'ay'),
+ (D_('iso_639_3', 'Bosnian'), 'bs'),
+ (_('Brasilian Portuguese'), 'pb'),
+ (D_('iso_639_3', 'Bulgarian'), 'bg'),
+ (D_('iso_639_3', 'Catalan'), 'ca'),
+ (D_('iso_639_3', 'Chinese'), 'zh'),
+ (D_('iso_639_3', 'Croatian'), 'hr'),
+ (D_('iso_639_3', 'Czech'), 'cs'),
+ (D_('iso_639_3', 'Danish'), 'da'),
+ (D_('iso_639_3', 'Dutch'), 'nl'),
+ (D_('iso_639_3', 'English'), 'en'),
+ (D_('iso_639_3', 'Esperanto'), 'eo'),
+ (D_('iso_639_3', 'Estonian'), 'et'),
+ (D_('iso_639_3', 'Finnish'), 'fi'),
+ (D_('iso_639_3', 'French'), 'fr'),
+ (D_('iso_639_3', 'Galician'), 'gl'),
+ (D_('iso_639_3', 'Georgian'), 'ka'),
+ (D_('iso_639_3', 'German'), 'de'),
+ (D_('iso_639_3', 'Greek, Modern (1453-)'), 'el'),
+ (D_('iso_639_3', 'Hebrew'), 'he'),
+ (D_('iso_639_3', 'Hindi'), 'hi'),
+ (D_('iso_639_3', 'Hungarian'), 'hu'),
+ (D_('iso_639_3', 'Icelandic'), 'is'),
+ (D_('iso_639_3', 'Indonesian'), 'id'),
+ (D_('iso_639_3', 'Italian'), 'it'),
+ (D_('iso_639_3', 'Japanese'), 'ja'),
+ (D_('iso_639_3', 'Kazakh'), 'kk'),
+ (D_('iso_639_3', 'Korean'), 'ko'),
+ (D_('iso_639_3', 'Latvian'), 'lv'),
+ (D_('iso_639_3', 'Lithuanian'), 'lt'),
+ (D_('iso_639_3', 'Luxembourgish'), 'lb'),
+ (D_('iso_639_3', 'Macedonian'), 'mk'),
+ (D_('iso_639_3', 'Malay (macrolanguage)'), 'ms'),
+ (D_('iso_639_3', 'Norwegian'), 'no'),
+ (D_('iso_639_3', 'Occitan (post 1500)'), 'oc'),
+ (D_('iso_639_3', 'Persian'), 'fa'),
+ (D_('iso_639_3', 'Polish'), 'pl'),
+ (D_('iso_639_3', 'Portuguese'), 'pt'),
+ (D_('iso_639_3', 'Romanian'), 'ro'),
+ (D_('iso_639_3', 'Russian'), 'ru'),
+ (D_('iso_639_3', 'Serbian'), 'sr'),
+ (D_('iso_639_3', 'Slovak'), 'sk'),
+ (D_('iso_639_3', 'Slovenian'), 'sl'),
+ (D_('iso_639_3', 'Spanish'), 'es'),
+ (D_('iso_639_3', 'Swedish'), 'sv'),
+ (D_('iso_639_3', 'Thai'), 'th'),
+ (D_('iso_639_3', 'Turkish'), 'tr'),
+ (D_('iso_639_3', 'Ukrainian'), 'uk'),
+ (D_('iso_639_3', 'Vietnamese'), 'vi'),]
# Map of ISO 639-1 language codes to the codes used by opensubtitles.org's API
-LANGUAGES = {'sq':'alb',
- 'ar':'ara',
- 'hy':'arm',
- 'ay':'ass',
- 'bs':'bos',
- 'pb':'pob',
- 'bg':'bul',
- 'ca':'cat',
- 'zh':'chi',
- 'hr':'hrv',
- 'cs':'cze',
- 'da':'dan',
- 'nl':'dut',
- 'en':'eng',
- 'eo':'epo',
- 'et':'est',
- 'fi':'fin',
- 'fr':'fre',
- 'gl':'glg',
- 'ka':'geo',
- 'de':'ger',
- 'el':'ell',
- 'he':'heb',
- 'hi':'hin',
- 'hu':'hun',
- 'is':'ice',
- 'id':'ind',
- 'it':'ita',
- 'ja':'jpn',
- 'kk':'kaz',
- 'ko':'kor',
- 'lv':'lav',
- 'lt':'lit',
- 'lb':'ltz',
- 'mk':'mac',
- 'ms':'may',
- 'no':'nor',
- 'oc':'oci',
- 'fa':'per',
- 'pl':'pol',
- 'pt':'por',
- 'ro':'rum',
- 'ru':'rus',
- 'sr':'scc',
- 'sk':'slo',
- 'sl':'slv',
- 'es':'spa',
- 'sv':'swe',
- 'th':'tha',
- 'tr':'tur',
- 'uk':'ukr',
- 'vi':'vie',}
-
-class SearchThread(threading.Thread):
+LANGUAGES = {'sq':'alb',
+ 'ar':'ara',
+ 'hy':'arm',
+ 'ay':'ass',
+ 'bs':'bos',
+ 'pb':'pob',
+ 'bg':'bul',
+ 'ca':'cat',
+ 'zh':'chi',
+ 'hr':'hrv',
+ 'cs':'cze',
+ 'da':'dan',
+ 'nl':'dut',
+ 'en':'eng',
+ 'eo':'epo',
+ 'et':'est',
+ 'fi':'fin',
+ 'fr':'fre',
+ 'gl':'glg',
+ 'ka':'geo',
+ 'de':'ger',
+ 'el':'ell',
+ 'he':'heb',
+ 'hi':'hin',
+ 'hu':'hun',
+ 'is':'ice',
+ 'id':'ind',
+ 'it':'ita',
+ 'ja':'jpn',
+ 'kk':'kaz',
+ 'ko':'kor',
+ 'lv':'lav',
+ 'lt':'lit',
+ 'lb':'ltz',
+ 'mk':'mac',
+ 'ms':'may',
+ 'no':'nor',
+ 'oc':'oci',
+ 'fa':'per',
+ 'pl':'pol',
+ 'pt':'por',
+ 'ro':'rum',
+ 'ru':'rus',
+ 'sr':'scc',
+ 'sk':'slo',
+ 'sl':'slv',
+ 'es':'spa',
+ 'sv':'swe',
+ 'th':'tha',
+ 'tr':'tur',
+ 'uk':'ukr',
+ 'vi':'vie',}
+
+class SearchThread (threading.Thread):
"""
This is the thread started when the dialog is searching for subtitles
"""
- def __init__(self, model):
+ def __init__ (self, model):
self.model = model
self._done = False
- self._lock = threading.Lock()
- threading.Thread.__init__(self)
+ self._lock = threading.Lock ()
+ threading.Thread.__init__ (self)
- def run(self):
- self.model.lock.acquire(True)
- self.model.results = self.model.os_search_subtitles()
- self.model.lock.release()
+ def run (self):
+ self.model.lock.acquire (True)
+ self.model.results = self.model.os_search_subtitles ()
+ self.model.lock.release ()
self._done = True
-
+
@property
- def done(self):
+ def done (self):
""" Thread-safe property to know whether the query is done or not """
- self._lock.acquire(True)
+ self._lock.acquire (True)
res = self._done
- self._lock.release()
+ self._lock.release ()
return res
-class DownloadThread(threading.Thread):
+class DownloadThread (threading.Thread):
"""
This is the thread started when the dialog is downloading the subtitles.
"""
- def __init__(self, model, subtitle_id):
+ def __init__ (self, model, subtitle_id):
self.model = model
self.subtitle_id = subtitle_id
self._done = False
- self._lock = threading.Lock()
- threading.Thread.__init__(self)
+ self._lock = threading.Lock ()
+ threading.Thread.__init__ (self)
+
+ def run (self):
+ model = self.model
+
+ model.lock.acquire (True)
+ model.subtitles = model.os_download_subtitles (self.subtitle_id)
+ model.lock.release ()
- def run(self):
- self.model.lock.acquire(True)
- self.model.subtitles = self.model.os_download_subtitles(self.subtitle_id)
- self.model.lock.release()
self._done = True
-
+
@property
- def done(self):
+ def done (self):
""" Thread-safe property to know whether the query is done or not """
- self._lock.acquire(True)
+ self._lock.acquire (True)
res = self._done
- self._lock.release()
+ self._lock.release ()
return res
# OpenSubtitles.org API abstraction
-class OpenSubtitlesModel(object):
+class OpenSubtitlesModel (object):
"""
This contains the logic of the opensubtitles service.
"""
- def __init__(self, server):
+ def __init__ (self, server):
self.server = server
self.token = None
try:
import locale
- self.lang = LANGUAGES[locale.getlocale()[0].split('_')[0]]
+ self.lang = LANGUAGES[locale.getlocale ()[0].split ('_')[0]]
except:
self.lang = 'eng'
self.hash = None
self.size = 0
- self.lock = threading.Lock()
+ self.lock = threading.Lock ()
self.results = []
self.subtitles = ''
self.message = ''
- def os_login(self, username='', password=''):
+ def os_login (self, username='', password=''):
"""
Logs into the opensubtitles web service and gets a valid token for
the comming comunications. If we are already logged it only checks
@@ -230,17 +234,18 @@ class OpenSubtitlesModel(object):
if self.token:
# We have already logged-in before, check the connection
try:
- result = self.server.NoOperation(self.token)
+ result = self.server.NoOperation (self.token)
except:
pass
if result and result['status'] != OK200:
return True
try:
- result = self.server.LogIn(username, password, self.lang, USER_AGENT)
+ result = self.server.LogIn (username, password, self.lang,
+ USER_AGENT)
except:
pass
- if result and result.get('status') == OK200:
- self.token = result.get('token')
+ if result and result.get ('status') == OK200:
+ self.token = result.get ('token')
if self.token:
return True
@@ -248,421 +253,448 @@ class OpenSubtitlesModel(object):
return False
- def os_search_subtitles(self):
+ def os_search_subtitles (self):
"""
"""
self.message = ''
- if self.os_login():
- searchdata = {'sublanguageid': self.lang,
- 'moviehash' : self.hash,
- 'moviebytesize': str(self.size)}
+ if self.os_login ():
+ searchdata = {'sublanguageid': self.lang,
+ 'moviehash' : self.hash,
+ 'moviebytesize': str (self.size)}
try:
- result = self.server.SearchSubtitles(self.token, [searchdata])
+ result = self.server.SearchSubtitles (self.token, [searchdata])
except xmlrpclib.ProtocolError:
self.message = _(u'Could not contact the OpenSubtitles website')
- if result.get('data'):
+ if result.get ('data'):
return result['data']
else:
self.message = _(u'No results found')
return None
- def os_download_subtitles(self, subtitleId):
+ def os_download_subtitles (self, subtitleId):
"""
"""
self.message = ''
- if self.os_login():
+ error_message = _(u'Could not contact the OpenSubtitles website')
+
+ if self.os_login ():
try:
- result = self.server.DownloadSubtitles(self.token, [subtitleId])
+ result = self.server.DownloadSubtitles (self.token,
+ [subtitleId])
except xmlrpclib.ProtocolError:
- self.message = _(u'Could not contact the OpenSubtitles website')
+ self.message = error_message
- if result and result.get('status') == OK200:
+ if result and result.get ('status') == OK200:
try:
subtitle64 = result['data'][0]['data']
except:
- self.message = _(u'Could not contact the OpenSubtitles website')
+ self.message = error_message
return None
import StringIO, gzip, base64
- subtitleDecoded = base64.decodestring(subtitle64)
- subtitleGzipped = StringIO.StringIO(subtitleDecoded)
- subtitleGzippedFile = gzip.GzipFile(fileobj=subtitleGzipped)
+ subtitleDecoded = base64.decodestring (subtitle64)
+ subtitleGzipped = StringIO.StringIO (subtitleDecoded)
+ subtitleGzippedFile = gzip.GzipFile (fileobj=subtitleGzipped)
- return subtitleGzippedFile.read()
+ return subtitleGzippedFile.read ()
return None
-class OpenSubtitles(gobject.GObject, Peas.Activatable):
+class OpenSubtitles (gobject.GObject, Peas.Activatable):
__gtype_name__ = 'OpenSubtitles'
- object = gobject.property(type = gobject.GObject)
+ object = gobject.property (type = gobject.GObject)
- def __init__(self):
+ def __init__ (self):
self.dialog = None
self.totem = None
- self.settings = Gio.Settings.new ('org.gnome.totem.plugins.opensubtitles')
+ schema = 'org.gnome.totem.plugins.opensubtitles'
+ self.settings = Gio.Settings.new (schema)
# totem.Plugin methods
- def do_activate(self):
+ def do_activate (self):
"""
Called when the plugin is activated.
- Here the sidebar page is initialized(set up the treeview, connect
+ Here the sidebar page is initialized (set up the treeview, connect
the callbacks, ...) and added to totem.
"""
self.totem = self.object
- self.filename = None
+ self.filename = None
- self.manager = self.totem.get_ui_manager()
- self.os_append_menu()
+ self.manager = self.totem.get_ui_manager ()
+ self.os_append_menu ()
- self.totem.connect('file-opened', self.on_totem__file_opened)
- self.totem.connect('file-closed', self.on_totem__file_closed)
+ self.totem.connect ('file-opened', self.on_totem__file_opened)
+ self.totem.connect ('file-closed', self.on_totem__file_closed)
- # Obtain the ServerProxy and init the model
- server = xmlrpclib.Server('http://api.opensubtitles.org/xml-rpc')
- self.model = OpenSubtitlesModel(server)
+ # Obtain the ServerProxy and init the model
+ server = xmlrpclib.Server ('http://api.opensubtitles.org/xml-rpc')
+ self.model = OpenSubtitlesModel (server)
- def do_deactivate(self):
+ def do_deactivate (self):
if self.dialog:
- self.dialog.destroy()
- self.dialog = None
-
- self.os_delete_menu()
+ self.dialog.destroy ()
+ self.dialog = None
+
+ self.os_delete_menu ()
# UI related code
- def os_build_dialog(self, action):
- builder = Totem.plugin_load_interface ("opensubtitles", "opensubtitles.ui", True, self.totem.get_main_window (), self)
+ def os_build_dialog (self, action):
+ builder = Totem.plugin_load_interface ("opensubtitles",
+ "opensubtitles.ui", True,
+ self.totem.get_main_window (),
+ self)
# Obtain all the widgets we need to initialize
- combobox = builder.get_object('language_combobox')
- languages = builder.get_object('language_model')
- self.progress = builder.get_object('progress_bar')
- self.treeview = builder.get_object('subtitle_treeview')
- self.liststore = builder.get_object('subtitle_model')
- self.dialog = builder.get_object('subtitles_dialog')
- self.find_button = builder.get_object('find_button')
- self.apply_button = builder.get_object('apply_button')
- self.close_button = builder.get_object('close_button')
+ combobox = builder.get_object ('language_combobox')
+ languages = builder.get_object ('language_model')
+ self.progress = builder.get_object ('progress_bar')
+ self.treeview = builder.get_object ('subtitle_treeview')
+ self.liststore = builder.get_object ('subtitle_model')
+ self.dialog = builder.get_object ('subtitles_dialog')
+ self.find_button = builder.get_object ('find_button')
+ self.apply_button = builder.get_object ('apply_button')
+ self.close_button = builder.get_object ('close_button')
# Set up and populate the languages combobox
- renderer = Gtk.CellRendererText()
+ renderer = Gtk.CellRendererText ()
sorted_languages = Gtk.TreeModelSort (model = languages)
- sorted_languages.set_sort_column_id(0, Gtk.SortType.ASCENDING)
- combobox.set_model(sorted_languages)
- combobox.pack_start(renderer, True)
- combobox.add_attribute(renderer, 'text', 0)
+ sorted_languages.set_sort_column_id (0, Gtk.SortType.ASCENDING)
+ combobox.set_model (sorted_languages)
+ combobox.pack_start (renderer, True)
+ combobox.add_attribute (renderer, 'text', 0)
lang = self.settings.get_string ('language')
if lang is not None:
self.model.lang = lang
for lang in LANGUAGES_STR:
- it = languages.append(lang)
+ it = languages.append (lang)
if LANGUAGES[lang[1]] == self.model.lang:
- (success, parentit) = sorted_languages.convert_child_iter_to_iter (it)
+ (success,
+ parentit) = sorted_languages.convert_child_iter_to_iter (it)
if success:
combobox.set_active_iter (parentit)
- # Set up the results treeview
- renderer = Gtk.CellRendererText()
- self.treeview.set_model(self.liststore)
- self.treeview.set_headers_visible(False)
- renderer.set_property('ellipsize', Pango.EllipsizeMode.END)
- column = Gtk.TreeViewColumn(_(u"Subtitles"), renderer, text=0)
- column.set_resizable(True)
- column.set_expand(True)
- self.treeview.append_column(column)
- # translators comment:
- # This is the file-type of the subtitle file detected
- column = Gtk.TreeViewColumn(_(u"Format"), renderer, text=1)
- column.set_resizable(False)
- self.treeview.append_column(column)
- # translators comment:
- # This is a rating of the quality of the subtitle
- column = Gtk.TreeViewColumn(_(u"Rating"), renderer, text=2)
- column.set_resizable(False)
- self.treeview.append_column(column)
-
- self.apply_button.set_sensitive(False)
-
- self.apply_button.connect('clicked', self.on_apply_clicked)
- self.find_button.connect('clicked', self.on_find_clicked)
- self.close_button.connect('clicked', self.on_close_clicked)
-
- # Set up signals
-
- combobox_changed_id = combobox.connect('changed', self.on_combobox__changed)
- self.dialog.connect ('delete-event', self.dialog.hide_on_delete)
- self.dialog.set_transient_for (self.totem.get_main_window())
- self.dialog.set_position (Gtk.WindowPosition.CENTER_ON_PARENT)
-
- # Connect the callbacks
- self.dialog.connect ('key-press-event', self.on_window__key_press_event)
- self.treeview.get_selection().connect('changed', self.on_treeview__row_change)
- self.treeview.connect('row-activated', self.on_treeview__row_activate)
-
- def os_show_dialog(self, action):
+ # Set up the results treeview
+ renderer = Gtk.CellRendererText ()
+ self.treeview.set_model (self.liststore)
+ self.treeview.set_headers_visible (False)
+ renderer.set_property ('ellipsize', Pango.EllipsizeMode.END)
+ column = Gtk.TreeViewColumn (_(u"Subtitles"), renderer, text=0)
+ column.set_resizable (True)
+ column.set_expand (True)
+ self.treeview.append_column (column)
+ # translators comment:
+ # This is the file-type of the subtitle file detected
+ column = Gtk.TreeViewColumn (_(u"Format"), renderer, text=1)
+ column.set_resizable (False)
+ self.treeview.append_column (column)
+ # translators comment:
+ # This is a rating of the quality of the subtitle
+ column = Gtk.TreeViewColumn (_(u"Rating"), renderer, text=2)
+ column.set_resizable (False)
+ self.treeview.append_column (column)
+
+ self.apply_button.set_sensitive (False)
+
+ self.apply_button.connect ('clicked', self.on_apply_clicked)
+ self.find_button.connect ('clicked', self.on_find_clicked)
+ self.close_button.connect ('clicked', self.on_close_clicked)
+
+ # Set up signals
+
+ combobox_changed_id = combobox.connect ('changed',
+ self.on_combobox__changed)
+ self.dialog.connect ('delete-event', self.dialog.hide_on_delete)
+ self.dialog.set_transient_for (self.totem.get_main_window ())
+ self.dialog.set_position (Gtk.WindowPosition.CENTER_ON_PARENT)
+
+ # Connect the callbacks
+ self.dialog.connect ('key-press-event', self.on_window__key_press_event)
+ self.treeview.get_selection ().connect ('changed',
+ self.on_treeview__row_change)
+ self.treeview.connect ('row-activated', self.on_treeview__row_activate)
+
+ def os_show_dialog (self, action):
if not self.dialog:
- self.os_build_dialog(action)
+ self.os_build_dialog (action)
- filename = self.totem.get_current_mrl()
+ filename = self.totem.get_current_mrl ()
if not self.model.results or filename != self.filename:
self.filename = filename
- self.dialog.show_all()
+ self.dialog.show_all ()
- self.progress.set_fraction(0.0)
+ self.progress.set_fraction (0.0)
- def os_append_menu(self):
+ def os_append_menu (self):
"""
"""
-
- self.os_action_group = Gtk.ActionGroup(name='OpenSubtitles')
- self.action = Gtk.Action(name='opensubtitles',
+ self.os_action_group = Gtk.ActionGroup (name='OpenSubtitles')
+
+ tooltip_text = _(u"Download movie subtitles from OpenSubtitles")
+ self.action = Gtk.Action (name='opensubtitles',
label=_(u'_Download Movie Subtitlesâ?¦'),
- tooltip=_(u"Download movie subtitles from OpenSubtitles"),
+ tooltip=tooltip_text,
stock_id=None)
- self.os_action_group.add_action(self.action)
+ self.os_action_group.add_action (self.action)
- self.manager.insert_action_group(self.os_action_group, 0)
+ self.manager.insert_action_group (self.os_action_group, 0)
- self.menu_id = self.manager.new_merge_id()
- self.manager.add_ui(self.menu_id,
- '/tmw-menubar/view/subtitles/subtitle-download-placeholder',
+ self.menu_id = self.manager.new_merge_id ()
+ path = '/tmw-menubar/view/subtitles/subtitle-download-placeholder'
+ self.manager.add_ui (self.menu_id,
+ path,
'opensubtitles',
'opensubtitles',
Gtk.UIManagerItemType.MENUITEM,
False
)
- self.action.set_visible(True)
+ self.action.set_visible (True)
- self.manager.ensure_update()
+ self.manager.ensure_update ()
- self.action.connect('activate', self.os_show_dialog)
+ self.action.connect ('activate', self.os_show_dialog)
- self.action.set_sensitive(self.totem.is_playing() and
- self.os_check_allowed_scheme() and
- not self.os_check_is_audio())
+ self.action.set_sensitive (self.totem.is_playing () and
+ self.os_check_allowed_scheme () and
+ not self.os_check_is_audio ())
- def os_check_allowed_scheme(self):
- scheme = Gio.file_new_for_uri(self.totem.get_current_mrl()).get_uri_scheme()
- if scheme == 'dvd' or scheme == 'http' or scheme == 'dvb' or scheme == 'vcd':
+ def os_check_allowed_scheme (self):
+ current_file = Gio.file_new_for_uri (self.totem.get_current_mrl ())
+ scheme = current_file.get_uri_scheme ()
+
+ if (scheme == 'dvd' or scheme == 'http' or
+ scheme == 'dvb' or scheme == 'vcd'):
return False
+
return True
- def os_check_is_audio(self):
+ def os_check_is_audio (self):
# FIXME need to use something else here
- # I think we must use video widget metadata but I don't found a way
- # to get this info from python
- filename = self.totem.get_current_mrl()
- if Gio.content_type_guess(filename, '')[0].split('/')[0] == 'audio':
+ # I think we must use video widget metadata but I don't found a way
+ # to get this info from python
+ filename = self.totem.get_current_mrl ()
+ if Gio.content_type_guess (filename, '')[0].split ('/')[0] == 'audio':
return True
return False
- def os_delete_menu(self):
- self.manager.remove_action_group(self.os_action_group)
- self.manager.remove_ui(self.menu_id)
+ def os_delete_menu (self):
+ self.manager.remove_action_group (self.os_action_group)
+ self.manager.remove_ui (self.menu_id)
- def os_get_results(self):
+ def os_get_results (self):
"""
"""
- self.liststore.clear()
- self.treeview.set_headers_visible(False)
+ self.liststore.clear ()
+ self.treeview.set_headers_visible (False)
self.model.results = []
- self.apply_button.set_sensitive(False)
- self.find_button.set_sensitive(False)
+ self.apply_button.set_sensitive (False)
+ self.find_button.set_sensitive (False)
- self.dialog.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH))
+ cursor = Gdk.Cursor.new (Gdk.CursorType.WATCH)
+ self.dialog.get_window ().set_cursor (cursor)
- thread = SearchThread(self.model)
- thread.start()
- gobject.idle_add(self.os_populate_treeview)
+ thread = SearchThread (self.model)
+ thread.start ()
+ gobject.idle_add (self.os_populate_treeview)
- self.progress.set_text(_(u'Searching subtitlesâ?¦'))
- gobject.timeout_add(350, self.os_progress_bar_increment, thread)
+ self.progress.set_text (_(u'Searching subtitlesâ?¦'))
+ gobject.timeout_add (350, self.os_progress_bar_increment, thread)
- def os_populate_treeview(self):
+ def os_populate_treeview (self):
"""
"""
- if self.model.lock.acquire(False) == False:
+ if self.model.lock.acquire (False) == False:
return True
if self.model.results:
- self.apply_button.set_sensitive(True)
+ self.apply_button.set_sensitive (True)
for subData in self.model.results:
- if not SUBTITLES_EXT.count(subData['SubFormat']):
- continue
- self.liststore.append([subData['SubFileName'], subData['SubFormat'], subData['SubRating'], subData['IDSubtitleFile'],])
- self.treeview.set_headers_visible(True)
+ if not SUBTITLES_EXT.count (subData['SubFormat']):
+ continue
+ self.liststore.append ([subData['SubFileName'],
+ subData['SubFormat'],
+ subData['SubRating'],
+ subData['IDSubtitleFile'],])
+ self.treeview.set_headers_visible (True)
else:
- self.apply_button.set_sensitive(False)
+ self.apply_button.set_sensitive (False)
- self.model.lock.release()
+ self.model.lock.release ()
- self.dialog.get_window().set_cursor(None)
+ self.dialog.get_window ().set_cursor (None)
return False
- def os_save_selected_subtitle(self, filename=None):
+ def os_save_selected_subtitle (self, filename=None):
"""
"""
- self.dialog.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH))
+ cursor = Gdk.Cursor.new (Gdk.CursorType.WATCH)
+ self.dialog.get_window ().set_cursor (cursor)
- model, rows = self.treeview.get_selection().get_selected_rows()
+ model, rows = self.treeview.get_selection ().get_selected_rows ()
if rows:
- iter = model.get_iter(rows[0])
- subtitle_id = model.get_value(iter, 3)
- subtitle_format = model.get_value(iter, 1)
+ iter = model.get_iter (rows[0])
+ subtitle_id = model.get_value (iter, 3)
+ subtitle_format = model.get_value (iter, 1)
gfile = None
if not filename:
- directory = Gio.file_new_for_path(xdg.BaseDirectory.xdg_cache_home + sep + 'totem' + sep + 'subtitles' + sep)
- if not directory.query_exists(None):
- if not path.exists (xdg.BaseDirectory.xdg_cache_home + sep + 'totem' + sep):
- mkdir (xdg.BaseDirectory.xdg_cache_home + sep + 'totem' + sep)
- if not path.exists (xdg.BaseDirectory.xdg_cache_home + sep + 'totem' + sep + 'subtitles' + sep):
- mkdir (xdg.BaseDirectory.xdg_cache_home + sep + 'totem' + sep + 'subtitles' + sep)
- # FIXME: We can't use this function until we depend on GLib (PyGObject) 2.18
- # directory.make_directory_with_parents()
-
- file = Gio.file_new_for_path(self.filename)
- movie_name = file.get_basename().rpartition('.')[0]
- filename = directory.get_uri() + sep + movie_name + '.' + subtitle_format
+ bpath = xdg.BaseDirectory.xdg_cache_home + sep
+ bpath += 'totem' + sep
+
+ directory = Gio.file_new_for_path (bpath + 'subtitles' + sep)
+
+ if not directory.query_exists (None):
+ if not path.exists (bpath):
+ mkdir (bpath)
+ if not path.exists (bpath + 'subtitles' + sep):
+ mkdir (bpath + 'subtitles' + sep)
+ # FIXME: We can't use this function until we depend on
+ # GLib (PyGObject) 2.18
+ # directory.make_directory_with_parents ()
+
+ file = Gio.file_new_for_path (self.filename)
+ movie_name = file.get_basename ().rpartition ('.')[0]
+
+ filename = directory.get_uri () + sep
+ filename += movie_name + '.' + subtitle_format
self.model.subtitles = ''
- thread = DownloadThread(self.model, subtitle_id)
- thread.start()
- gobject.idle_add(self.os_save_subtitles, filename)
+ thread = DownloadThread (self.model, subtitle_id)
+ thread.start ()
+ gobject.idle_add (self.os_save_subtitles, filename)
- self.progress.set_text(_(u'Downloading the subtitlesâ?¦'))
- gobject.timeout_add(350, self.os_progress_bar_increment, thread)
+ self.progress.set_text (_(u'Downloading the subtitlesâ?¦'))
+ gobject.timeout_add (350, self.os_progress_bar_increment, thread)
else:
#warn user!
pass
- def os_save_subtitles(self, filename):
- if self.model.lock.acquire(False) == False:
+ def os_save_subtitles (self, filename):
+ if self.model.lock.acquire (False) == False:
return True
if self.model.subtitles:
- # Delete all previous cached subtitle for this file
+ # Delete all previous cached subtitle for this file
for ext in SUBTITLES_EXT:
- fp = Gio.file_new_for_path(filename[:-3] + ext)
- if fp.query_exists(None):
- fp.delete(None)
+ fp = Gio.file_new_for_path (filename[:-3] + ext)
+ if fp.query_exists (None):
+ fp.delete (None)
fp = Gio.file_new_for_uri (filename)
suburi = fp.get_uri ()
- subFile = fp.replace('', False)
- subFile.write(self.model.subtitles)
- subFile.close()
+ subFile = fp.replace ('', False)
+ subFile.write (self.model.subtitles)
+ subFile.close ()
- self.model.lock.release()
+ self.model.lock.release ()
- self.dialog.get_window().set_cursor(None)
+ self.dialog.get_window ().set_cursor (None)
self.on_close_clicked (None)
if suburi:
- self.totem.set_current_subtitle(suburi)
+ self.totem.set_current_subtitle (suburi)
return False
- def os_progress_bar_increment(self, thread):
-
+ def os_progress_bar_increment (self, thread):
if not thread.done:
- self.progress.pulse()
+ self.progress.pulse ()
return True
if self.model.message:
- self.progress.set_text(self.model.message)
+ self.progress.set_text (self.model.message)
else:
- self.progress.set_text('')
-
- self.progress.set_fraction(0.0)
- self.find_button.set_sensitive(True)
- self.apply_button.set_sensitive(False)
- self.treeview.set_sensitive(True)
+ self.progress.set_text ('')
+
+ self.progress.set_fraction (0.0)
+ self.find_button.set_sensitive (True)
+ self.apply_button.set_sensitive (False)
+ self.treeview.set_sensitive (True)
return False
- def os_download_and_apply(self):
- self.apply_button.set_sensitive(False)
- self.find_button.set_sensitive(False)
- self.action.set_sensitive(False)
- self.treeview.set_sensitive(False)
- self.os_save_selected_subtitle()
+ def os_download_and_apply (self):
+ self.apply_button.set_sensitive (False)
+ self.find_button.set_sensitive (False)
+ self.action.set_sensitive (False)
+ self.treeview.set_sensitive (False)
+ self.os_save_selected_subtitle ()
# Callbacks
- def on_window__key_press_event(self, widget, event):
+ def on_window__key_press_event (self, widget, event):
if event.keyval == Gdk.KEY_Escape:
- self.dialog.destroy()
+ self.dialog.destroy ()
self.dialog = None
return True
return False
- def on_treeview__row_change(self, selection):
- if selection.count_selected_rows() > 0:
- self.apply_button.set_sensitive(True)
+ def on_treeview__row_change (self, selection):
+ if selection.count_selected_rows () > 0:
+ self.apply_button.set_sensitive (True)
else:
- self.apply_button.set_sensitive(False)
+ self.apply_button.set_sensitive (False)
- def on_treeview__row_activate(self, path, column, data):
- self.os_download_and_apply()
+ def on_treeview__row_activate (self, path, column, data):
+ self.os_download_and_apply ()
- def on_totem__file_opened(self, totem, filename):
+ def on_totem__file_opened (self, totem, filename):
"""
"""
# Check if allows subtitles
- if self.os_check_allowed_scheme() and not self.os_check_is_audio():
- self.action.set_sensitive(True)
- if self.dialog:
- self.find_button.set_sensitive(True)
- self.filename = self.totem.get_current_mrl()
- self.liststore.clear()
- self.treeview.set_headers_visible(False)
- self.apply_button.set_sensitive(False)
- self.results = []
- else:
- self.action.set_sensitive(False)
- if self.dialog and self.dialog.is_active():
- self.liststore.clear()
- self.treeview.set_headers_visible(False)
- self.apply_button.set_sensitive(False)
- self.find_button.set_sensitive(False)
-
- def on_totem__file_closed(self, totem):
- self.action.set_sensitive(False)
+ if self.os_check_allowed_scheme () and not self.os_check_is_audio ():
+ self.action.set_sensitive (True)
+ if self.dialog:
+ self.find_button.set_sensitive (True)
+ self.filename = self.totem.get_current_mrl ()
+ self.liststore.clear ()
+ self.treeview.set_headers_visible (False)
+ self.apply_button.set_sensitive (False)
+ self.results = []
+ else:
+ self.action.set_sensitive (False)
+ if self.dialog and self.dialog.is_active ():
+ self.liststore.clear ()
+ self.treeview.set_headers_visible (False)
+ self.apply_button.set_sensitive (False)
+ self.find_button.set_sensitive (False)
+
+ def on_totem__file_closed (self, totem):
+ self.action.set_sensitive (False)
if self.dialog:
- self.apply_button.set_sensitive(False)
- self.find_button.set_sensitive(False)
+ self.apply_button.set_sensitive (False)
+ self.find_button.set_sensitive (False)
- def on_combobox__changed(self, combobox):
- iter = combobox.get_active_iter()
- self.model.lang = LANGUAGES[combobox.get_model().get_value(iter, 1)]
- self.settings.set_string('language', self.model.lang)
+ def on_combobox__changed (self, combobox):
+ iter = combobox.get_active_iter ()
+ self.model.lang = LANGUAGES[combobox.get_model ().get_value (iter, 1)]
+ self.settings.set_string ('language', self.model.lang)
- def on_close_clicked(self, data):
- self.dialog.destroy()
+ def on_close_clicked (self, data):
+ self.dialog.destroy ()
self.dialog = None
- def on_apply_clicked(self, data):
- self.os_download_and_apply()
+ def on_apply_clicked (self, data):
+ self.os_download_and_apply ()
- def on_find_clicked(self, data):
- self.apply_button.set_sensitive(False)
- self.find_button.set_sensitive(False)
- self.filename = self.totem.get_current_mrl()
- self.model.hash , self.model.size = hashFile(self.filename)
+ def on_find_clicked (self, data):
+ self.apply_button.set_sensitive (False)
+ self.find_button.set_sensitive (False)
+ self.filename = self.totem.get_current_mrl ()
+ self.model.hash , self.model.size = hashFile (self.filename)
- self.os_get_results()
+ self.os_get_results ()
diff --git a/src/plugins/pythonconsole/pythonconsole.py b/src/plugins/pythonconsole/pythonconsole.py
index 6e6b286..a16244e 100644
--- a/src/plugins/pythonconsole/pythonconsole.py
+++ b/src/plugins/pythonconsole/pythonconsole.py
@@ -8,7 +8,7 @@
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
-#
+#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
@@ -18,7 +18,8 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-# Parts from "Interactive Python-GTK Console" (stolen from epiphany's console.py)
+# Parts from "Interactive Python-GTK Console" (stolen from epiphany's
+# console.py)
# Copyright (C), 1998 James Henstridge <james daa com au>
# Copyright (C), 2005 Adam Hooper <adamh densi com>
# Bits from gedit Python Console Plugin
@@ -42,13 +43,13 @@ from gi.repository import Totem
from gi.repository import Gio
import gobject
try:
- import rpdb2
- have_rpdb2 = True
+ import rpdb2
+ have_rpdb2 = True
except:
- have_rpdb2 = False
+ have_rpdb2 = False
import gettext
-gettext.textdomain("totem")
+gettext.textdomain ("totem")
D_ = gettext.dgettext
_ = gettext.gettext
@@ -66,92 +67,104 @@ ui_str = """
</ui>
"""
-class PythonConsolePlugin(gobject.GObject, Peas.Activatable):
- __gtype_name__ = 'PythonConsolePlugin'
-
- object = gobject.property(type = gobject.GObject)
-
- def __init__(self):
- self.totem = None
- self.window = None
-
- def do_activate(self):
- self.totem = self.object
-
- data = dict()
- manager = self.totem.get_ui_manager()
-
- data['action_group'] = Gtk.ActionGroup(name = 'Python')
-
- action = Gtk.Action(name = 'Python', label = 'Python', tooltip = _(u'Python Console Menu'), stock_id = None)
- data['action_group'].add_action(action)
-
- action = Gtk.Action(name = 'PythonConsole', label = _(u'_Python Console'),
- tooltip = _(u"Show Totem's Python console"),
- stock_id = 'gnome-mime-text-x-python')
- action.connect('activate', self.show_console)
- data['action_group'].add_action(action)
-
- action = Gtk.Action(name = 'PythonDebugger', label = _(u'Python Debugger'),
- tooltip = _(u"Enable remote Python debugging with rpdb2"), stock_id = None)
- if have_rpdb2:
- action.connect('activate', self.enable_debugging)
- else:
- action.set_visible(False)
- data['action_group'].add_action(action)
-
- manager.insert_action_group(data['action_group'], 0)
- data['ui_id'] = manager.add_ui_from_string(ui_str)
- manager.ensure_update()
-
- self.totem.set_data('PythonConsolePluginInfo', data)
-
- def show_console(self, action):
- if not self.window:
- console = PythonConsole(namespace = {'__builtins__' : __builtins__,
- 'Totem' : Totem,
- 'totem_object' : self.totem},
- destroy_cb = self.destroy_console)
-
- console.set_size_request(600, 400)
- console.eval('print "%s" %% totem_object' % _(u"You can access the Totem.Object through " \
- "\'totem_object\' :\\n%s"), False)
-
- self.window = Gtk.Window()
- self.window.set_title(_(u'Totem Python Console'))
- self.window.add(console)
- self.window.connect('destroy', self.destroy_console)
- self.window.show_all()
- else:
- self.window.show_all()
- self.window.grab_focus()
-
- def enable_debugging(self, action):
- msg = _(u"After you press OK, Totem will wait until you connect to it with winpdb or rpdb2. If you have not set a debugger password in DConf, it will use the default password ('totem').")
- dialog = Gtk.MessageDialog(None, 0, Gtk.MessageType.INFO, Gtk.ButtonsType.OK_CANCEL, msg)
- if dialog.run() == Gtk.ResponseType.OK:
- settings = Gio.Settings.new ('org.gnome.totem.plugins.pythonconsole')
- password = settings.get_string('rpdb2-password') or "totem"
- def start_debugger(password):
- rpdb2.start_embedded_debugger(password)
- return False
-
- gobject.idle_add(start_debugger, password)
- dialog.destroy()
-
- def destroy_console(self, *args):
- self.window.destroy()
- self.window = None
-
- def do_deactivate(self):
- data = self.totem.get_data('PythonConsolePluginInfo')
-
- manager = self.totem.get_ui_manager()
- manager.remove_ui(data['ui_id'])
- manager.remove_action_group(data['action_group'])
- manager.ensure_update()
-
- self.totem.set_data('PythonConsolePluginInfo', None)
-
- if self.window is not None:
- self.window.destroy()
+class PythonConsolePlugin (gobject.GObject, Peas.Activatable):
+ __gtype_name__ = 'PythonConsolePlugin'
+
+ object = gobject.property (type = gobject.GObject)
+
+ def __init__ (self):
+ self.totem = None
+ self.window = None
+
+ def do_activate (self):
+ self.totem = self.object
+
+ data = dict ()
+ manager = self.totem.get_ui_manager ()
+
+ data['action_group'] = Gtk.ActionGroup (name = 'Python')
+
+ action = Gtk.Action (name = 'Python', label = 'Python',
+ tooltip = _(u'Python Console Menu'),
+ stock_id = None)
+ data['action_group'].add_action (action)
+
+ action = Gtk.Action (name = 'PythonConsole',
+ label = _(u'_Python Console'),
+ tooltip = _(u"Show Totem's Python console"),
+ stock_id = 'gnome-mime-text-x-python')
+ action.connect ('activate', self.show_console)
+ data['action_group'].add_action (action)
+
+ action = Gtk.Action (name = 'PythonDebugger',
+ label = _(u'Python Debugger'),
+ tooltip = _(u"Enable remote Python debugging "\
+ "with rpdb2"),
+ stock_id = None)
+ if have_rpdb2:
+ action.connect ('activate', self.enable_debugging)
+ else:
+ action.set_visible (False)
+ data['action_group'].add_action (action)
+
+ manager.insert_action_group (data['action_group'], 0)
+ data['ui_id'] = manager.add_ui_from_string (ui_str)
+ manager.ensure_update ()
+
+ self.totem.set_data ('PythonConsolePluginInfo', data)
+
+ def show_console (self, action):
+ if not self.window:
+ console = PythonConsole (namespace = {
+ '__builtins__' : __builtins__,
+ 'Totem' : Totem,
+ 'totem_object' : self.totem
+ }, destroy_cb = self.destroy_console)
+
+ console.set_size_request (600, 400)
+ console.eval ('print "%s" %% totem_object' % _(u"You can access "\
+ "the Totem.Object through \'totem_object\' :\\n%s"), False)
+
+ self.window = Gtk.Window ()
+ self.window.set_title (_(u'Totem Python Console'))
+ self.window.add (console)
+ self.window.connect ('destroy', self.destroy_console)
+ self.window.show_all ()
+ else:
+ self.window.show_all ()
+ self.window.grab_focus ()
+
+ def enable_debugging (self, action):
+ msg = _(u"After you press OK, Totem will wait until you connect to it "\
+ "with winpdb or rpdb2. If you have not set a debugger "\
+ "password in DConf, it will use the default password "\
+ "('totem').")
+ dialog = Gtk.MessageDialog (None, 0, Gtk.MessageType.INFO,
+ Gtk.ButtonsType.OK_CANCEL, msg)
+ if dialog.run () == Gtk.ResponseType.OK:
+ schema = 'org.gnome.totem.plugins.pythonconsole'
+ settings = Gio.Settings.new (schema)
+ password = settings.get_string ('rpdb2-password') or "totem"
+ def start_debugger (password):
+ rpdb2.start_embedded_debugger (password)
+ return False
+
+ gobject.idle_add (start_debugger, password)
+ dialog.destroy ()
+
+ def destroy_console (self, *args):
+ self.window.destroy ()
+ self.window = None
+
+ def do_deactivate (self):
+ data = self.totem.get_data ('PythonConsolePluginInfo')
+
+ manager = self.totem.get_ui_manager ()
+ manager.remove_ui (data['ui_id'])
+ manager.remove_action_group (data['action_group'])
+ manager.ensure_update ()
+
+ self.totem.set_data ('PythonConsolePluginInfo', None)
+
+ if self.window is not None:
+ self.window.destroy ()
diff --git a/src/plugins/sample-python/sample-python.py b/src/plugins/sample-python/sample-python.py
index 3956cf8..025c13d 100644
--- a/src/plugins/sample-python/sample-python.py
+++ b/src/plugins/sample-python/sample-python.py
@@ -4,14 +4,14 @@ import gobject
from gi.repository import Peas
from gi.repository import Totem
-class SamplePython(gobject.GObject, Peas.Activatable):
- __gtype_name__ = 'SamplePython'
+class SamplePython (gobject.GObject, Peas.Activatable):
+ __gtype_name__ = 'SamplePython'
- object = gobject.property(type = gobject.GObject)
+ object = gobject.property (type = gobject.GObject)
- def do_activate(self):
- print "Activating sample Python plugin"
- self.object.action_fullscreen_toggle()
-
- def do_deactivate(self):
- print "Deactivating sample Python plugin"
+ def do_activate (self):
+ print "Activating sample Python plugin"
+ self.object.action_fullscreen_toggle ()
+
+ def do_deactivate (self):
+ print "Deactivating sample Python plugin"
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]