first cut of opml input support ("subscribe to channels from web")

git-svn-id: svn://svn.berlios.de/gpodder/trunk@120 b0d088ad-0a06-0410-aad2-9ed5178a7e87
This commit is contained in:
Thomas Perl 2006-06-13 21:00:31 +00:00
parent 37119822d7
commit 4bea57b125
7 changed files with 479 additions and 14 deletions

View File

@ -1,3 +1,10 @@
Tue, 13 Jun 2006 22:38:17 +0200 <thp@perli.net>
* Added libopmlreader, first cut of OPML input support -- you
can now select from a list of channels and add them to your
local channel list
* Added config option "opml_url" to specify URL which will
be used as source for the OPML input support feature
Tue, 13 Jun 2006 20:20:07 +0200 <thp@perli.net>
* Removed ipod-dbus support (has now its extra branch in svn)
* Bumped version date + release date

View File

@ -41,7 +41,7 @@
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image336">
<widget class="GtkImage" id="image383">
<property name="visible">True</property>
<property name="stock">gtk-cdrom</property>
<property name="icon_size">1</property>
@ -63,7 +63,7 @@
<signal name="activate" handler="on_itemUpdate_activate" last_modification_time="Sat, 29 Oct 2005 11:28:10 GMT"/>
<child internal-child="image">
<widget class="GtkImage" id="image337">
<widget class="GtkImage" id="image384">
<property name="visible">True</property>
<property name="stock">gtk-refresh</property>
<property name="icon_size">1</property>
@ -90,7 +90,7 @@
<signal name="activate" handler="on_sync_to_ipod_activate" last_modification_time="Wed, 05 Apr 2006 21:49:38 GMT"/>
<child internal-child="image">
<widget class="GtkImage" id="image338">
<widget class="GtkImage" id="image385">
<property name="visible">True</property>
<property name="stock">gtk-save</property>
<property name="icon_size">1</property>
@ -111,7 +111,7 @@
<signal name="activate" handler="on_cleanup_ipod_activate" last_modification_time="Wed, 05 Apr 2006 22:14:51 GMT"/>
<child internal-child="image">
<widget class="GtkImage" id="image339">
<widget class="GtkImage" id="image386">
<property name="visible">True</property>
<property name="stock">gtk-delete</property>
<property name="icon_size">1</property>
@ -138,7 +138,7 @@
<signal name="activate" handler="on_itemPreferences_activate" last_modification_time="Sat, 29 Oct 2005 11:29:01 GMT"/>
<child internal-child="image">
<widget class="GtkImage" id="image340">
<widget class="GtkImage" id="image387">
<property name="visible">True</property>
<property name="stock">gtk-preferences</property>
<property name="icon_size">1</property>
@ -165,7 +165,7 @@
<signal name="activate" handler="close_gpodder" last_modification_time="Sat, 29 Oct 2005 11:54:31 GMT"/>
<child internal-child="image">
<widget class="GtkImage" id="image341">
<widget class="GtkImage" id="image388">
<property name="visible">True</property>
<property name="stock">gtk-quit</property>
<property name="icon_size">1</property>
@ -189,7 +189,7 @@
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image342">
<widget class="GtkImage" id="image389">
<property name="visible">True</property>
<property name="stock">gtk-justify-left</property>
<property name="icon_size">1</property>
@ -211,7 +211,7 @@
<signal name="activate" handler="on_itemAddChannel_activate" last_modification_time="Sat, 29 Oct 2005 11:33:59 GMT"/>
<child internal-child="image">
<widget class="GtkImage" id="image343">
<widget class="GtkImage" id="image390">
<property name="visible">True</property>
<property name="stock">gtk-open</property>
<property name="icon_size">1</property>
@ -232,7 +232,7 @@
<signal name="activate" handler="on_itemEditChannel_activate" last_modification_time="Sat, 29 Oct 2005 11:34:38 GMT"/>
<child internal-child="image">
<widget class="GtkImage" id="image344">
<widget class="GtkImage" id="image391">
<property name="visible">True</property>
<property name="stock">gtk-edit</property>
<property name="icon_size">1</property>
@ -253,7 +253,7 @@
<signal name="activate" handler="on_itemRemoveChannel_activate" last_modification_time="Sat, 29 Oct 2005 11:35:16 GMT"/>
<child internal-child="image">
<widget class="GtkImage" id="image345">
<widget class="GtkImage" id="image392">
<property name="visible">True</property>
<property name="stock">gtk-delete</property>
<property name="icon_size">1</property>
@ -280,7 +280,7 @@
<signal name="activate" handler="on_itemExportChannels_activate" last_modification_time="Sat, 29 Oct 2005 11:37:45 GMT"/>
<child internal-child="image">
<widget class="GtkImage" id="image346">
<widget class="GtkImage" id="image393">
<property name="visible">True</property>
<property name="stock">gtk-save-as</property>
<property name="icon_size">1</property>
@ -292,6 +292,27 @@
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="itemImportChannels">
<property name="visible">True</property>
<property name="label" translatable="yes">_Import from web</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_itemImportChannels_activate" last_modification_time="Tue, 13 Jun 2006 19:39:42 GMT"/>
<child internal-child="image">
<widget class="GtkImage" id="image394">
<property name="visible">True</property>
<property name="stock">gtk-redo</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
</widget>
</child>
</widget>
@ -304,7 +325,7 @@
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image347">
<widget class="GtkImage" id="image395">
<property name="visible">True</property>
<property name="stock">gtk-help</property>
<property name="icon_size">1</property>
@ -326,7 +347,7 @@
<signal name="activate" handler="on_itemAbout_activate" last_modification_time="Sat, 29 Oct 2005 11:40:19 GMT"/>
<child internal-child="image">
<widget class="GtkImage" id="image348">
<widget class="GtkImage" id="image396">
<property name="visible">True</property>
<property name="stock">gtk-about</property>
<property name="icon_size">1</property>
@ -1725,7 +1746,7 @@
<widget class="GtkTable" id="table2">
<property name="border_width">10</property>
<property name="visible">True</property>
<property name="n_rows">4</property>
<property name="n_rows">5</property>
<property name="n_columns">2</property>
<property name="homogeneous">False</property>
<property name="row_spacing">5</property>
@ -1893,6 +1914,83 @@
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label41">
<property name="visible">True</property>
<property name="label" translatable="yes">Autostart:</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="left_attach">0</property>
<property name="right_attach">1</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="x_options">fill</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label42">
<property name="visible">True</property>
<property name="label" translatable="yes">Podcast directory:</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="left_attach">0</property>
<property name="right_attach">1</property>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
<property name="x_options">fill</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="opmlURL">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="editable">True</property>
<property name="visibility">True</property>
<property name="max_length">0</property>
<property name="text" translatable="yes"></property>
<property name="has_frame">True</property>
<property name="invisible_char">*</property>
<property name="activates_default">False</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
<property name="y_options"></property>
</packing>
</child>
</widget>
<packing>
<property name="tab_expand">False</property>
@ -2876,4 +2974,116 @@ you can use the usual format of the environment variables:
</child>
</widget>
<widget class="GtkWindow" id="gPodderOpmlLister">
<property name="visible">True</property>
<property name="title" translatable="yes">Select channels to subscribe to</property>
<property name="type">GTK_WINDOW_TOPLEVEL</property>
<property name="window_position">GTK_WIN_POS_CENTER</property>
<property name="modal">False</property>
<property name="default_width">500</property>
<property name="default_height">300</property>
<property name="resizable">True</property>
<property name="destroy_with_parent">False</property>
<property name="decorated">True</property>
<property name="skip_taskbar_hint">False</property>
<property name="skip_pager_hint">False</property>
<property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
<property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
<property name="focus_on_map">True</property>
<property name="urgency_hint">False</property>
<signal name="destroy" handler="on_gPodderOpmlLister_destroy" last_modification_time="Tue, 13 Jun 2006 19:28:07 GMT"/>
<child>
<widget class="GtkVBox" id="vboxOPML">
<property name="border_width">10</property>
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">5</property>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow5">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
<child>
<widget class="GtkTreeView" id="treeviewChannelChooser">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="headers_visible">True</property>
<property name="rules_hint">False</property>
<property name="reorderable">False</property>
<property name="enable_search">True</property>
<property name="fixed_height_mode">False</property>
<property name="hover_selection">False</property>
<property name="hover_expand">False</property>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="hboxBottomButtons">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">5</property>
<child>
<widget class="GtkButton" id="btnOK">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label">gtk-ok</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_btnOK_clicked" last_modification_time="Tue, 13 Jun 2006 19:31:12 GMT"/>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="pack_type">GTK_PACK_END</property>
</packing>
</child>
<child>
<placeholder/>
</child>
<child>
<widget class="GtkButton" id="btnCancel">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label">gtk-cancel</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_btnCancel_clicked" last_modification_time="Tue, 13 Jun 2006 19:31:15 GMT"/>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="pack_type">GTK_PACK_END</property>
</packing>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">True</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

View File

@ -46,6 +46,7 @@ from libpodcasts import channelsToModel
from librssreader import rssReader
from libopmlwriter import opmlWriter
from libopmlreader import opmlReader
from libwget import downloadThread
from libwget import downloadStatusManager
@ -324,6 +325,11 @@ class Gpodder(SimpleGladeApp):
def add_new_channel( self, result = None):
if result != None and result != "" and (result[:4] == "http" or result[:3] == "ftp"):
for old_channel in self.channels:
if old_channel.url == result:
if libgpodder.isDebugging():
print 'channel already exists in my list :)'
return
if libgpodder.isDebugging():
print ("Will add channel :%s") % result
self.statusLabel.set_text( _("Fetching channel index..."))
@ -525,6 +531,18 @@ class Gpodder(SimpleGladeApp):
# end dlg.run()
#-- Gpodder.on_itemExportChannels_activate }
#-- Gpodder.on_itemImportChannels_activate {
def on_itemImportChannels_activate(self, widget, *args):
if libgpodder.isDebugging:
print "on_itemImportChannels_activate called with self.%s" % widget.get_name()
opml_lister = Gpodderopmllister()
gl = gPodderLib()
url = gl.opml_url
opml_lister.get_channels_from_url( url, self.add_new_channel)
#-- Gpodder.on_itemImportChannels_activate }
#-- Gpodder.on_itemAbout_activate {
def on_itemAbout_activate(self, widget, *args):
if libgpodder.isDebugging():
@ -814,6 +832,7 @@ class Gpodderproperties(SimpleGladeApp):
self.ftpProxy.set_text( gl.ftp_proxy)
self.openApp.set_text( gl.open_app)
self.iPodMountpoint.set_text( gl.ipod_mount)
self.opmlURL.set_text( gl.opml_url)
self.updateonstartup.set_active(gl.update_on_startup)
# the use proxy env vars check box
self.cbEnvironmentVariables.set_active( gl.proxy_use_environment)
@ -906,6 +925,7 @@ class Gpodderproperties(SimpleGladeApp):
gl.open_app = self.openApp.get_text()
gl.proxy_use_environment = self.cbEnvironmentVariables.get_active()
gl.ipod_mount = self.iPodMountpoint.get_text()
gl.opml_url = self.opmlURL.get_text()
gl.update_on_startup = self.updateonstartup.get_active()
gl.propertiesChanged()
# create or remove symlink to download dir on desktop
@ -1004,6 +1024,84 @@ class Gpoddersync(SimpleGladeApp):
#-- Gpoddersync.on_gPodderSync_destroy }
class Gpodderopmllister(SimpleGladeApp):
def __init__(self, path="gpodder.glade",
root="gPodderOpmlLister",
domain=app_name, **kwargs):
path = os.path.join(glade_dir, path)
SimpleGladeApp.__init__(self, path, root, domain, **kwargs)
#-- Gpodderopmllister.new {
def new(self):
if libgpodder.isDebugging():
print "A new %s has been created" % self.__class__.__name__
# initiate channels list
self.channels = []
self.callback_for_channel = None
togglecell = gtk.CellRendererToggle()
togglecell.set_property( 'activatable', True)
togglecell.connect( 'toggled', self.callback_edited)
togglecolumn = gtk.TreeViewColumn( _("Subscribe"), togglecell, active=0)
titlecell = gtk.CellRendererText()
titlecolumn = gtk.TreeViewColumn( _("Channel name"), titlecell, text=1)
for itemcolumn in ( togglecolumn, titlecolumn ):
self.treeviewChannelChooser.append_column( itemcolumn)
#-- Gpodderopmllister.new }
#-- Gpodderopmllister custom methods {
# Write your own methods here
def callback_edited( self, cell, path):
model = self.treeviewChannelChooser.get_model()
activated = not model[path][0]
url = model[path][2]
model[path][0] = activated
if activated:
self.channels.append( url)
else:
self.channels.remove( url)
if libgpodder.isDebugging():
print url
def get_channels_from_url( self, url, callback):
reader = opmlReader()
reader.parseXML( url)
self.treeviewChannelChooser.set_model( reader.get_model())
self.callback_for_channel = callback
#-- Gpodderopmllister custom methods }
#-- Gpodderopmllister.on_gPodderOpmlLister_destroy {
def on_gPodderOpmlLister_destroy(self, widget, *args):
if libgpodder.isDebugging():
print "on_gPodderOpmlLister_destroy called with self.%s" % widget.get_name()
#-- Gpodderopmllister.on_gPodderOpmlLister_destroy }
#-- Gpodderopmllister.on_btnOK_clicked {
def on_btnOK_clicked(self, widget, *args):
if libgpodder.isDebugging():
print "on_btnOK_clicked called with self.%s" % widget.get_name()
self.gPodderOpmlLister.destroy()
# add channels that have been selected
for url in self.channels:
if self.callback_for_channel != None:
self.callback_for_channel (url)
#-- Gpodderopmllister.on_btnOK_clicked }
#-- Gpodderopmllister.on_btnCancel_clicked {
def on_btnCancel_clicked(self, widget, *args):
if libgpodder.isDebugging():
print "on_btnCancel_clicked called with self.%s" % widget.get_name()
self.gPodderOpmlLister.destroy()
#-- Gpodderopmllister.on_btnCancel_clicked }
#-- main {
def main( __version__ = None):
@ -1018,6 +1116,7 @@ def main( __version__ = None):
#g_podder_properties = Gpodderproperties()
#g_podder_episode = Gpodderepisode()
#g_podder_sync = Gpoddersync()
#g_podder_opml_lister = Gpodderopmllister()
g_podder.set_icon()
g_podder.run()

View File

@ -85,6 +85,11 @@ class Gpodder(SimpleGladeApp):
print "on_itemExportChannels_activate called with self.%s" % widget.get_name()
#-- Gpodder.on_itemExportChannels_activate }
#-- Gpodder.on_itemImportChannels_activate {
def on_itemImportChannels_activate(self, widget, *args):
print "on_itemImportChannels_activate called with self.%s" % widget.get_name()
#-- Gpodder.on_itemImportChannels_activate }
#-- Gpodder.on_itemAbout_activate {
def on_itemAbout_activate(self, widget, *args):
print "on_itemAbout_activate called with self.%s" % widget.get_name()
@ -273,6 +278,39 @@ class Gpoddersync(SimpleGladeApp):
#-- Gpoddersync.on_gPodderSync_destroy }
class Gpodderopmllister(SimpleGladeApp):
def __init__(self, path="gpodder.glade",
root="gPodderOpmlLister",
domain=app_name, **kwargs):
path = os.path.join(glade_dir, path)
SimpleGladeApp.__init__(self, path, root, domain, **kwargs)
#-- Gpodderopmllister.new {
def new(self):
print "A new %s has been created" % self.__class__.__name__
#-- Gpodderopmllister.new }
#-- Gpodderopmllister custom methods {
# Write your own methods here
#-- Gpodderopmllister custom methods }
#-- Gpodderopmllister.on_gPodderOpmlLister_destroy {
def on_gPodderOpmlLister_destroy(self, widget, *args):
print "on_gPodderOpmlLister_destroy called with self.%s" % widget.get_name()
#-- Gpodderopmllister.on_gPodderOpmlLister_destroy }
#-- Gpodderopmllister.on_btnOK_clicked {
def on_btnOK_clicked(self, widget, *args):
print "on_btnOK_clicked called with self.%s" % widget.get_name()
#-- Gpodderopmllister.on_btnOK_clicked }
#-- Gpodderopmllister.on_btnCancel_clicked {
def on_btnCancel_clicked(self, widget, *args):
print "on_btnCancel_clicked called with self.%s" % widget.get_name()
#-- Gpodderopmllister.on_btnCancel_clicked }
#-- main {
def main():
@ -281,6 +319,7 @@ def main():
g_podder_properties = Gpodderproperties()
g_podder_episode = Gpodderepisode()
g_podder_sync = Gpoddersync()
g_podder_opml_lister = Gpodderopmllister()
g_podder.run()

View File

@ -69,6 +69,9 @@ globalLock = threading.RLock()
# my gpodderlib variable
g_podder_lib = None
# default url to use for opml directory on the web
default_opml_directory = 'http://share.opml.org/opml/topPodcasts.opml'
def isDebugging():
return debugging
@ -94,6 +97,7 @@ class gPodderLibClass( object):
proxy_use_environment = False
open_app = ""
ipod_mount = ""
opml_url = ""
desktop_link = _("gPodder downloads")
gpodderconf_section = 'gpodder-conf-1'
@ -149,6 +153,7 @@ class gPodderLibClass( object):
self.write_to_parser( parser, 'proxy_use_env', self.proxy_use_environment)
self.write_to_parser( parser, 'ipod_mount', self.ipod_mount)
self.write_to_parser( parser, 'update_on_startup', self.update_on_startup)
self.write_to_parser( parser, 'opml_url', self.opml_url)
fn = self.getConfigFilename()
fp = open( fn, "w")
parser.write( fp)
@ -199,6 +204,7 @@ class gPodderLibClass( object):
http = self.get_from_parser( parser, 'http_proxy')
ftp = self.get_from_parser( parser, 'ftp_proxy')
app = self.get_from_parser( parser, 'player', 'gnome-open')
opml_url = self.get_from_parser( parser, 'opml_url', default_opml_directory)
self.proxy_use_environment = self.get_boolean_from_parser( parser, 'proxy_use_env', True)
self.ipod_mount = self.get_from_parser( parser, 'ipod_mount', '/media/ipod/')
self.update_on_startup = self.get_boolean_from_parser(parser, 'update_on_startup', default=False)
@ -212,10 +218,15 @@ class gPodderLibClass( object):
self.open_app = strip( app)
else:
self.open_app = 'gnome-open'
if strip( opml_url) != '':
self.opml_url = strip( opml_url)
else:
self.opml_url = default_opml_directory
except:
# TODO: well, well.. (http + ftp?)
self.open_app = 'gnome-open'
self.ipod_mount = '/media/ipod/'
self.opml_url = default_opml_directory
if was_oldstyle:
self.saveConfig()

View File

@ -0,0 +1,92 @@
#
# gPodder (a media aggregator / podcast client)
# Copyright (C) 2005-2006 Thomas Perl <thp at perli.net>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, 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
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
#
#
# libopmlreader.py -- opml ("podcast list") reader functionality
# thomas perl <thp@perli.net> 20060613
#
#
import gtk
import gobject
import libgpodder
from xml.sax.saxutils import DefaultHandler
from xml.sax.handler import ErrorHandler
from xml.sax import make_parser
from string import strip
from libpodcasts import opmlChannel
from libpodcasts import stripHtml
from librssreader import rssErrorHandler
class opmlReader( DefaultHandler):
channels = []
title = 'Unknown OPML Channel'
current_item = None
current_element_data = ""
def __init__( self):
None
def get_model( self):
new_model = gtk.ListStore( gobject.TYPE_BOOLEAN, gobject.TYPE_STRING, gobject.TYPE_STRING)
for channel in self.channels:
new_iter = new_model.append()
new_model.set( new_iter, 0, False)
new_model.set( new_iter, 1, channel.title)
new_model.set( new_iter, 2, channel.xmlurl)
return new_model
def parseXML( self, filename):
self.channels = []
parser = make_parser()
parser.returns_unicode = True
parser.setContentHandler( self)
parser.setErrorHandler( rssErrorHandler())
# no multithreaded access to filename
libgpodder.getLock()
try:
parser.parse( filename)
finally:
libgpodder.releaseLock()
def startElement( self, name, attrs):
self.current_element_data = ""
if name == 'outline' and attrs.get( 'type', '???') == 'rss':
self.channels.append( opmlChannel( attrs.get( 'xmlUrl', ''), attrs.get( 'title', '') ))
def endElement( self, name):
if name == 'title':
self.title = self.current_element_data
def characters( self, ch):
self.current_element_data = self.current_element_data + ch

View File

@ -391,6 +391,13 @@ class podcastItem(object):
return '%d Bytes' % size
class opmlChannel(object):
def __init__( self, xmlurl, title = 'Unknown OPML Channel'):
self.title = title
self.xmlurl = xmlurl
def channelsToModel( channels):
new_model = gtk.ListStore( gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_OBJECT)