Rework podcast subscription in gPodder GUI

This currently blocks the UI, but allows for
a much cleaner and easier approach later on.
This commit is contained in:
Thomas Perl 2009-08-25 16:19:14 +02:00
parent dc08efdabc
commit 74bb593baf
7 changed files with 164 additions and 276 deletions

View file

@ -630,50 +630,6 @@
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">5</property>
<child>
<object class="GtkHBox" id="hboxAddChannel">
<property name="visible">False</property>
<property name="homogeneous">False</property>
<property name="spacing">5</property>
<child>
<object class="GtkEntry" id="entryAddChannel">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="max_length">0</property>
<property name="has_frame">True</property>
<property name="invisible_char">●</property>
<property name="activates_default">False</property>
<signal handler="on_btnAddChannel_clicked" name="activate"/>
<signal handler="on_entryAddChannel_changed" name="changed"/>
</object>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<object class="GtkButton" id="btnAddChannel">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label">gtk-add</property>
<property name="use_stock">True</property>
<property name="focus_on_click">True</property>
<signal handler="on_btnAddChannel_clicked" name="clicked"/>
</object>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
</object>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow" id="scrolledwindow6">
<property name="visible">True</property>

View file

@ -163,8 +163,6 @@ gPodderSettings = {
"gPodder. Useful for Rockbox players.")),
'mp3_player_max_filename_length': (int, 100,
_("The maximum filename length for FS-based devices.")),
'show_url_entry_in_podcast_list': (bool, False,
_("Whether or not to show the URL entry (add podcast) box in the main window.")),
'rockbox_copy_coverart' : (bool, False,
_("Create rockbox-compatible coverart and copy it to the device when "
"syncing. See: 'rockbox_coverart_size'.")),

View file

@ -30,8 +30,8 @@ class gPodderAddPodcast(BuilderWidget):
finger_friendly_widgets = ['btn_close', 'btn_add']
def new(self):
if not hasattr(self, 'url_callback'):
self.url_callback = None
if not hasattr(self, 'add_urls_callback'):
self.add_urls_callback = None
if hasattr(self, 'custom_label'):
self.label_add.set_text(self.custom_label)
if hasattr(self, 'custom_title'):
@ -68,6 +68,6 @@ class gPodderAddPodcast(BuilderWidget):
def on_btn_add_clicked(self, widget):
url = self.entry_url.get_text()
self.on_btn_close_clicked(widget)
if self.url_callback is not None:
self.url_callback(url)
if self.add_urls_callback is not None:
self.add_urls_callback([url])

View file

@ -176,7 +176,7 @@ class BuilderWidget(GtkBuilderWidget):
else:
raise Exception('Unknown interface type')
def UsernamePasswordDialog(self, title, message, username=None, password=None, username_prompt=_('Username'), register_callback=None):
def show_login_dialog(self, title, message, username=None, password=None, username_prompt=_('Username'), register_callback=None):
""" An authentication dialog based on
http://ardoris.wordpress.com/2008/07/05/pygtk-text-entry-dialog/ """

View file

@ -41,19 +41,18 @@ class gPodderPodcastDirectory(BuilderWidget):
(MODE_DOWNLOAD, MODE_SEARCH) = range(2)
def new(self):
# initiate channels list
self.channels = []
self.callback_for_channel = None
self.callback_finished = None
if hasattr(self, 'custom_title'):
self.gPodderPodcastDirectory.set_title(self.custom_title)
if hasattr(self, 'hide_url_entry'):
self.hboxOpmlUrlEntry.hide_all()
new_parent = self.notebookChannelAdder.get_parent()
new_parent.remove(self.notebookChannelAdder)
self.vboxOpmlImport.reparent(new_parent)
if not hasattr(self, 'add_urls_callback'):
self.add_urls_callback = None
self.setup_treeview(self.treeviewChannelChooser)
self.setup_treeview(self.treeviewTopPodcastsChooser)
self.setup_treeview(self.treeviewYouTubeChooser)
@ -77,16 +76,8 @@ class gPodderPodcastDirectory(BuilderWidget):
def callback_edited( self, cell, path):
model = self.get_treeview().get_model()
url = model[path][OpmlListModel.C_URL]
model[path][OpmlListModel.C_SELECTED] = not model[path][OpmlListModel.C_SELECTED]
if model[path][OpmlListModel.C_SELECTED]:
self.channels.append( url)
else:
self.channels.remove( url)
self.btnOK.set_sensitive( bool(len(self.get_selected_channels())))
self.btnOK.set_sensitive(bool(len(self.get_selected_channels())))
def on_entryURL_changed(self, editable):
old_mode = self.current_mode
@ -131,7 +122,6 @@ class gPodderPodcastDirectory(BuilderWidget):
tv = self.treeviewChannelChooser
self.btnDownloadOpml.set_sensitive(True)
self.entryURL.set_sensitive(True)
self.channels = []
tv.set_model(model)
tv.set_sensitive(True)
@ -155,18 +145,14 @@ class gPodderPodcastDirectory(BuilderWidget):
util.idle_add(self.thread_finished, model, tab)
def get_channels_from_url( self, url, callback_for_channel = None, callback_finished = None):
if callback_for_channel:
self.callback_for_channel = callback_for_channel
if callback_finished:
self.callback_finished = callback_finished
self.entryURL.set_text( url)
self.btnDownloadOpml.set_sensitive( False)
self.entryURL.set_sensitive( False)
self.btnOK.set_sensitive( False)
self.treeviewChannelChooser.set_sensitive( False)
threading.Thread( target = self.thread_func).start()
threading.Thread( target = lambda: self.thread_func(1)).start()
def download_opml_file(self, url):
self.entryURL.set_text(url)
self.btnDownloadOpml.set_sensitive(False)
self.entryURL.set_sensitive(False)
self.btnOK.set_sensitive(False)
self.treeviewChannelChooser.set_sensitive(False)
threading.Thread(target=self.thread_func).start()
threading.Thread(target=lambda: self.thread_func(1)).start()
def select_all( self, value ):
enabled = False
@ -182,7 +168,7 @@ class gPodderPodcastDirectory(BuilderWidget):
pass
def on_btnDownloadOpml_clicked(self, widget, *args):
self.get_channels_from_url( self.entryURL.get_text())
self.get_channels_from_url(self.entryURL.get_text())
def on_btnSearchYouTube_clicked(self, widget, *args):
self.entryYoutubeSearch.set_sensitive(False)
@ -197,16 +183,12 @@ class gPodderPodcastDirectory(BuilderWidget):
self.select_all(False)
def on_btnOK_clicked(self, widget, *args):
self.channels = self.get_selected_channels()
channel_urls = self.get_selected_channels()
self.gPodderPodcastDirectory.destroy()
# add channels that have been selected
for url in self.channels:
if self.callback_for_channel:
self.callback_for_channel( url)
if self.callback_finished:
util.idle_add(self.callback_finished)
if self.add_urls_callback is not None:
self.add_urls_callback(channel_urls)
def on_btnCancel_clicked(self, widget, *args):
self.gPodderPodcastDirectory.destroy()

View file

@ -292,6 +292,16 @@ class PodcastListModel(gtk.ListStore):
self.C_COVER, self._get_cover_image(channel))
self.update_by_iter(iter)
def get_path_from_url(self, url):
# Return the tree model path for a given URL
if url is None:
return None
for row in self:
if row[self.C_URL] == url:
return row.path
return None
def update_by_urls(self, urls):
# Given a list of URLs, update each matching row
for row in self:

View file

@ -246,18 +246,11 @@ class gPodder(BuilderWidget, dbus.service.Object):
# FIXME: Implement e-mail sending of list in win32
self.item_email_subscriptions.set_sensitive(False)
if self.config.show_url_entry_in_podcast_list:
self.hboxAddChannel.show()
if not gpodder.interface == gpodder.MAEMO and not self.config.show_toolbar:
self.toolbar.hide()
self.config.add_observer(self.on_config_changed)
self.default_entry_text_color = self.entryAddChannel.get_style().text[gtk.STATE_NORMAL]
self.entryAddChannel.connect('focus-in-event', self.entry_add_channel_focus)
self.entryAddChannel.connect('focus-out-event', self.entry_add_channel_unfocus)
self.entry_add_channel_unfocus(self.entryAddChannel, None)
self.uar = None
self.tray_icon = None
self.episode_shownotes_window = None
@ -736,16 +729,6 @@ class gPodder(BuilderWidget, dbus.service.Object):
# that's why we require the restart of gPodder in the message.
return False
def entry_add_channel_focus(self, widget, event):
widget.modify_text(gtk.STATE_NORMAL, self.default_entry_text_color)
if widget.get_text() == self.ENTER_URL_TEXT:
widget.set_text('')
def entry_add_channel_unfocus(self, widget, event):
if widget.get_text() == '':
widget.set_text(self.ENTER_URL_TEXT)
widget.modify_text(gtk.STATE_NORMAL, gtk.gdk.color_parse('#aaaaaa'))
def on_config_changed(self, name, old_value, new_value):
if name == 'show_toolbar' and gpodder.interface != gpodder.MAEMO:
if new_value:
@ -754,11 +737,6 @@ class gPodder(BuilderWidget, dbus.service.Object):
self.toolbar.hide()
elif name == 'episode_list_descriptions' and gpodder.interface != gpodder.MAEMO:
self.updateTreeView()
elif name == 'show_url_entry_in_podcast_list':
if new_value:
self.hboxAddChannel.show()
else:
self.hboxAddChannel.hide()
def read_apps(self):
time.sleep(3) # give other parts of gpodder a chance to start up
@ -1677,158 +1655,121 @@ class gPodder(BuilderWidget, dbus.service.Object):
if (rl.endswith('.jpg') or rl.endswith('.png') or rl.endswith('.gif') or rl.endswith('.svg')) and dnd_channel is not None:
self.cover_downloader.replace_cover(dnd_channel, result)
else:
self.add_new_channel(result)
self.add_podcast_list([result])
def add_new_channel(self, result=None, ask_download_new=True, quiet=False, block=False, authentication_tokens=None):
result = util.normalize_feed_url(result)
(scheme, rest) = result.split('://', 1)
def offer_new_episodes(self):
new_episodes = self.get_new_episodes()
if new_episodes:
self.new_episodes_show(new_episodes)
return True
return False
if not result:
cute_scheme = saxutils.escape(scheme)+'://'
title = _('%s URLs are not supported') % cute_scheme
message = _('gPodder does not understand the URL you supplied.')
def add_podcast_list(self, urls):
"""Subscribe to a list of podcast given their URLs"""
# Sort and split the URL list into three buckets
queued, failed, existing = [], [], []
for input_url in urls:
url = util.normalize_feed_url(input_url)
if url is None:
# Fail this one because the URL is not valid
failed.append(input_url)
elif self.podcast_list_model.get_path_from_url(url) is not None:
# A podcast already exists in the list for this URL
existing.append(url)
else:
# This URL has survived the first round - queue for add
queued.append(url)
# After the initial sorting and splitting, try all queued podcasts
for url in queued:
log('QUEUE RUNNER: %s', url, sender=self)
channel = self._add_new_channel(url)
if channel is None:
failed.append(url)
else:
self.channels.append(channel)
self.channel_list_changed = True
# Report already-existing subscriptions to the user
if existing:
title = _('Existing subscriptions skipped')
message = _('You are already subscribed to these podcasts:') \
+ '\n\n' + '\n'.join(saxutils.escape(url) for url in existing)
self.show_message(message, title, widget=self.treeChannels)
# Report failed subscriptions to the user
if failed:
title = _('Could not add some podcasts')
message = _('Some podcasts could not be added to your list:') \
+ '\n\n' + '\n'.join(saxutils.escape(url) for url in failed)
self.show_message(message, title, important=True)
return
for old_channel in self.channels:
if old_channel.url == result:
log( 'Channel already exists: %s', result)
# Select the existing channel in combo box
for i in range( len( self.channels)):
if self.channels[i] == old_channel:
self.treeChannels.get_selection().select_path( (i,))
self.on_treeChannels_cursor_changed(self.treeChannels)
break
self.show_message(_('You have already subscribed to this podcast: %s') % (
saxutils.escape( old_channel.title), ), _('Already added'), widget=self.treeChannels)
return
# If at least one podcast has been added, save and update all
if self.channel_list_changed:
self.save_channels_opml()
waitdlg = gtk.MessageDialog(self.gPodder, 0, gtk.MESSAGE_INFO, gtk.BUTTONS_NONE)
waitdlg.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
waitdlg.set_title(_('Downloading episode list'))
waitdlg.set_markup('<b><big>%s</big></b>' % waitdlg.get_title())
waitdlg.format_secondary_text(_('Downloading episode information for %s') % result)
waitpb = gtk.ProgressBar()
if block:
waitdlg.vbox.add(waitpb)
waitdlg.show_all()
waitdlg.set_response_sensitive(gtk.RESPONSE_CANCEL, False)
# Update the list of subscribed podcasts
self.update_feed_cache(force_update=False)
self.update_podcasts_tab()
self.entryAddChannel.set_text(_('Downloading feed...'))
self.entryAddChannel.set_sensitive(False)
self.btnAddChannel.set_sensitive(False)
args = (result, self.add_new_channel_finish, authentication_tokens, ask_download_new, quiet, waitdlg)
thread = threading.Thread( target=self.add_new_channel_proc, args=args )
thread.start()
# If only one podcast was added, select it
if len(urls) == 1:
path = self.podcast_list_model.get_path_from_url(urls[0])
if path is not None:
selection = self.treeChannels.get_selection()
selection.select_path(path)
self.on_treeChannels_cursor_changed(self.treeChannels)
while block and thread.isAlive():
while gtk.events_pending():
gtk.main_iteration( False)
waitpb.pulse()
time.sleep(0.1)
# Offer to download new episodes
self.offer_new_episodes()
def add_new_channel_proc( self, url, callback, authentication_tokens, *callback_args):
log( 'Adding new channel: %s', url)
channel = error = None
def _add_new_channel(self, url, authentication_tokens=None):
# The URL is valid and does not exist already - subscribe!
try:
channel = PodcastChannel.load(self.db, url=url, create=True,
authentication_tokens=authentication_tokens,
max_episodes=self.config.max_episodes_per_feed,
channel = PodcastChannel.load(self.db, url=url, create=True, \
authentication_tokens=authentication_tokens, \
max_episodes=self.config.max_episodes_per_feed, \
download_dir=self.config.download_dir)
except feedcore.AuthenticationRequired, e:
error = e
except feedcore.WifiLogin, e:
error = e
except feedcore.AuthenticationRequired:
title = _('Feed requires authentication')
message = _('Please enter your username and password.')
success, auth_tokens = self.show_login_dialog(title, message)
if success:
return self._add_new_channel(url, \
authentication_tokens=auth_tokens)
except feedcore.WifiLogin, error:
title = _('Website redirection detected')
message = _('The URL you are trying to add redirects to %s.') \
+ _('Do you want to visit the website now?')
message = message % saxutils.escape(error.data)
if self.show_confirmation(message, title):
util.open_website(error.data)
return None
except Exception, e:
log('Error adding channel: %s', e, traceback=True, sender=self)
self.show_message(saxutils.escape(str(e)), \
_('Cannot subscribe to podcast'), important=True)
log('Subscription error: %s', e, traceback=True, sender=self)
return None
util.idle_add( callback, channel, url, error, *callback_args )
try:
username, password = util.username_password_from_url(url)
except ValueError, ve:
username, password = (None, None)
if username is not None and channel.username is None and \
password is not None and channel.password is None:
channel.username = username
channel.password = password
channel.save()
self._update_cover(channel)
return channel
def save_channels_opml(self):
exporter = opml.Exporter(gpodder.subscription_file)
return exporter.write(self.channels)
def add_new_channel_finish( self, channel, url, error, ask_download_new, quiet, waitdlg):
if channel is not None:
self.channels.append( channel)
self.channel_list_changed = True
self.save_channels_opml()
if not quiet:
# download changed channels and select the new episode in the UI afterwards
self.update_feed_cache(force_update=False, select_url_afterwards=channel.url)
try:
(username, password) = util.username_password_from_url(url)
except ValueError, ve:
self.show_message(_('The following error occured while trying to get authentication data from the URL:') + '\n\n' + ve.message, _('Error getting authentication data'), important=True)
(username, password) = (None, None)
log('Error getting authentication data from URL: %s', url, traceback=True)
if username and self.show_confirmation( _('You have supplied <b>%s</b> as username and a password for this feed. Would you like to use the same authentication data for downloading episodes?') % ( saxutils.escape( username), ), _('Password authentication')):
channel.username = username
channel.password = password
log('Saving authentication data for episode downloads..', sender = self)
channel.save()
# We need to update the channel list otherwise the authentication
# data won't show up in the channel editor.
# TODO: Only updated the newly added feed to save some cpu cycles
self.channels = PodcastChannel.load_from_db(self.db, self.config.download_dir)
self.channel_list_changed = True
if ask_download_new:
new_episodes = channel.get_new_episodes(downloading=self.episode_is_downloading)
if len(new_episodes):
self.new_episodes_show(new_episodes)
elif isinstance(error, feedcore.AuthenticationRequired):
response, auth_tokens = self.UsernamePasswordDialog(
_('Feed requires authentication'), _('Please enter your username and password.'))
if response:
self.add_new_channel( url, authentication_tokens=auth_tokens )
elif isinstance(error, feedcore.WifiLogin):
if self.show_confirmation(_('The URL you are trying to add redirects to the website %s. Do you want to visit the website to login now?') % saxutils.escape(error.data), _('Website redirection detected')):
util.open_website(error.data)
if self.show_confirmation(_('Please login to the website now. Should I retry subscribing to the podcast at %s?') % saxutils.escape(url), _('Retry adding channel')):
self.add_new_channel(url)
else:
# Ok, the URL is not a channel, or there is some other
# error - let's see if it's a web page or OPML file...
handled = False
try:
data = urllib2.urlopen(url).read().lower()
if '</opml>' in data:
# This looks like an OPML feed
self.on_item_import_from_file_activate(None, url)
handled = True
elif '</html>' in data:
# This looks like a web page
title = _('The URL is a website')
message = _('The URL you specified points to a web page. You need to find the "feed" URL of the podcast to add to gPodder. Do you want to visit this website now and look for the podcast feed URL?\n\n(Hint: Look for "XML feed", "RSS feed" or "Podcast feed" if you are unsure for what to look. If there is only an iTunes URL, try adding this one.)')
if self.show_confirmation(message, title):
util.open_website(url)
handled = True
except Exception, e:
log('Error trying to handle the URL as OPML or web page: %s', e, sender=self)
if not handled:
title = _('Error adding podcast')
message = _('The podcast could not be added. Please check the spelling of the URL or try again later.')
self.show_message(message, title, important=True)
self.entryAddChannel.set_text(self.ENTER_URL_TEXT)
self.entryAddChannel.set_sensitive(True)
self.btnAddChannel.set_sensitive(True)
self.update_podcasts_tab()
self._update_cover(channel)
waitdlg.destroy()
def update_feed_cache_finish_callback(self, updated_urls=None, select_url_afterwards=None):
self.db.commit()
self.updating_feed_cache = False
@ -2249,12 +2190,9 @@ class gPodder(BuilderWidget, dbus.service.Object):
_config=self.config)
def on_itemDownloadAllNew_activate(self, widget, *args):
new_episodes = self.get_new_episodes()
if len(new_episodes):
self.new_episodes_show(new_episodes)
else:
self.show_message(_('No new episodes available for download. Check for new episodes later.'), \
_('No new episodes'), widget=self.btnUpdateFeeds)
if not self.offer_new_episodes():
self.show_message(_('Please check for new episodes later.'), \
_('No new episodes available'), widget=self.btnUpdateFeeds)
def get_new_episodes(self, channels=None):
if channels is None:
@ -2524,22 +2462,28 @@ class gPodder(BuilderWidget, dbus.service.Object):
gPodderDependencyManager(self.gPodder)
def on_add_new_google_search(self, widget, *args):
def add_google_video_search(query):
self.add_new_channel('http://video.google.com/videofeed?type=search&q='+urllib.quote(query)+'&so=1&num=250&output=rss')
def add_google_video_search(urls):
assert len(urls) == 1
query = urls[0]
self.add_podcast_list(['http://video.google.com/videofeed?type=search&q='+urllib.quote(query)+'&so=1&num=250&output=rss'])
gPodderAddPodcast(self.gPodder, url_callback=add_google_video_search, custom_title=_('Add Google Video search'), custom_label=_('Search for:'))
gPodderAddPodcast(self.gPodder, add_urls_callback=add_google_video_search, custom_title=_('Add Google Video search'), custom_label=_('Search for:'))
def on_upgrade_from_videocenter(self, widget):
from gpodder import nokiavideocenter
vc = nokiavideocenter.UpgradeFromVideocenter()
if vc.db2opml():
gPodderPodcastDirectory(self.gPodder, _config=self.config, custom_title=_('Import podcasts from Video Center'), hide_url_entry=True).get_channels_from_url(vc.opmlfile, lambda url: self.add_new_channel(url,False,block=True), lambda: self.on_itemDownloadAllNew_activate(self.gPodder))
dir = gPodderPodcastDirectory(self.gPodder, _config=self.config, \
custom_title=_('Import podcasts from Video Center'), \
add_urls_callback=self.add_podcast_list, \
hide_url_entry=True)
dir.download_opml_file(vc.opmlfile)
else:
self.show_message(_('Have you installed Video Center on your tablet?'), _('Cannot find Video Center subscriptions'), important=True)
def require_my_gpodder_authentication(self):
if not self.config.my_gpodder_username or not self.config.my_gpodder_password:
success, authentication = self.UsernamePasswordDialog(_('Login to my.gpodder.org'), _('Please enter your e-mail address and your password.'), username=self.config.my_gpodder_username, password=self.config.my_gpodder_password, username_prompt=_('E-Mail Address'), register_callback=lambda: util.open_website('http://my.gpodder.org/register'))
success, authentication = self.show_login_dialog(_('Login to my.gpodder.org'), _('Please enter your e-mail address and your password.'), username=self.config.my_gpodder_username, password=self.config.my_gpodder_password, username_prompt=_('E-Mail Address'), register_callback=lambda: util.open_website('http://my.gpodder.org/register'))
if success and authentication[0] and authentication[1]:
self.config.my_gpodder_username, self.config.my_gpodder_password = authentication
return True
@ -2563,15 +2507,15 @@ class gPodder(BuilderWidget, dbus.service.Object):
fp.close()
(added, skipped) = (0, 0)
i = opml.Importer(gpodder.subscription_file)
for item in i.items:
url = item['url']
if url not in (c.url for c in self.channels):
self.add_new_channel(url, ask_download_new=False, block=True)
added += 1
else:
log('Already added: %s', url, sender=self)
skipped += 1
self.updateComboBox()
existing = [c.url for c in self.channels]
urls = [item['url'] for item in i.items if item['url'] not in existing]
skipped = len(i.items) - len(urls)
added = len(urls)
self.add_podcast_list(urls)
self.my_gpodder_offer_autoupload()
if added > 0:
self.show_message(_('Added %d new subscriptions and skipped %d existing ones.') % (added, skipped), _('Result of subscription download'), widget=self.treeChannels)
@ -2602,7 +2546,8 @@ class gPodder(BuilderWidget, dbus.service.Object):
self.show_message(_('Please set up your username and password first.'), _('Username and password needed'), important=True)
def on_itemAddChannel_activate(self, widget, *args):
gPodderAddPodcast(self.gPodder, url_callback=self.add_new_channel)
gPodderAddPodcast(self.gPodder, \
add_urls_callback=self.add_podcast_list)
def on_itemEditChannel_activate(self, widget, *args):
if self.active_channel is None:
@ -2698,7 +2643,11 @@ class gPodder(BuilderWidget, dbus.service.Object):
dlg.destroy()
if filename is not None:
gPodderPodcastDirectory(self.gPodder, _config=self.config, custom_title=_('Import podcasts from OPML file'), hide_url_entry=True).get_channels_from_url(filename, lambda url: self.add_new_channel(url,False,block=True), lambda: self.on_itemDownloadAllNew_activate(self.gPodder))
dir = gPodderPodcastDirectory(self.gPodder, _config=self.config, \
custom_title=_('Import podcasts from OPML file'), \
add_urls_callback=self.add_podcast_list, \
hide_url_entry=True)
dir.download_opml_file(filename)
def on_itemExportChannels_activate(self, widget, *args):
if not self.channels:
@ -2731,7 +2680,9 @@ class gPodder(BuilderWidget, dbus.service.Object):
dlg.destroy()
def on_itemImportChannels_activate(self, widget, *args):
gPodderPodcastDirectory(self.gPodder, _config=self.config).get_channels_from_url(self.config.opml_url, lambda url: self.add_new_channel(url,False,block=True), lambda: self.on_itemDownloadAllNew_activate(self.gPodder))
dir = gPodderPodcastDirectory(self.gPodder, _config=self.config, \
add_urls_callback=self.add_podcast_list)
dir.download_opml_file(self.config.opml_url)
def on_homepage_activate(self, widget, *args):
util.open_website(gpodder.__url__)
@ -2840,15 +2791,6 @@ class gPodder(BuilderWidget, dbus.service.Object):
self.updateTreeView()
def on_entryAddChannel_changed(self, widget, *args):
active = self.entryAddChannel.get_text() not in ('', self.ENTER_URL_TEXT)
self.btnAddChannel.set_sensitive( active)
def on_btnAddChannel_clicked(self, widget, *args):
url = self.entryAddChannel.get_text()
self.entryAddChannel.set_text('')
self.add_new_channel( url)
def on_btnEditChannel_clicked(self, widget, *args):
self.on_itemEditChannel_activate( widget, args)