mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
merge from trunk
This commit is contained in:
commit
814ece6980
3191
resources/images/default_cover.svg
Normal file
3191
resources/images/default_cover.svg
Normal file
File diff suppressed because it is too large
Load Diff
After Width: | Height: | Size: 105 KiB |
@ -453,7 +453,7 @@ from calibre.devices.hanvon.driver import N516, EB511, ALEX, AZBOOKA, THEBOOK
|
|||||||
from calibre.devices.edge.driver import EDGE
|
from calibre.devices.edge.driver import EDGE
|
||||||
from calibre.devices.teclast.driver import TECLAST_K3, NEWSMY, IPAPYRUS
|
from calibre.devices.teclast.driver import TECLAST_K3, NEWSMY, IPAPYRUS
|
||||||
from calibre.devices.sne.driver import SNE
|
from calibre.devices.sne.driver import SNE
|
||||||
from calibre.devices.misc import PALMPRE, AVANT
|
from calibre.devices.misc import PALMPRE, AVANT, SWEEX
|
||||||
from calibre.devices.folder_device.driver import FOLDER_DEVICE_FOR_CONFIG
|
from calibre.devices.folder_device.driver import FOLDER_DEVICE_FOR_CONFIG
|
||||||
from calibre.devices.kobo.driver import KOBO
|
from calibre.devices.kobo.driver import KOBO
|
||||||
|
|
||||||
@ -499,7 +499,6 @@ plugins += [
|
|||||||
]
|
]
|
||||||
# Order here matters. The first matched device is the one used.
|
# Order here matters. The first matched device is the one used.
|
||||||
plugins += [
|
plugins += [
|
||||||
ITUNES,
|
|
||||||
HANLINV3,
|
HANLINV3,
|
||||||
HANLINV5,
|
HANLINV5,
|
||||||
BLACKBERRY,
|
BLACKBERRY,
|
||||||
@ -551,6 +550,8 @@ plugins += [
|
|||||||
FOLDER_DEVICE_FOR_CONFIG,
|
FOLDER_DEVICE_FOR_CONFIG,
|
||||||
AVANT,
|
AVANT,
|
||||||
MENTOR,
|
MENTOR,
|
||||||
|
SWEEX,
|
||||||
|
ITUNES,
|
||||||
]
|
]
|
||||||
plugins += [x for x in list(locals().values()) if isinstance(x, type) and \
|
plugins += [x for x in list(locals().values()) if isinstance(x, type) and \
|
||||||
x.__name__.endswith('MetadataReader')]
|
x.__name__.endswith('MetadataReader')]
|
||||||
|
@ -49,3 +49,23 @@ class AVANT(USBMS):
|
|||||||
EBOOK_DIR_MAIN = ''
|
EBOOK_DIR_MAIN = ''
|
||||||
SUPPORTS_SUB_DIRS = True
|
SUPPORTS_SUB_DIRS = True
|
||||||
|
|
||||||
|
class SWEEX(USBMS):
|
||||||
|
name = 'Sweex Device Interface'
|
||||||
|
gui_name = 'Sweex'
|
||||||
|
description = _('Communicate with the Sweex MM300')
|
||||||
|
author = 'Kovid Goyal'
|
||||||
|
supported_platforms = ['windows', 'osx', 'linux']
|
||||||
|
|
||||||
|
# Ordered list of supported formats
|
||||||
|
FORMATS = ['epub', 'prc', 'fb2', 'html', 'rtf', 'chm', 'pdf', 'txt']
|
||||||
|
|
||||||
|
VENDOR_ID = [0x0525]
|
||||||
|
PRODUCT_ID = [0xa4a5]
|
||||||
|
BCD = [0x0319]
|
||||||
|
|
||||||
|
VENDOR_NAME = 'SWEEX'
|
||||||
|
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = 'EBOOKREADER'
|
||||||
|
|
||||||
|
EBOOK_DIR_MAIN = ''
|
||||||
|
SUPPORTS_SUB_DIRS = True
|
||||||
|
|
||||||
|
@ -273,6 +273,7 @@ def filter_metadata_results(item):
|
|||||||
|
|
||||||
def do_cover_check(item):
|
def do_cover_check(item):
|
||||||
item.has_cover = False
|
item.has_cover = False
|
||||||
|
if item.isbn:
|
||||||
try:
|
try:
|
||||||
item.has_cover = check_for_cover(item.isbn)
|
item.has_cover = check_for_cover(item.isbn)
|
||||||
except:
|
except:
|
||||||
|
@ -34,7 +34,8 @@ def fetch_metadata(url, max=100, timeout=5.):
|
|||||||
errmsg = soup.find('errormessage').string
|
errmsg = soup.find('errormessage').string
|
||||||
raise ISBNDBError('Error fetching metadata: '+errmsg)
|
raise ISBNDBError('Error fetching metadata: '+errmsg)
|
||||||
total_results = int(book_list['total_results'])
|
total_results = int(book_list['total_results'])
|
||||||
np = '&page_number=%s&'%(page_number+1)
|
page_number += 1
|
||||||
|
np = '&page_number=%s&'%page_number
|
||||||
url = re.sub(r'\&page_number=\d+\&', np, url)
|
url = re.sub(r'\&page_number=\d+\&', np, url)
|
||||||
books.extend(book_list.findAll('bookdata'))
|
books.extend(book_list.findAll('bookdata'))
|
||||||
max -= 1
|
max -= 1
|
||||||
|
@ -7,7 +7,7 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2008, Marshall T. Vandegrift <llasram@gmail.com>'
|
__copyright__ = '2008, Marshall T. Vandegrift <llasram@gmail.com>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import os, re, uuid, logging, functools
|
import os, re, uuid, logging
|
||||||
from mimetypes import types_map
|
from mimetypes import types_map
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from itertools import count
|
from itertools import count
|
||||||
@ -808,17 +808,17 @@ class Manifest(object):
|
|||||||
pat = re.compile(r'&(%s);'%('|'.join(user_entities.keys())))
|
pat = re.compile(r'&(%s);'%('|'.join(user_entities.keys())))
|
||||||
data = pat.sub(lambda m:user_entities[m.group(1)], data)
|
data = pat.sub(lambda m:user_entities[m.group(1)], data)
|
||||||
|
|
||||||
fromstring = functools.partial(etree.fromstring, parser=RECOVER_PARSER)
|
parser = etree.XMLParser(no_network=True, huge_tree=True)
|
||||||
# Try with more & more drastic measures to parse
|
# Try with more & more drastic measures to parse
|
||||||
def first_pass(data):
|
def first_pass(data):
|
||||||
try:
|
try:
|
||||||
data = fromstring(data)
|
data = etree.fromstring(data, parser=parser)
|
||||||
except etree.XMLSyntaxError, err:
|
except etree.XMLSyntaxError, err:
|
||||||
self.oeb.log.exception('Initial parse failed:')
|
self.oeb.log.exception('Initial parse failed:')
|
||||||
repl = lambda m: ENTITYDEFS.get(m.group(1), m.group(0))
|
repl = lambda m: ENTITYDEFS.get(m.group(1), m.group(0))
|
||||||
data = ENTITY_RE.sub(repl, data)
|
data = ENTITY_RE.sub(repl, data)
|
||||||
try:
|
try:
|
||||||
data = fromstring(data)
|
data = etree.fromstring(data, parser=parser)
|
||||||
except etree.XMLSyntaxError, err:
|
except etree.XMLSyntaxError, err:
|
||||||
self.oeb.logger.warn('Parsing file %r as HTML' % self.href)
|
self.oeb.logger.warn('Parsing file %r as HTML' % self.href)
|
||||||
if err.args and err.args[0].startswith('Excessive depth'):
|
if err.args and err.args[0].startswith('Excessive depth'):
|
||||||
@ -832,9 +832,9 @@ class Manifest(object):
|
|||||||
elem.text = elem.text.strip('-')
|
elem.text = elem.text.strip('-')
|
||||||
data = etree.tostring(data, encoding=unicode)
|
data = etree.tostring(data, encoding=unicode)
|
||||||
try:
|
try:
|
||||||
data = fromstring(data)
|
data = etree.fromstring(data, parser=parser)
|
||||||
except etree.XMLSyntaxError:
|
except etree.XMLSyntaxError:
|
||||||
data = fromstring(data)
|
data = etree.fromstring(data, parser=RECOVER_PARSER)
|
||||||
return data
|
return data
|
||||||
data = first_pass(data)
|
data = first_pass(data)
|
||||||
|
|
||||||
@ -866,12 +866,12 @@ class Manifest(object):
|
|||||||
data = etree.tostring(data, encoding=unicode)
|
data = etree.tostring(data, encoding=unicode)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = fromstring(data)
|
data = etree.fromstring(data, parser=parser)
|
||||||
except:
|
except:
|
||||||
data = data.replace(':=', '=').replace(':>', '>')
|
data = data.replace(':=', '=').replace(':>', '>')
|
||||||
data = data.replace('<http:/>', '')
|
data = data.replace('<http:/>', '')
|
||||||
try:
|
try:
|
||||||
data = fromstring(data)
|
data = etree.fromstring(data, parser=parser)
|
||||||
except etree.XMLSyntaxError:
|
except etree.XMLSyntaxError:
|
||||||
self.oeb.logger.warn('Stripping comments and meta tags from %s'%
|
self.oeb.logger.warn('Stripping comments and meta tags from %s'%
|
||||||
self.href)
|
self.href)
|
||||||
@ -882,7 +882,7 @@ class Manifest(object):
|
|||||||
"<?xml version='1.0' encoding='utf-8'?><o:p></o:p>",
|
"<?xml version='1.0' encoding='utf-8'?><o:p></o:p>",
|
||||||
'')
|
'')
|
||||||
data = data.replace("<?xml version='1.0' encoding='utf-8'??>", '')
|
data = data.replace("<?xml version='1.0' encoding='utf-8'??>", '')
|
||||||
data = fromstring(data)
|
data = etree.fromstring(data)
|
||||||
elif namespace(data.tag) != XHTML_NS:
|
elif namespace(data.tag) != XHTML_NS:
|
||||||
# OEB_DOC_NS, but possibly others
|
# OEB_DOC_NS, but possibly others
|
||||||
ns = namespace(data.tag)
|
ns = namespace(data.tag)
|
||||||
|
@ -248,7 +248,7 @@ def info_dialog(parent, title, msg, det_msg='', show=False):
|
|||||||
|
|
||||||
class Dispatcher(QObject):
|
class Dispatcher(QObject):
|
||||||
'''Convenience class to ensure that a function call always happens in the
|
'''Convenience class to ensure that a function call always happens in the
|
||||||
thread the reciver was created in.'''
|
thread the receiver was created in.'''
|
||||||
dispatch_signal = pyqtSignal(object, object)
|
dispatch_signal = pyqtSignal(object, object)
|
||||||
|
|
||||||
def __init__(self, func):
|
def __init__(self, func):
|
||||||
@ -507,7 +507,7 @@ def pixmap_to_data(pixmap, format='JPEG'):
|
|||||||
buf = QBuffer(ba)
|
buf = QBuffer(ba)
|
||||||
buf.open(QBuffer.WriteOnly)
|
buf.open(QBuffer.WriteOnly)
|
||||||
pixmap.save(buf, format)
|
pixmap.save(buf, format)
|
||||||
return str(ba.data())
|
return bytes(ba.data())
|
||||||
|
|
||||||
class ResizableDialog(QDialog):
|
class ResizableDialog(QDialog):
|
||||||
|
|
||||||
|
@ -90,7 +90,7 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
|
|||||||
COVER_FETCH_TIMEOUT = 240 # seconds
|
COVER_FETCH_TIMEOUT = 240 # seconds
|
||||||
|
|
||||||
def do_reset_cover(self, *args):
|
def do_reset_cover(self, *args):
|
||||||
pix = QPixmap(I('book.svg'))
|
pix = QPixmap(I('default_cover.svg'))
|
||||||
self.cover.setPixmap(pix)
|
self.cover.setPixmap(pix)
|
||||||
self.cover_changed = True
|
self.cover_changed = True
|
||||||
self.cover_data = None
|
self.cover_data = None
|
||||||
@ -408,7 +408,7 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
|
|||||||
if cover:
|
if cover:
|
||||||
pm.loadFromData(cover)
|
pm.loadFromData(cover)
|
||||||
if pm.isNull():
|
if pm.isNull():
|
||||||
pm = QPixmap(I('book.svg'))
|
pm = QPixmap(I('default_cover.svg'))
|
||||||
else:
|
else:
|
||||||
self.cover_data = cover
|
self.cover_data = cover
|
||||||
self.cover.setPixmap(pm)
|
self.cover.setPixmap(pm)
|
||||||
|
@ -56,7 +56,8 @@ class ToolbarMixin(object): # {{{
|
|||||||
partial(self.edit_metadata, False, bulk=True))
|
partial(self.edit_metadata, False, bulk=True))
|
||||||
md.addSeparator()
|
md.addSeparator()
|
||||||
md.addAction(_('Download metadata and covers'),
|
md.addAction(_('Download metadata and covers'),
|
||||||
partial(self.download_metadata, False, covers=True))
|
partial(self.download_metadata, False, covers=True),
|
||||||
|
Qt.ControlModifier+Qt.Key_D)
|
||||||
md.addAction(_('Download only metadata'),
|
md.addAction(_('Download only metadata'),
|
||||||
partial(self.download_metadata, False, covers=False))
|
partial(self.download_metadata, False, covers=False))
|
||||||
md.addAction(_('Download only covers'),
|
md.addAction(_('Download only covers'),
|
||||||
|
@ -43,6 +43,14 @@ class FormatPath(unicode):
|
|||||||
ans.deleted_after_upload = False
|
ans.deleted_after_upload = False
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
_default_image = None
|
||||||
|
|
||||||
|
def default_image():
|
||||||
|
global _default_image
|
||||||
|
if _default_image is None:
|
||||||
|
_default_image = QImage(I('default_cover.svg'))
|
||||||
|
return _default_image
|
||||||
|
|
||||||
class BooksModel(QAbstractTableModel): # {{{
|
class BooksModel(QAbstractTableModel): # {{{
|
||||||
|
|
||||||
about_to_be_sorted = pyqtSignal(object, name='aboutToBeSorted')
|
about_to_be_sorted = pyqtSignal(object, name='aboutToBeSorted')
|
||||||
@ -71,7 +79,7 @@ class BooksModel(QAbstractTableModel): # {{{
|
|||||||
self.book_on_device = None
|
self.book_on_device = None
|
||||||
self.editable_cols = ['title', 'authors', 'rating', 'publisher',
|
self.editable_cols = ['title', 'authors', 'rating', 'publisher',
|
||||||
'tags', 'series', 'timestamp', 'pubdate']
|
'tags', 'series', 'timestamp', 'pubdate']
|
||||||
self.default_image = QImage(I('book.svg'))
|
self.default_image = default_image()
|
||||||
self.sorted_on = DEFAULT_SORT
|
self.sorted_on = DEFAULT_SORT
|
||||||
self.sort_history = [self.sorted_on]
|
self.sort_history = [self.sorted_on]
|
||||||
self.last_search = '' # The last search performed on this model
|
self.last_search = '' # The last search performed on this model
|
||||||
|
@ -56,6 +56,8 @@ def init_qt(args):
|
|||||||
|
|
||||||
def get_default_library_path():
|
def get_default_library_path():
|
||||||
fname = _('Calibre Library')
|
fname = _('Calibre Library')
|
||||||
|
if iswindows:
|
||||||
|
fname = 'Calibre Library'
|
||||||
if isinstance(fname, unicode):
|
if isinstance(fname, unicode):
|
||||||
try:
|
try:
|
||||||
fname = fname.encode(filesystem_encoding)
|
fname = fname.encode(filesystem_encoding)
|
||||||
|
@ -579,7 +579,7 @@ void PictureFlowPrivate::resetSlides()
|
|||||||
static QImage prepareSurface(QImage img, int w, int h)
|
static QImage prepareSurface(QImage img, int w, int h)
|
||||||
{
|
{
|
||||||
Qt::TransformationMode mode = Qt::SmoothTransformation;
|
Qt::TransformationMode mode = Qt::SmoothTransformation;
|
||||||
img = img.scaled(w, h, Qt::KeepAspectRatioByExpanding, mode);
|
img = img.scaled(w, h, Qt::IgnoreAspectRatio, mode);
|
||||||
|
|
||||||
// slightly larger, to accommodate for the reflection
|
// slightly larger, to accommodate for the reflection
|
||||||
int hs = int(h * REFLECTION_FACTOR);
|
int hs = int(h * REFLECTION_FACTOR);
|
||||||
|
@ -539,17 +539,20 @@ MIME = '''\
|
|||||||
</mime-info>
|
</mime-info>
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def render_svg(image, dest):
|
def render_svg(image, dest, width=128, height=128):
|
||||||
from PyQt4.QtGui import QPainter, QImage
|
from PyQt4.QtGui import QPainter, QImage
|
||||||
from PyQt4.QtSvg import QSvgRenderer
|
from PyQt4.QtSvg import QSvgRenderer
|
||||||
svg = QSvgRenderer(image.readAll())
|
image = image.readAll() if hasattr(image, 'readAll') else image
|
||||||
|
svg = QSvgRenderer(image)
|
||||||
painter = QPainter()
|
painter = QPainter()
|
||||||
image = QImage(128,128,QImage.Format_ARGB32_Premultiplied)
|
image = QImage(width, height, QImage.Format_ARGB32)
|
||||||
painter.begin(image)
|
painter.begin(image)
|
||||||
painter.setRenderHints(QPainter.Antialiasing|QPainter.TextAntialiasing|QPainter.SmoothPixmapTransform|QPainter.HighQualityAntialiasing)
|
painter.setRenderHints(QPainter.Antialiasing|QPainter.TextAntialiasing|QPainter.SmoothPixmapTransform|QPainter.HighQualityAntialiasing)
|
||||||
painter.setCompositionMode(QPainter.CompositionMode_SourceOver)
|
painter.setCompositionMode(QPainter.CompositionMode_SourceOver)
|
||||||
svg.render(painter)
|
svg.render(painter)
|
||||||
painter.end()
|
painter.end()
|
||||||
|
if dest is None:
|
||||||
|
return image
|
||||||
image.save(dest)
|
image.save(dest)
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
Loading…
x
Reference in New Issue
Block a user