Sync to pluginize

This commit is contained in:
John Schember 2009-04-02 20:57:09 -04:00
commit 394b35b435
25 changed files with 407 additions and 368 deletions

View File

@ -2,7 +2,7 @@ __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en'
__appname__ = 'calibre'
__version__ = '0.5.4'
__version__ = '0.5.5'
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
'''
Various run time constants.

View File

@ -6,6 +6,9 @@ Code for the conversion of ebook formats and the reading of metadata
from various formats.
'''
import traceback, os
from calibre import CurrentDir
class ConversionError(Exception):
def __init__(self, msg, only_msg=False):
@ -22,3 +25,54 @@ BOOK_EXTENSIONS = ['lrf', 'rar', 'zip', 'rtf', 'lit', 'txt', 'htm', 'xhtm',
'html', 'xhtml', 'pdf', 'prc', 'mobi', 'azw',
'epub', 'fb2', 'djvu', 'lrx', 'cbr', 'cbz', 'oebzip',
'rb', 'imp', 'odt']
class HTMLRenderer(object):
def __init__(self, page, loop):
self.page, self.loop = page, loop
self.data = ''
self.exception = self.tb = None
def __call__(self, ok):
from PyQt4.Qt import QImage, QPainter, QByteArray, QBuffer
try:
if not ok:
raise RuntimeError('Rendering of HTML failed.')
image = QImage(self.page.viewportSize(), QImage.Format_ARGB32)
image.setDotsPerMeterX(96*(100/2.54))
image.setDotsPerMeterY(96*(100/2.54))
painter = QPainter(image)
self.page.mainFrame().render(painter)
painter.end()
ba = QByteArray()
buf = QBuffer(ba)
buf.open(QBuffer.WriteOnly)
image.save(buf, 'JPEG')
self.data = str(ba.data())
except Exception, e:
self.exception = e
self.traceback = traceback.format_exc()
finally:
self.loop.exit(0)
def render_html(path_to_html, width=590, height=750):
from PyQt4.QtWebKit import QWebPage
from PyQt4.Qt import QEventLoop, QPalette, Qt, SIGNAL, QUrl, QSize
path_to_html = os.path.abspath(path_to_html)
with CurrentDir(os.path.dirname(path_to_html)):
page = QWebPage()
pal = page.palette()
pal.setBrush(QPalette.Background, Qt.white)
page.setPalette(pal)
page.setViewportSize(QSize(width, height))
page.mainFrame().setScrollBarPolicy(Qt.Vertical, Qt.ScrollBarAlwaysOff)
page.mainFrame().setScrollBarPolicy(Qt.Horizontal, Qt.ScrollBarAlwaysOff)
loop = QEventLoop()
renderer = HTMLRenderer(page, loop)
page.connect(page, SIGNAL('loadFinished(bool)'), renderer)
page.mainFrame().load(QUrl.fromLocalFile(path_to_html))
loop.exec_()
return renderer

View File

@ -339,7 +339,7 @@ OptionRecommendation(name='language',
trimmer = ManifestTrimmer()
trimmer(self.oeb, self.opts)
self.log.info('Creating %s output...'%self.output_plugin.name)
self.log.info('Creating %s...'%self.output_plugin.name)
self.output_plugin.convert(self.oeb, self.output, self.input_plugin, self.opts,
self.log)

View File

@ -208,6 +208,10 @@ class HTMLProcessor(Processor, Rationalizer):
for tag in self.root.xpath('//table | //tr | //th | //td'):
tag.tag = 'div'
# ADE can't handle &amp; in an img url
for tag in self.root.xpath('//img[@src]'):
tag.set('src', tag.get('src', '').replace('&', ''))
def save(self):
for meta in list(self.root.xpath('//meta')):

View File

@ -162,9 +162,13 @@ class LRFStream(LRFObject):
self.stream = stream.read(self.stream_size)
if self.stream_flags & 0x200 !=0:
l = len(self.stream);
key = l % self._scramble_key + 0xF;
key = self._scramble_key&0xFF
if key != 0 and key <= 0xF0:
key = l % key + 0xF
else:
key = 0
if l > 0x400 and (isinstance(self, ImageStream) or isinstance(self, Font) or isinstance(self, SoundStream)):
l = 0x400;
l = 0x400
self.stream = self.descramble_buffer(self.stream, l, key)
if self.stream_flags & 0x100 !=0:
decomp_size = struct.unpack("<I", self.stream[:4])[0]

View File

@ -266,12 +266,14 @@ class MobiReader(object):
parse_cache[htmlfile] = root
self.htmlfile = htmlfile
ncx = cStringIO.StringIO()
opf = self.create_opf(htmlfile, guide, root)
opf, ncx_manifest_entry = self.create_opf(htmlfile, guide, root)
self.created_opf_path = os.path.splitext(htmlfile)[0]+'.opf'
opf.render(open(self.created_opf_path, 'wb'), ncx)
opf.render(open(self.created_opf_path, 'wb'), ncx,
ncx_manifest_entry=ncx_manifest_entry)
ncx = ncx.getvalue()
if ncx:
open(os.path.splitext(htmlfile)[0]+'.ncx', 'wb').write(ncx)
ncx_path = os.path.join(os.path.dirname(htmlfile), 'toc.ncx')
open(ncx_path, 'wb').write(ncx)
with open('styles.css', 'wb') as s:
s.write(self.base_css_rules+'\n\n')
@ -284,8 +286,9 @@ class MobiReader(object):
if self.book_header.exth is not None or self.embedded_mi is not None:
self.log.debug('Creating OPF...')
ncx = cStringIO.StringIO()
opf = self.create_opf(htmlfile, guide, root)
opf.render(open(os.path.splitext(htmlfile)[0]+'.opf', 'wb'), ncx)
opf, ncx_manifest_entry = self.create_opf(htmlfile, guide, root)
opf.render(open(os.path.splitext(htmlfile)[0]+'.opf', 'wb'), ncx,
ncx_manifest_entry )
ncx = ncx.getvalue()
if ncx:
open(os.path.splitext(htmlfile)[0]+'.ncx', 'wb').write(ncx)
@ -434,7 +437,10 @@ class MobiReader(object):
for ref in opf.guide:
if ref.type.lower() == 'toc':
toc = ref.href()
ncx_manifest_entry = None
if toc:
ncx_manifest_entry = 'toc.ncx'
elems = root.xpath('//*[@id="%s"]'%toc.partition('#')[-1])
tocobj = None
ent_pat = re.compile(r'&(\S+?);')
@ -461,7 +467,7 @@ class MobiReader(object):
if tocobj is not None:
opf.set_toc(tocobj)
return opf
return opf, ncx_manifest_entry
def sizeof_trailing_entries(self, data):
@ -589,7 +595,7 @@ def get_metadata(stream):
if mr.book_header.exth is None:
mi = MetaInformation(mr.name, [_('Unknown')])
else:
mi = mr.create_opf('dummy.html')
mi = mr.create_opf('dummy.html')[0]
try:
if hasattr(mr.book_header.exth, 'cover_offset'):
cover_index = mr.book_header.first_image_index + \

View File

@ -44,7 +44,7 @@ class OEBOutput(OutputFormatPlugin):
else:
raw = etree.tostring(raw, encoding='utf-8',
pretty_print=opts.pretty_print)
raw = raw + '<?xml version="1.0" encoding="utf-8" ?>\n'
raw = '<?xml version="1.0" encoding="utf-8" ?>\n'+raw
if isinstance(raw, unicode):
raw = raw.encode('utf-8')
with open(path, 'wb') as f:

View File

@ -7,14 +7,12 @@ Defines various abstract base classes that can be subclassed to create powerful
__docformat__ = "restructuredtext en"
import logging, os, cStringIO, time, traceback, re, urlparse, sys, tempfile, functools
import logging, os, cStringIO, time, traceback, re, urlparse, sys
from collections import defaultdict
from functools import partial
from contextlib import nested, closing
from PyQt4.Qt import QApplication, QFile, Qt, QPalette, QSize, QImage, QPainter, \
QBuffer, QByteArray, SIGNAL, QUrl, QEventLoop, QIODevice
from PyQt4.QtWebKit import QWebPage
from PyQt4.Qt import QApplication, QFile, QIODevice
from calibre import browser, __appname__, iswindows, \
@ -22,14 +20,15 @@ from calibre import browser, __appname__, iswindows, \
from calibre.ebooks.BeautifulSoup import BeautifulSoup, NavigableString, CData, Tag
from calibre.ebooks.metadata.opf2 import OPFCreator
from calibre.ebooks.lrf import entity_to_unicode
from calibre.ebooks import render_html
from calibre.ebooks.metadata.toc import TOC
from calibre.ebooks.metadata import MetaInformation
from calibre.web.feeds import feed_from_xml, templates, feeds_from_index, Feed
from calibre.web.fetch.simple import option_parser as web2disk_option_parser
from calibre.web.fetch.simple import RecursiveFetcher
from calibre.utils.threadpool import WorkRequest, ThreadPool, NoResultsPending
from calibre.ptempfile import PersistentTemporaryFile
from calibre.gui2 import images_rc # Needed for default cover
from calibre.ptempfile import PersistentTemporaryFile, \
PersistentTemporaryDirectory
class BasicNewsRecipe(object):
@ -787,15 +786,18 @@ class BasicNewsRecipe(object):
'''
Create a generic cover for recipes that dont have a cover
'''
from calibre.gui2 import images_rc # Needed for access to logo
images_rc
if QApplication.instance() is None: QApplication([])
f = QFile(':/library')
f.open(QIODevice.ReadOnly)
img = str(f.readAll())
img_data = str(f.readAll())
tdir = PersistentTemporaryDirectory('_default_cover')
img = os.path.join(tdir, 'logo.png')
with open(img, 'wb') as g:
g.write(img_data)
f.close()
f = tempfile.NamedTemporaryFile(suffix='library.png')
f.write(img)
f.flush()
img = f.name
img = os.path.basename(img)
html= u'''\
<html>
<head>
@ -834,38 +836,16 @@ class BasicNewsRecipe(object):
date=strftime(self.timefmt),
app=__appname__ +' '+__version__,
img=img)
f2 = tempfile.NamedTemporaryFile(suffix='cover.html')
f2.write(html.encode('utf-8'))
f2.flush()
page = QWebPage()
pal = page.palette()
pal.setBrush(QPalette.Background, Qt.white)
page.setPalette(pal)
page.setViewportSize(QSize(590, 750))
page.mainFrame().setScrollBarPolicy(Qt.Vertical, Qt.ScrollBarAlwaysOff)
page.mainFrame().setScrollBarPolicy(Qt.Horizontal, Qt.ScrollBarAlwaysOff)
loop = QEventLoop()
def render_html(page, loop, ok):
try:
image = QImage(page.viewportSize(), QImage.Format_ARGB32)
image.setDotsPerMeterX(96*(100/2.54))
image.setDotsPerMeterY(96*(100/2.54))
painter = QPainter(image)
page.mainFrame().render(painter)
painter.end()
ba = QByteArray()
buf = QBuffer(ba)
buf.open(QBuffer.WriteOnly)
image.save(buf, 'JPEG')
image_data = str(ba.data())
cover_file.write(image_data)
hf = os.path.join(tdir, 'cover.htm')
with open(hf, 'wb') as f:
f.write(html.encode('utf-8'))
renderer = render_html(hf)
if renderer.tb is not None:
self.logger.warning('Failed to render default cover')
self.logger.debug(renderer.tb)
else:
cover_file.write(renderer.data)
cover_file.flush()
finally:
loop.exit(0)
page.connect(page, SIGNAL('loadFinished(bool)'), functools.partial(render_html, page, loop))
page.mainFrame().load(QUrl.fromLocalFile(f2.name))
loop.exec_()
def create_opf(self, feeds, dir=None):

View File

@ -13,7 +13,6 @@ class Exiled(BasicNewsRecipe):
__author__ = 'Darko Miletic'
description = "Mankind's only alternative since 1997 - Formerly known as The eXile"
publisher = 'Exiled Online'
language = _('English')
category = 'news, politics, international'
oldest_article = 15
max_articles_per_feed = 100
@ -21,10 +20,12 @@ class Exiled(BasicNewsRecipe):
use_embedded_content = False
encoding = 'utf8'
remove_javascript = True
language = _('English')
cover_url = 'http://exiledonline.com/wp-content/themes/exiledonline_theme/images/header-sm.gif'
html2lrf_options = [
'--comment' , description
, '--base-font-size', '10'
, '--category' , category
, '--publisher' , publisher
]
@ -49,3 +50,8 @@ class Exiled(BasicNewsRecipe):
soup.head.insert(0,mtag)
return soup
def get_article_url(self, article):
raw = article.get('link', None)
final = raw + 'all/1/'
return final

View File

@ -6,7 +6,7 @@ __copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>'
laprensa.com.ni
'''
import locale
import datetime
import time
from calibre.web.feeds.news import BasicNewsRecipe
@ -23,23 +23,9 @@ class LaPrensa_ni(BasicNewsRecipe):
encoding = 'cp1252'
remove_javascript = True
language = _('Spanish')
#Locale setting to get appropriate date/month values in Spanish
try:
#Windows seting for locale
locale.setlocale(locale.LC_TIME,'Spanish_Nicaragua')
except locale.Error:
#Linux setting for locale -- choose one appropriate for your distribution
try:
locale.setlocale(locale.LC_TIME,'es_NI')
except locale.Error:
try:
locale.setlocale(locale.LC_TIME,'es_ES')
except:
pass
current_index = time.strftime("http://www.laprensa.com.ni/archivo/%Y/%B/%d/noticias/")
months_es = ['enero','febrero','marzo','abril','mayo','junio','julio','agosto','septiembre','octubre','noviembre','diciembre']
current_month = months_es[datetime.date.today().month - 1]
current_index = time.strftime("http://www.laprensa.com.ni/archivo/%Y/" + current_month + "/%d/noticias/")
html2lrf_options = [
'--comment', description
@ -91,6 +77,3 @@ class LaPrensa_ni(BasicNewsRecipe):
totalfeeds.append((feedtitle, articles))
return totalfeeds
def cleanup(self):
#Going back to the default locale
locale.setlocale(locale.LC_TIME,'')

View File

@ -2,10 +2,10 @@ from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.db.models import permalink
from django.contrib.auth.models import User
from calibre.www.apps.tagging.fields import TagField
from tagging.fields import TagField
from calibre.www.apps.blog.managers import PublicManager
import calibre.www.apps.tagging as tagging
import tagging
class Category(models.Model):
"""Category model."""

View File

@ -40,10 +40,10 @@ INSTALLED_APPS = (
'django.contrib.sites',
'django.contrib.admin',
'django.contrib.comments',
'django.contrib.markup',
'calibre.www.apps.inlines',
'calibre.www.apps.tagging',
'tagging',
'calibre.www.apps.blog',
)

View File

@ -2,14 +2,16 @@ from django.conf.urls.defaults import patterns, include, handler404, handler500
from django.conf import settings
# Uncomment the next two lines to enable the admin:
#from django.contrib import admin
#admin.autodiscover()
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
# (r'^admin/(.*)', admin.site.root),
(r'^admin/(.*)', admin.site.root),
(r'^comments/', include('django.contrib.comments.urls')),
(r'', include('calibre.www.apps.blog.urls')),
)

View File

@ -1,6 +1,6 @@
from django.utils.translation import ugettext as _
from calibre.www.apps.tagging.managers import ModelTaggedItemManager, TagDescriptor
from tagging.managers import ModelTaggedItemManager, TagDescriptor
VERSION = (0, 3, 'pre')

View File

@ -1,5 +1,5 @@
from django.contrib import admin
from calibre.www.apps.tagging.models import Tag, TaggedItem
from tagging.models import Tag, TaggedItem
admin.site.register(TaggedItem)
admin.site.register(Tag)

View File

@ -5,9 +5,9 @@ from django.db.models import signals
from django.db.models.fields import CharField
from django.utils.translation import ugettext_lazy as _
from calibre.www.apps.tagging import settings
from calibre.www.apps.tagging.models import Tag
from calibre.www.apps.tagging.utils import edit_string_for_tags
from tagging import settings
from tagging.models import Tag
from tagging.utils import edit_string_for_tags
class TagField(CharField):
"""
@ -101,7 +101,7 @@ class TagField(CharField):
return 'CharField'
def formfield(self, **kwargs):
from calibre.www.apps.tagging import forms
from tagging import forms
defaults = {'form_class': forms.TagField}
defaults.update(kwargs)
return super(TagField, self).formfield(**defaults)

View File

@ -4,9 +4,9 @@ Tagging components for Django's form library.
from django import forms
from django.utils.translation import ugettext as _
from calibre.www.apps.tagging import settings
from calibre.www.apps.tagging.models import Tag
from calibre.www.apps.tagging.utils import parse_tag_input
from tagging import settings
from tagging.models import Tag
from tagging.utils import parse_tag_input
class AdminTagForm(forms.ModelForm):
class Meta:

View File

@ -5,7 +5,7 @@ application.
from django.contrib.contenttypes.models import ContentType
from django.db import models
from calibre.www.apps.tagging.models import Tag, TaggedItem
from tagging.models import Tag, TaggedItem
class ModelTagManager(models.Manager):
"""

View File

@ -13,9 +13,9 @@ from django.db import connection, models
from django.db.models.query import QuerySet
from django.utils.translation import ugettext_lazy as _
from calibre.www.apps.tagging import settings
from calibre.www.apps.tagging.utils import calculate_cloud, get_tag_list, get_queryset_and_model, parse_tag_input
from calibre.www.apps.tagging.utils import LOGARITHMIC
from tagging import settings
from tagging.utils import calculate_cloud, get_tag_list, get_queryset_and_model, parse_tag_input
from tagging.utils import LOGARITHMIC
qn = connection.ops.quote_name

View File

@ -159,7 +159,7 @@ def get_tag_list(tags):
* A ``Tag`` ``QuerySet``.
"""
from calibre.www.apps.tagging.models import Tag
from tagging.models import Tag
if isinstance(tags, Tag):
return [tags]
elif isinstance(tags, QuerySet) and tags.model is Tag:
@ -201,7 +201,7 @@ def get_tag(tag):
If no matching tag can be found, ``None`` will be returned.
"""
from calibre.www.apps.tagging.models import Tag
from tagging.models import Tag
if isinstance(tag, Tag):
return tag

View File

@ -5,8 +5,8 @@ from django.http import Http404
from django.utils.translation import ugettext as _
from django.views.generic.list_detail import object_list
from calibre.www.apps.tagging.models import Tag, TaggedItem
from calibre.www.apps.tagging.utils import get_tag, get_queryset_and_model
from tagging.models import Tag, TaggedItem
from tagging.utils import get_tag, get_queryset_and_model
def tagged_object_list(request, queryset_or_model=None, tag=None,
related_tags=False, related_tag_counts=True, **kwargs):