Switch to reading plugin index from plugins mirror

This commit is contained in:
Kovid Goyal 2013-08-04 15:21:54 +05:30
parent 66d897893a
commit eb9714c761

View File

@ -23,10 +23,8 @@ from calibre.gui2 import error_dialog, question_dialog, info_dialog, NONE, open_
from calibre.gui2.preferences.plugins import ConfigWidget
from calibre.utils.date import UNDEFINED_DATE, format_date
MR_URL = 'http://www.mobileread.com/forums/'
MR_INDEX_URL = MR_URL + 'showpost.php?p=1362767&postcount=1'
SERVER = 'http://plugins.calibre-ebook.com/'
INDEX_URL = '%splugins.json.bz2' % SERVER
FILTER_ALL = 0
FILTER_INSTALLED = 1
FILTER_UPDATE_AVAILABLE = 2
@ -55,33 +53,30 @@ def filter_not_installed_plugins(display_plugin):
return not display_plugin.is_installed()
def read_available_plugins(raise_error=False):
import json, bz2
display_plugins = []
br = browser()
br.set_handle_gzip(True)
try:
raw = br.open_novisit(MR_INDEX_URL).read()
raw = br.open_novisit(INDEX_URL).read()
if not raw:
return
raw = json.loads(bz2.decompress(raw))
except:
if raise_error:
raise
traceback.print_exc()
return
raw = raw.decode('utf-8', errors='replace')
root = html.fromstring(raw)
list_nodes = root.xpath('//div[@id="post_message_1362767"]/ul/li')
# Add our deprecated plugins which are nested in a grey span
list_nodes.extend(root.xpath('//div[@id="post_message_1362767"]/span/ul/li'))
for list_node in list_nodes:
for plugin in raw.itervalues():
try:
display_plugin = DisplayPlugin(list_node)
display_plugin = DisplayPlugin(plugin)
get_installed_plugin_status(display_plugin)
display_plugins.append(display_plugin)
except:
if DEBUG:
prints('======= MobileRead Parse Error =======')
prints('======= Plugin Parse Error =======')
traceback.print_exc()
prints(html.tostring(list_node))
import pprint
pprint.pprint(plugin)
display_plugins = sorted(display_plugins, key=lambda k: k.name)
return display_plugins
@ -189,61 +184,21 @@ class PluginFilterComboBox(QComboBox):
class DisplayPlugin(object):
def __init__(self, list_node):
# The html from the index web page looks like this:
'''
<li><a href="http://www.mobileread.com/forums/showthread.php?t=121787">Book Sync</a><br />
<i>Add books to a list to be automatically sent to your device the next time it is connected.<br />
<span class="resize_1">Version: 1.1; Released: 02-22-2011; Calibre: 0.7.42; Author: kiwidude; <br />
Platforms: Windows, OSX, Linux; History: Yes;</span></i></li>
'''
self.name = list_node.xpath('a')[0].text_content().strip()
self.forum_link = list_node.xpath('a/@href')[0].strip()
def __init__(self, plugin):
self.name = plugin['name']
self.forum_link = plugin['thread_url']
self.zip_url = SERVER + plugin['file']
self.installed_version = None
description_text = list_node.xpath('i')[0].text_content()
description_parts = description_text.partition('Version:')
self.description = description_parts[0].strip()
details_text = description_parts[1] + description_parts[2].replace('\r\n','')
details_pairs = details_text.split(';')
details = {}
for details_pair in details_pairs:
pair = details_pair.split(':')
if len(pair) == 2:
key = pair[0].strip().lower()
value = pair[1].strip()
details[key] = value
donation_node = list_node.xpath('i/span/a/@href')
self.donation_link = donation_node[0] if donation_node else None
self.available_version = self._version_text_to_tuple(details.get('version', None))
release_date = details.get('released', '01-01-0101').split('-')
date_parts = [int(re.search(r'(\d+)', x).group(1)) for x in release_date]
self.release_date = datetime.date(date_parts[2], date_parts[0], date_parts[1])
self.calibre_required_version = self._version_text_to_tuple(details.get('calibre', None))
self.author = details.get('author', '')
self.platforms = [p.strip().lower() for p in details.get('platforms', '').split(',')]
# Optional pairing just for plugins which require checking for uninstall first
self.uninstall_plugins = []
uninstall = details.get('uninstall', None)
if uninstall:
self.uninstall_plugins = [i.strip() for i in uninstall.split(',')]
self.has_changelog = details.get('history', 'No').lower() in ['yes', 'true']
self.is_deprecated = details.get('deprecated', 'No').lower() in ['yes', 'true']
def _version_text_to_tuple(self, version_text):
if version_text:
ver = version_text.split('.')
while len(ver) < 3:
ver.append('0')
ver = [int(re.search(r'(\d+)', x).group(1)) for x in ver]
return tuple(ver)
else:
return None
self.description = plugin['description']
self.donation_link = plugin['donate']
self.available_version = tuple(plugin['version'])
self.release_date = datetime.datetime(*tuple(map(int, re.split(r'\D', plugin['last_modified'])))[:6]).date()
self.calibre_required_version = plugin['minimum_calibre_version']
self.author = plugin['author']
self.platforms = plugin['supported_platforms']
self.uninstall_plugins = plugin['uninstall'] or []
self.has_changelog = plugin['history']
self.is_deprecated = plugin['deprecated']
def is_disabled(self):
if self.plugin is None:
@ -456,6 +411,7 @@ class PluginUpdaterDialog(SizePersistedDialog):
SizePersistedDialog.__init__(self, gui, 'Plugin Updater plugin:plugin updater dialog')
self.gui = gui
self.forum_link = None
self.zip_url = None
self.model = None
self.do_restart = False
self._initialize_controls()
@ -475,8 +431,8 @@ class PluginUpdaterDialog(SizePersistedDialog):
self._select_and_focus_view()
else:
error_dialog(self.gui, _('Update Check Failed'),
_('Unable to reach the MobileRead plugins forum index page.'),
det_msg=MR_INDEX_URL, show=True)
_('Unable to reach the plugin index page.'),
det_msg=INDEX_URL, show=True)
self.filter_combo.setEnabled(False)
# Cause our dialog size to be restored from prefs or created on first usage
self.resize_dialog()
@ -599,6 +555,7 @@ class PluginUpdaterDialog(SizePersistedDialog):
display_plugin = self.model.display_plugins[actual_idx.row()]
self.description.setText(display_plugin.description)
self.forum_link = display_plugin.forum_link
self.zip_url = display_plugin.zip_url
self.forum_action.setEnabled(bool(self.forum_link))
self.install_button.setEnabled(display_plugin.is_valid_to_install())
self.install_action.setEnabled(self.install_button.isEnabled())
@ -611,6 +568,7 @@ class PluginUpdaterDialog(SizePersistedDialog):
else:
self.description.setText('')
self.forum_link = None
self.zip_url = None
self.forum_action.setEnabled(False)
self.install_button.setEnabled(False)
self.install_action.setEnabled(False)
@ -703,17 +661,7 @@ class PluginUpdaterDialog(SizePersistedDialog):
for name_to_remove in uninstall_names:
self._uninstall_plugin(name_to_remove)
if DEBUG:
prints('Locating zip file for %s: %s'% (display_plugin.name, display_plugin.forum_link))
self.gui.status_bar.showMessage(
_('Locating zip file for %(name)s: %(link)s') % dict(
name=display_plugin.name, link=display_plugin.forum_link))
plugin_zip_url = self._read_zip_attachment_url(display_plugin.forum_link)
if not plugin_zip_url:
return error_dialog(self.gui, _('Install Plugin Failed'),
_('Unable to locate a plugin zip file for <b>%s</b>') % display_plugin.name,
det_msg=display_plugin.forum_link, show=True)
plugin_zip_url = display_plugin.zip_url
if DEBUG:
prints('Downloading plugin zip attachment: ', plugin_zip_url)
self.gui.status_bar.showMessage(_('Downloading plugin zip attachment: %s') % plugin_zip_url)
@ -852,39 +800,11 @@ class PluginUpdaterDialog(SizePersistedDialog):
prints(html.tostring(spoiler_node))
return None
def _read_zip_attachment_url(self, forum_link):
br = browser()
br.set_handle_gzip(True)
try:
raw = br.open_novisit(forum_link).read()
if not raw:
return None
except:
traceback.print_exc()
return None
raw = raw.decode('utf-8', errors='replace')
root = html.fromstring(raw)
attachment_nodes = root.xpath('//fieldset/table/tr/td/a')
for attachment_node in attachment_nodes:
try:
filename = attachment_node.text_content().lower()
if filename.find('.zip') != -1:
full_url = MR_URL + attachment_node.attrib['href']
return full_url
except:
if DEBUG:
prints('======= MobileRead Parse Error =======')
traceback.print_exc()
prints(html.tostring(attachment_node))
return None
def _download_zip(self, plugin_zip_url):
from calibre.ptempfile import PersistentTemporaryFile
br = browser()
br.set_handle_gzip(True)
raw = br.open_novisit(plugin_zip_url).read()
pt = PersistentTemporaryFile('.zip')
pt.write(raw)
pt.close()
with PersistentTemporaryFile('.zip') as pt:
pt.write(raw)
return pt.name