Skip to content

Commit

Permalink
Gtk UI: Import titles from OPML / directory (bug 1711)
Browse files Browse the repository at this point in the history
We now prefer titles from the podcast directory and OPML
files to the title in the feed. For URL-only adds, we still
use the title from the feed.
  • Loading branch information
thp committed Nov 17, 2012
1 parent 173d074 commit 672ae57
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 25 deletions.
13 changes: 7 additions & 6 deletions src/gpodder/gtkui/desktop/podcastdirectory.py
Expand Up @@ -47,8 +47,8 @@ def new(self):
new_parent.remove(self.notebookChannelAdder)
self.vboxOpmlImport.reparent(new_parent)

if not hasattr(self, 'add_urls_callback'):
self.add_urls_callback = None
if not hasattr(self, 'add_podcast_list'):
self.add_podcast_list = None

self.setup_treeview(self.treeviewChannelChooser)
self.setup_treeview(self.treeviewTopPodcastsChooser)
Expand Down Expand Up @@ -89,7 +89,8 @@ def get_selected_channels(self, tab=None):
if model is not None:
for row in model:
if row[OpmlListModel.C_SELECTED]:
channels.append(row[OpmlListModel.C_URL])
channels.append((row[OpmlListModel.C_TITLE],
row[OpmlListModel.C_URL]))

return channels

Expand Down Expand Up @@ -172,12 +173,12 @@ def on_btnSelectNone_clicked(self, widget, *args):
self.select_all(False)

def on_btnOK_clicked(self, widget, *args):
channel_urls = self.get_selected_channels()
channels = self.get_selected_channels()
self.gPodderPodcastDirectory.destroy()

# add channels that have been selected
if self.add_urls_callback is not None:
self.add_urls_callback(channel_urls)
if self.add_podcast_list is not None:
self.add_podcast_list(channels)

def on_btnCancel_clicked(self, widget, *args):
self.gPodderPodcastDirectory.destroy()
Expand Down
9 changes: 5 additions & 4 deletions src/gpodder/gtkui/interface/addpodcast.py
Expand Up @@ -30,8 +30,8 @@

class gPodderAddPodcast(BuilderWidget):
def new(self):
if not hasattr(self, 'add_urls_callback'):
self.add_urls_callback = None
if not hasattr(self, 'add_podcast_list'):
self.add_podcast_list = None
if hasattr(self, 'custom_label'):
self.label_add.set_text(self.custom_label)
if hasattr(self, 'custom_title'):
Expand Down Expand Up @@ -82,6 +82,7 @@ def on_entry_url_activate(self, widget):
def on_btn_add_clicked(self, widget):
url = self.entry_url.get_text()
self.on_btn_close_clicked(widget)
if self.add_urls_callback is not None:
self.add_urls_callback([url])
if self.add_podcast_list is not None:
title = None # FIXME: Add title GUI element
self.add_podcast_list([(title, url)])

42 changes: 30 additions & 12 deletions src/gpodder/gtkui/main.py
Expand Up @@ -413,7 +413,11 @@ def on_add_remove_podcasts_mygpo(self):
self.mygpo_client.confirm_received_actions(ignored)

def execute_podcast_actions(selected):
add_list = [c.action.url for c in selected if c.action.is_add]
# In the future, we might retrieve the title from gpodder.net here,
# but for now, we just use "None" to use the feed-provided title
title = None
add_list = [(title, c.action.url)
for c in selected if c.action.is_add]
remove_list = [c.podcast for c in selected if c.action.is_remove]

# Apply the accepted changes locally
Expand Down Expand Up @@ -2120,8 +2124,8 @@ def offer_new_episodes(self, channels=None):
return True
return False

def add_podcast_list(self, urls, auth_tokens=None):
"""Subscribe to a list of podcast given their URLs
def add_podcast_list(self, podcasts, auth_tokens=None):
"""Subscribe to a list of podcast given (title, url) pairs
If auth_tokens is given, it should be a dictionary
mapping URLs to (username, password) tuples."""
Expand All @@ -2131,18 +2135,24 @@ def add_podcast_list(self, urls, auth_tokens=None):

existing_urls = set(podcast.url for podcast in self.channels)

# For a given URL, the desired title (or None)
title_for_url = {}

# Sort and split the URL list into five buckets
queued, failed, existing, worked, authreq = [], [], [], [], []
for input_url in urls:
for input_title, input_url in podcasts:
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 url in existing_urls:
# A podcast already exists in the list for this URL
existing.append(url)
# XXX: Should we try to update the title of the existing
# subscription from input_title here if it is different?
else:
# This URL has survived the first round - queue for add
title_for_url[url] = input_title
queued.append(url)
if url != input_url and input_url in auth_tokens:
auth_tokens[url] = auth_tokens[input_url]
Expand Down Expand Up @@ -2216,7 +2226,9 @@ def on_after_update():

# If we have authentication data to retry, do so here
if retry_podcasts:
self.add_podcast_list(retry_podcasts.keys(), retry_podcasts)
podcasts = [(title_for_url.get(url), url)
for url in retry_podcasts.keys()]
self.add_podcast_list(podcasts, retry_podcasts)
# This will NOT show new episodes for podcasts that have
# been added ("worked"), but it will prevent problems with
# multiple dialogs being open at the same time ;)
Expand All @@ -2240,8 +2252,9 @@ def thread_proc():
# After the initial sorting and splitting, try all queued podcasts
length = len(queued)
for index, url in enumerate(queued):
title = title_for_url.get(url)
progress.on_progress(float(index)/float(length))
progress.on_message(url)
progress.on_message(title or url)
try:
# The URL is valid and does not exist already - subscribe!
channel = self.model.load_podcast(url=url, create=True, \
Expand All @@ -2253,11 +2266,16 @@ def thread_proc():
except ValueError, ve:
username, password = (None, None)

if title is not None:
# Prefer title from subscription source (bug 1711)
channel.title = title

if username is not None and channel.auth_username is None and \
password is not None and channel.auth_password is None:
channel.auth_username = username
channel.auth_password = password
channel.save()

channel.save()

self._update_cover(channel)
except feedcore.AuthenticationRequired:
Expand Down Expand Up @@ -2878,7 +2896,7 @@ def on_register_button_clicked():

dir = gPodderPodcastDirectory(self.gPodder, _config=self.config, \
custom_title=_('Subscriptions on gpodder.net'), \
add_urls_callback=self.add_podcast_list, \
add_podcast_list=self.add_podcast_list,
hide_url_entry=True)

# TODO: Refactor this into "gpodder.my" or mygpoclient, so that
Expand All @@ -2891,7 +2909,7 @@ def on_register_button_clicked():

def on_itemAddChannel_activate(self, widget=None):
gPodderAddPodcast(self.gPodder, \
add_urls_callback=self.add_podcast_list)
add_podcast_list=self.add_podcast_list)

def on_itemEditChannel_activate(self, widget, *args):
if self.active_channel is None:
Expand Down Expand Up @@ -3027,7 +3045,7 @@ def on_item_import_from_file_activate(self, widget, filename=None):
if filename is not None:
dir = gPodderPodcastDirectory(self.gPodder, _config=self.config, \
custom_title=_('Import podcasts from OPML file'), \
add_urls_callback=self.add_podcast_list, \
add_podcast_list=self.add_podcast_list,
hide_url_entry=True)
dir.download_opml_file(filename)

Expand Down Expand Up @@ -3058,7 +3076,7 @@ def on_itemExportChannels_activate(self, widget, *args):

def on_itemImportChannels_activate(self, widget, *args):
dir = gPodderPodcastDirectory(self.main_window, _config=self.config, \
add_urls_callback=self.add_podcast_list)
add_podcast_list=self.add_podcast_list)
util.idle_add(dir.download_opml_file, my.EXAMPLES_OPML)

def on_homepage_activate(self, widget, *args):
Expand Down Expand Up @@ -3389,7 +3407,7 @@ def show_gui_window(self):
@dbus.service.method(gpodder.dbus_interface)
def subscribe_to_url(self, url):
gPodderAddPodcast(self.gPodder,
add_urls_callback=self.add_podcast_list,
add_podcast_list=self.add_podcast_list,
preset_url=url)

@dbus.service.method(gpodder.dbus_interface)
Expand Down
7 changes: 4 additions & 3 deletions src/gpodder/gtkui/opml.py
Expand Up @@ -29,12 +29,13 @@
import urllib

class OpmlListModel(gtk.ListStore):
C_SELECTED, C_DESCRIPTION_MARKUP, C_URL = range(3)
C_SELECTED, C_TITLE, C_DESCRIPTION_MARKUP, C_URL = range(4)

def __init__(self, importer):
gtk.ListStore.__init__(self, bool, str, str)
gtk.ListStore.__init__(self, bool, str, str, str)
for channel in importer.items:
self.append([False, self._format_channel(channel), channel['url']])
self.append([False, channel['title'],
self._format_channel(channel), channel['url']])

def _format_channel(self, channel):
title = cgi.escape(urllib.unquote_plus(channel['title']))
Expand Down

0 comments on commit 672ae57

Please sign in to comment.