mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Sync to trunk.
This commit is contained in:
commit
12bce0197b
@ -9,6 +9,7 @@ from itertools import cycle
|
|||||||
|
|
||||||
from calibre.devices.usbms.driver import USBMS
|
from calibre.devices.usbms.driver import USBMS
|
||||||
from calibre import sanitize_file_name as sanitize
|
from calibre import sanitize_file_name as sanitize
|
||||||
|
from calibre.ebooks.metadata import string_to_authors
|
||||||
|
|
||||||
class JETBOOK(USBMS):
|
class JETBOOK(USBMS):
|
||||||
name = 'Ectaco JetBook Device Interface'
|
name = 'Ectaco JetBook Device Interface'
|
||||||
@ -118,7 +119,7 @@ class JETBOOK(USBMS):
|
|||||||
match = cls.JETBOOK_FILE_NAME_PATTERN.match(fn)
|
match = cls.JETBOOK_FILE_NAME_PATTERN.match(fn)
|
||||||
if match is not None:
|
if match is not None:
|
||||||
mi.title = check_unicode(match.group('title'))
|
mi.title = check_unicode(match.group('title'))
|
||||||
authors = match.group('authors').split('&')
|
authors = string_to_authors(match.group('authors'))
|
||||||
mi.authors = map(check_unicode, authors)
|
mi.authors = map(check_unicode, authors)
|
||||||
|
|
||||||
return mi
|
return mi
|
||||||
|
@ -70,6 +70,8 @@ def option_recommendation_to_cli_option(add_option, rec):
|
|||||||
switches.append('--'+opt.long_switch)
|
switches.append('--'+opt.long_switch)
|
||||||
attrs = dict(dest=opt.name, help=opt.help,
|
attrs = dict(dest=opt.name, help=opt.help,
|
||||||
choices=opt.choices, default=rec.recommended_value)
|
choices=opt.choices, default=rec.recommended_value)
|
||||||
|
if opt.long_switch == 'verbose':
|
||||||
|
attrs['action'] = 'count'
|
||||||
if isinstance(rec.recommended_value, type(True)):
|
if isinstance(rec.recommended_value, type(True)):
|
||||||
attrs['action'] = 'store_false' if rec.recommended_value else \
|
attrs['action'] = 'store_false' if rec.recommended_value else \
|
||||||
'store_true'
|
'store_true'
|
||||||
|
@ -19,7 +19,7 @@ import xml.dom.minidom as dom
|
|||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
from calibre.devices.prs500.prstypes import field
|
from calibre.devices.prs500.prstypes import field
|
||||||
from calibre.ebooks.metadata import MetaInformation
|
from calibre.ebooks.metadata import MetaInformation, string_to_authors
|
||||||
|
|
||||||
BYTE = "<B" #: Unsigned char little endian encoded in 1 byte
|
BYTE = "<B" #: Unsigned char little endian encoded in 1 byte
|
||||||
WORD = "<H" #: Unsigned short little endian encoded in 2 bytes
|
WORD = "<H" #: Unsigned short little endian encoded in 2 bytes
|
||||||
@ -221,10 +221,7 @@ def get_metadata(stream):
|
|||||||
@param stream: A file like object or an instance of L{LRFMetaFile}
|
@param stream: A file like object or an instance of L{LRFMetaFile}
|
||||||
"""
|
"""
|
||||||
lrf = stream if isinstance(stream, LRFMetaFile) else LRFMetaFile(stream)
|
lrf = stream if isinstance(stream, LRFMetaFile) else LRFMetaFile(stream)
|
||||||
au = lrf.author.strip().split(',')
|
authors = string_to_authors(lrf.author)
|
||||||
authors = []
|
|
||||||
for i in au:
|
|
||||||
authors.extend(i.split('&'))
|
|
||||||
mi = MetaInformation(lrf.title.strip(), authors)
|
mi = MetaInformation(lrf.title.strip(), authors)
|
||||||
mi.author = lrf.author.strip()
|
mi.author = lrf.author.strip()
|
||||||
mi.comments = lrf.free_text.strip()
|
mi.comments = lrf.free_text.strip()
|
||||||
|
@ -13,7 +13,9 @@ from urlparse import urlparse
|
|||||||
|
|
||||||
from calibre import relpath
|
from calibre import relpath
|
||||||
|
|
||||||
|
_author_pat = re.compile(',?\s+and\s+', re.IGNORECASE)
|
||||||
def string_to_authors(raw):
|
def string_to_authors(raw):
|
||||||
|
raw = _author_pat.sub('&', raw)
|
||||||
raw = raw.replace('&&', u'\uffff')
|
raw = raw.replace('&&', u'\uffff')
|
||||||
authors = [a.strip().replace(u'\uffff', '&') for a in raw.split('&')]
|
authors = [a.strip().replace(u'\uffff', '&') for a in raw.split('&')]
|
||||||
return authors
|
return authors
|
||||||
|
@ -4,7 +4,7 @@ __copyright__ = '2008, Ashish Kulkarni <kulkarni.ashish@gmail.com>'
|
|||||||
|
|
||||||
import sys, os
|
import sys, os
|
||||||
|
|
||||||
from calibre.ebooks.metadata import MetaInformation
|
from calibre.ebooks.metadata import MetaInformation, string_to_authors
|
||||||
|
|
||||||
MAGIC = ['\x00\x01BOOKDOUG', '\x00\x02BOOKDOUG']
|
MAGIC = ['\x00\x01BOOKDOUG', '\x00\x02BOOKDOUG']
|
||||||
|
|
||||||
@ -34,11 +34,7 @@ def get_metadata(stream):
|
|||||||
if title:
|
if title:
|
||||||
mi.title = title
|
mi.title = title
|
||||||
if author:
|
if author:
|
||||||
src = author.split('&')
|
mi.authors = string_to_authors(author)
|
||||||
authors = []
|
|
||||||
for au in src:
|
|
||||||
authors += au.split(',')
|
|
||||||
mi.authors = authors
|
|
||||||
mi.author = author
|
mi.author = author
|
||||||
if category:
|
if category:
|
||||||
mi.category = category
|
mi.category = category
|
||||||
|
@ -49,7 +49,7 @@ class ISBNDBMetadata(MetaInformation):
|
|||||||
def __init__(self, book):
|
def __init__(self, book):
|
||||||
MetaInformation.__init__(self, None, [])
|
MetaInformation.__init__(self, None, [])
|
||||||
|
|
||||||
self.isbn = book['isbn']
|
self.isbn = book.get('isbn13', book.get('isbn'))
|
||||||
self.title = book.find('titlelong').string
|
self.title = book.find('titlelong').string
|
||||||
if not self.title:
|
if not self.title:
|
||||||
self.title = book.find('title').string
|
self.title = book.find('title').string
|
||||||
@ -74,7 +74,6 @@ class ISBNDBMetadata(MetaInformation):
|
|||||||
self.comments = 'SUMMARY:\n'+summ.string
|
self.comments = 'SUMMARY:\n'+summ.string
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def build_isbn(base_url, opts):
|
def build_isbn(base_url, opts):
|
||||||
return base_url + 'index1=isbn&value1='+opts.isbn
|
return base_url + 'index1=isbn&value1='+opts.isbn
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ from calibre.utils.config import prefs
|
|||||||
from calibre.ebooks.metadata.opf2 import OPF
|
from calibre.ebooks.metadata.opf2 import OPF
|
||||||
|
|
||||||
from calibre.customize.ui import get_file_type_metadata, set_file_type_metadata
|
from calibre.customize.ui import get_file_type_metadata, set_file_type_metadata
|
||||||
from calibre.ebooks.metadata import MetaInformation
|
from calibre.ebooks.metadata import MetaInformation, string_to_authors
|
||||||
|
|
||||||
_METADATA_PRIORITIES = [
|
_METADATA_PRIORITIES = [
|
||||||
'html', 'htm', 'xhtml', 'xhtm',
|
'html', 'htm', 'xhtml', 'xhtm',
|
||||||
@ -132,10 +132,7 @@ def metadata_from_filename(name, pat=None):
|
|||||||
pass
|
pass
|
||||||
try:
|
try:
|
||||||
au = match.group('authors')
|
au = match.group('authors')
|
||||||
aus = au.split(',')
|
aus = string_to_authors(au)
|
||||||
authors = []
|
|
||||||
for a in aus:
|
|
||||||
authors.extend(a.split('&'))
|
|
||||||
mi.authors = authors
|
mi.authors = authors
|
||||||
except IndexError:
|
except IndexError:
|
||||||
pass
|
pass
|
||||||
|
@ -7,7 +7,7 @@ import uuid
|
|||||||
from urllib import unquote, quote
|
from urllib import unquote, quote
|
||||||
|
|
||||||
from calibre.constants import __appname__, __version__
|
from calibre.constants import __appname__, __version__
|
||||||
from calibre.ebooks.metadata import MetaInformation
|
from calibre.ebooks.metadata import MetaInformation, string_to_authors
|
||||||
from calibre.ebooks.BeautifulSoup import BeautifulStoneSoup, BeautifulSoup
|
from calibre.ebooks.BeautifulSoup import BeautifulStoneSoup, BeautifulSoup
|
||||||
from calibre.ebooks.lrf import entity_to_unicode
|
from calibre.ebooks.lrf import entity_to_unicode
|
||||||
from calibre.ebooks.metadata import Resource, ResourceCollection
|
from calibre.ebooks.metadata import Resource, ResourceCollection
|
||||||
@ -270,11 +270,7 @@ class OPF(MetaInformation):
|
|||||||
role = 'aut'
|
role = 'aut'
|
||||||
if role == 'aut' and elem.string:
|
if role == 'aut' and elem.string:
|
||||||
raw = self.ENTITY_PATTERN.sub(entity_to_unicode, elem.string)
|
raw = self.ENTITY_PATTERN.sub(entity_to_unicode, elem.string)
|
||||||
au = raw.split(',')
|
return string_to_authors(raw)
|
||||||
ans = []
|
|
||||||
for i in au:
|
|
||||||
ans.extend(i.split('&'))
|
|
||||||
return [a.strip() for a in ans]
|
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def get_author_sort(self):
|
def get_author_sort(self):
|
||||||
|
@ -15,7 +15,7 @@ try:
|
|||||||
_imagemagick_loaded = True
|
_imagemagick_loaded = True
|
||||||
except:
|
except:
|
||||||
_imagemagick_loaded = False
|
_imagemagick_loaded = False
|
||||||
from calibre.ebooks.metadata import MetaInformation, authors_to_string
|
from calibre.ebooks.metadata import MetaInformation, string_to_authors, authors_to_string
|
||||||
from calibre.utils.pdftk import set_metadata as pdftk_set_metadata
|
from calibre.utils.pdftk import set_metadata as pdftk_set_metadata
|
||||||
from calibre.utils.podofo import get_metadata as podofo_get_metadata, \
|
from calibre.utils.podofo import get_metadata as podofo_get_metadata, \
|
||||||
set_metadata as podofo_set_metadata, Unavailable, get_metadata_quick
|
set_metadata as podofo_set_metadata, Unavailable, get_metadata_quick
|
||||||
@ -69,12 +69,8 @@ def get_metadata_pypdf(stream):
|
|||||||
if info.title:
|
if info.title:
|
||||||
mi.title = info.title
|
mi.title = info.title
|
||||||
if info.author:
|
if info.author:
|
||||||
src = info.author.split('&')
|
|
||||||
authors = []
|
|
||||||
for au in src:
|
|
||||||
authors += au.split(',')
|
|
||||||
mi.authors = authors
|
|
||||||
mi.author = info.author
|
mi.author = info.author
|
||||||
|
mi.authors = string_to_authors(info.author)
|
||||||
if info.subject:
|
if info.subject:
|
||||||
mi.category = info.subject
|
mi.category = info.subject
|
||||||
except Exception, err:
|
except Exception, err:
|
||||||
|
@ -4,7 +4,7 @@ __copyright__ = '2008, Ashish Kulkarni <kulkarni.ashish@gmail.com>'
|
|||||||
|
|
||||||
import sys, struct
|
import sys, struct
|
||||||
|
|
||||||
from calibre.ebooks.metadata import MetaInformation
|
from calibre.ebooks.metadata import MetaInformation, string_to_authors
|
||||||
|
|
||||||
MAGIC = '\xb0\x0c\xb0\x0c\x02\x00NUVO\x00\x00\x00\x00'
|
MAGIC = '\xb0\x0c\xb0\x0c\x02\x00NUVO\x00\x00\x00\x00'
|
||||||
|
|
||||||
@ -41,12 +41,8 @@ def get_metadata(stream):
|
|||||||
if key.strip() == 'TITLE':
|
if key.strip() == 'TITLE':
|
||||||
mi.title = value.strip()
|
mi.title = value.strip()
|
||||||
elif key.strip() == 'AUTHOR':
|
elif key.strip() == 'AUTHOR':
|
||||||
src = value.split('&')
|
|
||||||
authors = []
|
|
||||||
for au in src:
|
|
||||||
authors += au.split(',')
|
|
||||||
mi.authors = authors
|
|
||||||
mi.author = value
|
mi.author = value
|
||||||
|
mi.authors = string_to_authors(value)
|
||||||
except Exception, err:
|
except Exception, err:
|
||||||
msg = u'Couldn\'t read metadata from rb: %s with error %s'%(mi.title, unicode(err))
|
msg = u'Couldn\'t read metadata from rb: %s with error %s'%(mi.title, unicode(err))
|
||||||
print >>sys.stderr, msg.encode('utf8')
|
print >>sys.stderr, msg.encode('utf8')
|
||||||
|
@ -5,7 +5,7 @@ Edit metadata in RTF files.
|
|||||||
"""
|
"""
|
||||||
import re, cStringIO, sys
|
import re, cStringIO, sys
|
||||||
|
|
||||||
from calibre.ebooks.metadata import MetaInformation
|
from calibre.ebooks.metadata import MetaInformation, string_to_authors
|
||||||
|
|
||||||
title_pat = re.compile(r'\{\\info.*?\{\\title(.*?)(?<!\\)\}', re.DOTALL)
|
title_pat = re.compile(r'\{\\info.*?\{\\title(.*?)(?<!\\)\}', re.DOTALL)
|
||||||
author_pat = re.compile(r'\{\\info.*?\{\\author(.*?)(?<!\\)\}', re.DOTALL)
|
author_pat = re.compile(r'\{\\info.*?\{\\author(.*?)(?<!\\)\}', re.DOTALL)
|
||||||
@ -76,10 +76,7 @@ def get_metadata(stream):
|
|||||||
category = category_match.group(1).strip()
|
category = category_match.group(1).strip()
|
||||||
mi = MetaInformation(title, author)
|
mi = MetaInformation(title, author)
|
||||||
if author:
|
if author:
|
||||||
au = author.split(',')
|
mi.authors = string_to_authors(author)
|
||||||
mi.authors = []
|
|
||||||
for i in au:
|
|
||||||
mi.authors.extend(i.split('&'))
|
|
||||||
mi.comments = comment
|
mi.comments = comment
|
||||||
mi.category = category
|
mi.category = category
|
||||||
return mi
|
return mi
|
||||||
|
@ -41,11 +41,14 @@ SVG_NS = 'http://www.w3.org/2000/svg'
|
|||||||
XLINK_NS = 'http://www.w3.org/1999/xlink'
|
XLINK_NS = 'http://www.w3.org/1999/xlink'
|
||||||
CALIBRE_NS = 'http://calibre.kovidgoyal.net/2009/metadata'
|
CALIBRE_NS = 'http://calibre.kovidgoyal.net/2009/metadata'
|
||||||
RE_NS = 'http://exslt.org/regular-expressions'
|
RE_NS = 'http://exslt.org/regular-expressions'
|
||||||
|
MBP_NS = 'http://www.mobipocket.com'
|
||||||
|
|
||||||
XPNSMAP = {'h' : XHTML_NS, 'o1' : OPF1_NS, 'o2' : OPF2_NS,
|
XPNSMAP = {'h' : XHTML_NS, 'o1' : OPF1_NS, 'o2' : OPF2_NS,
|
||||||
'd09': DC09_NS, 'd10': DC10_NS, 'd11': DC11_NS,
|
'd09': DC09_NS, 'd10': DC10_NS, 'd11': DC11_NS,
|
||||||
'xsi': XSI_NS, 'dt' : DCTERMS_NS, 'ncx': NCX_NS,
|
'xsi': XSI_NS, 'dt' : DCTERMS_NS, 'ncx': NCX_NS,
|
||||||
'svg': SVG_NS, 'xl' : XLINK_NS, 're': RE_NS}
|
'svg': SVG_NS, 'xl' : XLINK_NS, 're': RE_NS,
|
||||||
|
'mbp': MBP_NS }
|
||||||
|
|
||||||
OPF1_NSMAP = {'dc': DC11_NS, 'oebpackage': OPF1_NS}
|
OPF1_NSMAP = {'dc': DC11_NS, 'oebpackage': OPF1_NS}
|
||||||
OPF2_NSMAP = {'opf': OPF2_NS, 'dc': DC11_NS, 'dcterms': DCTERMS_NS,
|
OPF2_NSMAP = {'opf': OPF2_NS, 'dc': DC11_NS, 'dcterms': DCTERMS_NS,
|
||||||
'xsi': XSI_NS, 'calibre': CALIBRE_NS}
|
'xsi': XSI_NS, 'calibre': CALIBRE_NS}
|
||||||
@ -785,10 +788,12 @@ class Manifest(object):
|
|||||||
data = first_pass(data)
|
data = first_pass(data)
|
||||||
# Force into the XHTML namespace
|
# Force into the XHTML namespace
|
||||||
if barename(data.tag) != 'html':
|
if barename(data.tag) != 'html':
|
||||||
data = first_pass('<html>'+data+'</html>')
|
self.log.warn('File %r does not appear to be (X)HTML'%self.href)
|
||||||
if barename(data.tag) != 'html':
|
nroot = etree.fromstring('<html></html>')
|
||||||
raise NotHTML(
|
for child in list(data):
|
||||||
'File %r does not appear to be (X)HTML' % self.href)
|
child.getparent.remove(child)
|
||||||
|
nroot.append(child)
|
||||||
|
data = nroot
|
||||||
elif not namespace(data.tag):
|
elif not namespace(data.tag):
|
||||||
data.attrib['xmlns'] = XHTML_NS
|
data.attrib['xmlns'] = XHTML_NS
|
||||||
data = etree.tostring(data, encoding=unicode)
|
data = etree.tostring(data, encoding=unicode)
|
||||||
@ -799,10 +804,11 @@ class Manifest(object):
|
|||||||
try:
|
try:
|
||||||
data = etree.fromstring(data)
|
data = etree.fromstring(data)
|
||||||
except etree.XMLSyntaxError:
|
except etree.XMLSyntaxError:
|
||||||
self.oeb.logger.warn('Stripping comments from %s'%
|
self.oeb.logger.warn('Stripping comments and meta tags from %s'%
|
||||||
self.href)
|
self.href)
|
||||||
data = re.compile(r'<!--.*?-->', re.DOTALL).sub('',
|
data = re.compile(r'<!--.*?-->', re.DOTALL).sub('',
|
||||||
data)
|
data)
|
||||||
|
data = re.sub(r'<meta\s+[^>]+?>', '', data)
|
||||||
data = etree.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
|
||||||
@ -1371,9 +1377,11 @@ class TOC(object):
|
|||||||
:attr:`href`: Book-internal URL referenced by this node.
|
:attr:`href`: Book-internal URL referenced by this node.
|
||||||
:attr:`klass`: Optional semantic class referenced by this node.
|
:attr:`klass`: Optional semantic class referenced by this node.
|
||||||
:attr:`id`: Option unique identifier for this node.
|
:attr:`id`: Option unique identifier for this node.
|
||||||
|
:attr:`author`: Optional author attribution for periodicals <mbp:>
|
||||||
|
:attr:`description`: Optional description attribute for periodicals <mbp:>
|
||||||
"""
|
"""
|
||||||
def __init__(self, title=None, href=None, klass=None, id=None,
|
def __init__(self, title=None, href=None, klass=None, id=None,
|
||||||
play_order=None):
|
play_order=None, author=None, description=None):
|
||||||
self.title = title
|
self.title = title
|
||||||
self.href = urlnormalize(href) if href else href
|
self.href = urlnormalize(href) if href else href
|
||||||
self.klass = klass
|
self.klass = klass
|
||||||
@ -1383,10 +1391,12 @@ class TOC(object):
|
|||||||
if play_order is None:
|
if play_order is None:
|
||||||
play_order = self.next_play_order()
|
play_order = self.next_play_order()
|
||||||
self.play_order = play_order
|
self.play_order = play_order
|
||||||
|
self.author = author
|
||||||
|
self.description = description
|
||||||
|
|
||||||
def add(self, title, href, klass=None, id=None, play_order=0):
|
def add(self, title, href, klass=None, id=None, play_order=0, author=None, description=None):
|
||||||
"""Create and return a new sub-node of this node."""
|
"""Create and return a new sub-node of this node."""
|
||||||
node = TOC(title, href, klass, id, play_order)
|
node = TOC(title, href, klass, id, play_order, author, description)
|
||||||
self.nodes.append(node)
|
self.nodes.append(node)
|
||||||
return node
|
return node
|
||||||
|
|
||||||
|
@ -351,9 +351,27 @@ class OEBReader(object):
|
|||||||
self.logger.warn('TOC reference %r not found' % href)
|
self.logger.warn('TOC reference %r not found' % href)
|
||||||
continue
|
continue
|
||||||
id = child.get('id')
|
id = child.get('id')
|
||||||
klass = child.get('class')
|
klass = child.get('class', 'chapter')
|
||||||
|
|
||||||
po = int(child.get('playOrder', self.oeb.toc.next_play_order()))
|
po = int(child.get('playOrder', self.oeb.toc.next_play_order()))
|
||||||
node = toc.add(title, href, id=id, klass=klass, play_order=po)
|
|
||||||
|
authorElement = xpath(child,
|
||||||
|
'descendant::mbp:meta[@name = "author"]')
|
||||||
|
if authorElement :
|
||||||
|
author = authorElement[0].text
|
||||||
|
else :
|
||||||
|
author = None
|
||||||
|
|
||||||
|
descriptionElement = xpath(child,
|
||||||
|
'descendant::mbp:meta[@name = "description"]')
|
||||||
|
if descriptionElement :
|
||||||
|
description = descriptionElement[0].text
|
||||||
|
else :
|
||||||
|
description = None
|
||||||
|
|
||||||
|
node = toc.add(title, href, id=id, klass=klass,
|
||||||
|
play_order=po, description=description, author=author)
|
||||||
|
|
||||||
self._toc_from_navpoint(item, node, child)
|
self._toc_from_navpoint(item, node, child)
|
||||||
|
|
||||||
def _toc_from_ncx(self, item):
|
def _toc_from_ncx(self, item):
|
||||||
|
@ -10,7 +10,7 @@ import textwrap
|
|||||||
|
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
|
|
||||||
from calibre.ebooks.oeb.base import XPNSMAP
|
from calibre.ebooks.oeb.base import XPath, XPNSMAP
|
||||||
from calibre import guess_type
|
from calibre import guess_type
|
||||||
|
|
||||||
class Jacket(object):
|
class Jacket(object):
|
||||||
@ -41,10 +41,11 @@ class Jacket(object):
|
|||||||
''')
|
''')
|
||||||
|
|
||||||
def remove_first_image(self):
|
def remove_first_image(self):
|
||||||
|
path = XPath('//h:img[@src]')
|
||||||
for i, item in enumerate(self.oeb.spine):
|
for i, item in enumerate(self.oeb.spine):
|
||||||
if i > 2: break
|
if i > 2: break
|
||||||
for img in item.data.xpath('//h:img[@src]', namespace=XPNSMAP):
|
for img in path(item.data):
|
||||||
href = item.abshref(img.get('src'))
|
href = item.abshref(img.get('src'))
|
||||||
image = self.oeb.manifest.hrefs.get(href, None)
|
image = self.oeb.manifest.hrefs.get(href, None)
|
||||||
if image is not None:
|
if image is not None:
|
||||||
self.log('Removing first image', img.get('src'))
|
self.log('Removing first image', img.get('src'))
|
||||||
|
@ -31,6 +31,8 @@ class WizardWidget(QWidget, Ui_Form):
|
|||||||
q = '[re:test(@%s, "%s", "i")]'%(attr, val)
|
q = '[re:test(@%s, "%s", "i")]'%(attr, val)
|
||||||
else:
|
else:
|
||||||
q = '[@%s]'%attr
|
q = '[@%s]'%attr
|
||||||
|
elif val:
|
||||||
|
q = '[re:test(., "%s", "i")]'%(val)
|
||||||
expr = '//'+tag + q
|
expr = '//'+tag + q
|
||||||
return expr
|
return expr
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ from calibre.gui2.dialogs.fetch_metadata_ui import Ui_FetchMetadata
|
|||||||
from calibre.gui2 import error_dialog, NONE, info_dialog
|
from calibre.gui2 import error_dialog, NONE, info_dialog
|
||||||
from calibre.gui2.widgets import ProgressIndicator
|
from calibre.gui2.widgets import ProgressIndicator
|
||||||
from calibre.utils.config import prefs
|
from calibre.utils.config import prefs
|
||||||
|
from calibre import strftime
|
||||||
|
|
||||||
class Fetcher(QThread):
|
class Fetcher(QThread):
|
||||||
|
|
||||||
@ -45,7 +46,7 @@ class Matches(QAbstractTableModel):
|
|||||||
return len(self.matches)
|
return len(self.matches)
|
||||||
|
|
||||||
def columnCount(self, *args):
|
def columnCount(self, *args):
|
||||||
return 5
|
return 6
|
||||||
|
|
||||||
def headerData(self, section, orientation, role):
|
def headerData(self, section, orientation, role):
|
||||||
if role != Qt.DisplayRole:
|
if role != Qt.DisplayRole:
|
||||||
@ -57,6 +58,7 @@ class Matches(QAbstractTableModel):
|
|||||||
elif section == 2: text = _("Author Sort")
|
elif section == 2: text = _("Author Sort")
|
||||||
elif section == 3: text = _("Publisher")
|
elif section == 3: text = _("Publisher")
|
||||||
elif section == 4: text = _("ISBN")
|
elif section == 4: text = _("ISBN")
|
||||||
|
elif section == 5: text = _("Published")
|
||||||
|
|
||||||
return QVariant(text)
|
return QVariant(text)
|
||||||
else:
|
else:
|
||||||
@ -80,6 +82,9 @@ class Matches(QAbstractTableModel):
|
|||||||
res = book.publisher
|
res = book.publisher
|
||||||
elif col == 4:
|
elif col == 4:
|
||||||
res = book.isbn
|
res = book.isbn
|
||||||
|
elif col == 5:
|
||||||
|
if hasattr(book.pubdate, 'timetuple'):
|
||||||
|
res = strftime('%b %Y', book.pubdate.timetuple())
|
||||||
if not res:
|
if not res:
|
||||||
return NONE
|
return NONE
|
||||||
return QVariant(res)
|
return QVariant(res)
|
||||||
@ -126,7 +131,7 @@ class FetchMetadata(QDialog, Ui_FetchMetadata):
|
|||||||
prefs['isbndb_com_key'] = key
|
prefs['isbndb_com_key'] = key
|
||||||
else:
|
else:
|
||||||
key = None
|
key = None
|
||||||
title = author = publisher = isbn = None
|
title = author = publisher = isbn = pubdate = None
|
||||||
if self.isbn:
|
if self.isbn:
|
||||||
isbn = self.isbn
|
isbn = self.isbn
|
||||||
if self.title:
|
if self.title:
|
||||||
|
@ -10,8 +10,9 @@ import os
|
|||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
from PyQt4.QtCore import SIGNAL, QObject, QCoreApplication, Qt, QTimer, QThread
|
from PyQt4.QtCore import SIGNAL, QObject, QCoreApplication, Qt, QTimer, QThread, QDate
|
||||||
from PyQt4.QtGui import QPixmap, QListWidgetItem, QErrorMessage, QDialog, QCompleter
|
from PyQt4.QtGui import QPixmap, QListWidgetItem, QErrorMessage, QDialog, QCompleter
|
||||||
|
|
||||||
from calibre.gui2 import qstring_to_unicode, error_dialog, file_icon_provider, \
|
from calibre.gui2 import qstring_to_unicode, error_dialog, file_icon_provider, \
|
||||||
@ -234,6 +235,7 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
|
|||||||
self.cover.setAcceptDrops(True)
|
self.cover.setAcceptDrops(True)
|
||||||
self._author_completer = AuthorCompleter(self.db)
|
self._author_completer = AuthorCompleter(self.db)
|
||||||
self.authors.setCompleter(self._author_completer)
|
self.authors.setCompleter(self._author_completer)
|
||||||
|
self.pubdate.setMinimumDate(QDate(100,1,1))
|
||||||
self.connect(self.cover, SIGNAL('cover_changed()'), self.cover_dropped)
|
self.connect(self.cover, SIGNAL('cover_changed()'), self.cover_dropped)
|
||||||
QObject.connect(self.cover_button, SIGNAL("clicked(bool)"), \
|
QObject.connect(self.cover_button, SIGNAL("clicked(bool)"), \
|
||||||
self.select_cover)
|
self.select_cover)
|
||||||
@ -279,6 +281,9 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
|
|||||||
comments = self.db.comments(row)
|
comments = self.db.comments(row)
|
||||||
self.comments.setPlainText(comments if comments else '')
|
self.comments.setPlainText(comments if comments else '')
|
||||||
cover = self.db.cover(row)
|
cover = self.db.cover(row)
|
||||||
|
pubdate = db.pubdate(self.id, index_is_id=True)
|
||||||
|
self.pubdate.setDate(QDate(pubdate.year, pubdate.month,
|
||||||
|
pubdate.day))
|
||||||
|
|
||||||
exts = self.db.formats(row)
|
exts = self.db.formats(row)
|
||||||
if exts:
|
if exts:
|
||||||
@ -441,6 +446,9 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
|
|||||||
if book.author_sort: self.author_sort.setText(book.author_sort)
|
if book.author_sort: self.author_sort.setText(book.author_sort)
|
||||||
if book.publisher: self.publisher.setEditText(book.publisher)
|
if book.publisher: self.publisher.setEditText(book.publisher)
|
||||||
if book.isbn: self.isbn.setText(book.isbn)
|
if book.isbn: self.isbn.setText(book.isbn)
|
||||||
|
if book.pubdate:
|
||||||
|
d = book.pubdate
|
||||||
|
self.pubdate.setDate(QDate(d.year, d.month, d.day))
|
||||||
summ = book.comments
|
summ = book.comments
|
||||||
if summ:
|
if summ:
|
||||||
prefix = qstring_to_unicode(self.comments.toPlainText())
|
prefix = qstring_to_unicode(self.comments.toPlainText())
|
||||||
@ -485,6 +493,9 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
|
|||||||
self.db.set_series(self.id, qstring_to_unicode(self.series.currentText()), notify=False)
|
self.db.set_series(self.id, qstring_to_unicode(self.series.currentText()), notify=False)
|
||||||
self.db.set_series_index(self.id, self.series_index.value(), notify=False)
|
self.db.set_series_index(self.id, self.series_index.value(), notify=False)
|
||||||
self.db.set_comment(self.id, qstring_to_unicode(self.comments.toPlainText()), notify=False)
|
self.db.set_comment(self.id, qstring_to_unicode(self.comments.toPlainText()), notify=False)
|
||||||
|
d = self.pubdate.date()
|
||||||
|
self.db.set_pubdate(self.id, datetime(d.year(), d.month(), d.day()))
|
||||||
|
|
||||||
if self.cover_changed:
|
if self.cover_changed:
|
||||||
self.db.set_cover(self.id, pixmap_to_data(self.cover.pixmap()))
|
self.db.set_cover(self.id, pixmap_to_data(self.cover.pixmap()))
|
||||||
QDialog.accept(self)
|
QDialog.accept(self)
|
||||||
|
@ -325,6 +325,19 @@
|
|||||||
<item row="8" column="1" colspan="2">
|
<item row="8" column="1" colspan="2">
|
||||||
<widget class="QLineEdit" name="isbn"/>
|
<widget class="QLineEdit" name="isbn"/>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="9" column="0">
|
||||||
|
<widget class="QLabel" name="label_10">
|
||||||
|
<property name="text">
|
||||||
|
<string>Publishe&d:</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>pubdate</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item row="4" column="1">
|
<item row="4" column="1">
|
||||||
<widget class="QComboBox" name="publisher">
|
<widget class="QComboBox" name="publisher">
|
||||||
<property name="editable">
|
<property name="editable">
|
||||||
@ -348,6 +361,16 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="9" column="1">
|
||||||
|
<widget class="QDateEdit" name="pubdate">
|
||||||
|
<property name="displayFormat">
|
||||||
|
<string>MMM yyyy</string>
|
||||||
|
</property>
|
||||||
|
<property name="calendarPopup">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -632,6 +655,7 @@
|
|||||||
<tabstop>tag_editor_button</tabstop>
|
<tabstop>tag_editor_button</tabstop>
|
||||||
<tabstop>remove_series_button</tabstop>
|
<tabstop>remove_series_button</tabstop>
|
||||||
<tabstop>isbn</tabstop>
|
<tabstop>isbn</tabstop>
|
||||||
|
<tabstop>pubdate</tabstop>
|
||||||
<tabstop>comments</tabstop>
|
<tabstop>comments</tabstop>
|
||||||
<tabstop>fetch_metadata_button</tabstop>
|
<tabstop>fetch_metadata_button</tabstop>
|
||||||
<tabstop>fetch_cover_button</tabstop>
|
<tabstop>fetch_cover_button</tabstop>
|
||||||
|
@ -314,7 +314,10 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
|||||||
QObject.connect(self.action_convert,
|
QObject.connect(self.action_convert,
|
||||||
SIGNAL('triggered(bool)'), self.convert_single)
|
SIGNAL('triggered(bool)'), self.convert_single)
|
||||||
self.convert_menu = cm
|
self.convert_menu = cm
|
||||||
|
|
||||||
pm = QMenu()
|
pm = QMenu()
|
||||||
|
ap = self.action_preferences
|
||||||
|
pm.addAction(ap.icon(), ap.text())
|
||||||
pm.addAction(self.preferences_action)
|
pm.addAction(self.preferences_action)
|
||||||
pm.addAction(_('Run welcome wizard'))
|
pm.addAction(_('Run welcome wizard'))
|
||||||
self.connect(pm.actions()[1], SIGNAL('triggered(bool)'),
|
self.connect(pm.actions()[1], SIGNAL('triggered(bool)'),
|
||||||
|
@ -9,6 +9,7 @@ from zlib import compress, decompress
|
|||||||
|
|
||||||
from calibre.ebooks.metadata import MetaInformation
|
from calibre.ebooks.metadata import MetaInformation
|
||||||
from calibre.web.feeds.recipes import migrate_automatic_profile_to_automatic_recipe
|
from calibre.web.feeds.recipes import migrate_automatic_profile_to_automatic_recipe
|
||||||
|
from calibre.ebooks.metadata import string_to_authors
|
||||||
|
|
||||||
class Concatenate(object):
|
class Concatenate(object):
|
||||||
'''String concatenation aggregator for sqlite'''
|
'''String concatenation aggregator for sqlite'''
|
||||||
@ -97,7 +98,7 @@ class LibraryDatabase(object):
|
|||||||
obj = conn.execute('INSERT INTO books(title, timestamp, author_sort) VALUES (?,?,?)',
|
obj = conn.execute('INSERT INTO books(title, timestamp, author_sort) VALUES (?,?,?)',
|
||||||
(book['title'], book['timestamp'], authors))
|
(book['title'], book['timestamp'], authors))
|
||||||
id = obj.lastrowid
|
id = obj.lastrowid
|
||||||
authors = authors.split('&')
|
authors = string_to_authors(authors)
|
||||||
for a in authors:
|
for a in authors:
|
||||||
author = conn.execute('SELECT id from authors WHERE name=?', (a,)).fetchone()
|
author = conn.execute('SELECT id from authors WHERE name=?', (a,)).fetchone()
|
||||||
if author:
|
if author:
|
||||||
@ -1103,7 +1104,7 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE;
|
|||||||
item[col] = val
|
item[col] = val
|
||||||
break
|
break
|
||||||
if column == 'authors':
|
if column == 'authors':
|
||||||
val = val.split('&,')
|
val = string_to_authors(val)
|
||||||
self.set_authors(id, val)
|
self.set_authors(id, val)
|
||||||
elif column == 'title':
|
elif column == 'title':
|
||||||
self.set_title(id, val)
|
self.set_title(id, val)
|
||||||
@ -1266,7 +1267,7 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE;
|
|||||||
mi.authors = ['Unknown']
|
mi.authors = ['Unknown']
|
||||||
authors = []
|
authors = []
|
||||||
for a in mi.authors:
|
for a in mi.authors:
|
||||||
authors += a.split('&')
|
authors += string_to_authors(a)
|
||||||
self.set_authors(id, authors)
|
self.set_authors(id, authors)
|
||||||
if mi.author_sort:
|
if mi.author_sort:
|
||||||
self.set_author_sort(id, mi.author_sort)
|
self.set_author_sort(id, mi.author_sort)
|
||||||
|
@ -993,7 +993,7 @@ class LibraryDatabase2(LibraryDatabase):
|
|||||||
mi.authors = [_('Unknown')]
|
mi.authors = [_('Unknown')]
|
||||||
authors = []
|
authors = []
|
||||||
for a in mi.authors:
|
for a in mi.authors:
|
||||||
authors += a.split('&')
|
authors += string_to_authors(a)
|
||||||
self.set_authors(id, authors, notify=False)
|
self.set_authors(id, authors, notify=False)
|
||||||
if mi.author_sort:
|
if mi.author_sort:
|
||||||
self.set_author_sort(id, mi.author_sort, notify=False)
|
self.set_author_sort(id, mi.author_sort, notify=False)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
|
|
||||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
@ -10,35 +11,59 @@ globeandmail.com
|
|||||||
from calibre.web.feeds.news import BasicNewsRecipe
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
class GlobeAndMail(BasicNewsRecipe):
|
class GlobeAndMail(BasicNewsRecipe):
|
||||||
|
title = u'Globe and Mail'
|
||||||
title = 'Globe and Mail'
|
|
||||||
__author__ = 'Kovid Goyal'
|
|
||||||
language = _('English')
|
language = _('English')
|
||||||
oldest_article = 2.0
|
__author__ = 'Kovid Goyal'
|
||||||
|
oldest_article = 2
|
||||||
|
max_articles_per_feed = 10
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
|
extra_css = '''
|
||||||
|
h3 {font-size: 22pt; font-weight:bold; margin:0px; padding:0px 0px 8pt 0px;}
|
||||||
|
h4 {margin-top: 0px;}
|
||||||
|
#byline { font-family: monospace; font-weight:bold; }
|
||||||
|
#placeline {font-weight:bold;}
|
||||||
|
#credit {margin-top:0px;}
|
||||||
|
.tag {font-size: 22pt;}'''
|
||||||
description = 'Canada\'s national newspaper'
|
description = 'Canada\'s national newspaper'
|
||||||
remove_tags_before = dict(id="article-top")
|
remove_tags_before = dict(id="article-top")
|
||||||
remove_tags = [
|
remove_tags = [
|
||||||
{'id':['util', 'article-tabs', 'comments', 'article-relations',
|
{'id':['util', 'article-tabs', 'comments', 'article-relations',
|
||||||
'gallery-controls', 'video', 'galleryLoading']},
|
'gallery-controls', 'video', 'galleryLoading','deck','header'] },
|
||||||
]
|
{'class':['credit','inline-img-caption','tab-pointer'] },
|
||||||
remove_tags_after = dict(id='article-content')
|
dict(name='div', attrs={'id':'lead-photo'}),
|
||||||
|
dict(name='div', attrs={'class':'right'}),
|
||||||
|
dict(name='div', attrs={'id':'footer'}),
|
||||||
|
dict(name='div', attrs={'id':'beta-msg'}),
|
||||||
|
dict(name='img', attrs={'class':'headshot'}),
|
||||||
|
dict(name='div', attrs={'class':'brand'}),
|
||||||
|
dict(name='div', attrs={'id':'nav-wrap'}),
|
||||||
|
dict(name='div', attrs={'id':'featureTopics'}),
|
||||||
|
dict(name='div', attrs={'id':'videoNav'}),
|
||||||
|
dict(name='div', attrs={'id':'blog-header'}),
|
||||||
|
dict(name='div', attrs={'id':'right-rail'}),
|
||||||
|
dict(name='div', attrs={'id':'group-footer-container'}),
|
||||||
|
dict(name=['iframe','img'])
|
||||||
|
]
|
||||||
|
remove_tags_after = [{'id':['article-content']},
|
||||||
|
{'class':['pull','inline-img'] },
|
||||||
|
dict(name='img', attrs={'class':'inline-media-embed'}),
|
||||||
|
]
|
||||||
feeds = [
|
feeds = [
|
||||||
('Latest headlines', 'http://www.theglobeandmail.com/?service=rss'),
|
(u'Latest headlines', u'http://www.theglobeandmail.com/?service=rss'),
|
||||||
('Top stories', 'http://www.theglobeandmail.com/?service=rss&feed=topstories'),
|
(u'Top stories', u'http://www.theglobeandmail.com/?service=rss&feed=topstories'),
|
||||||
('National', 'http://www.theglobeandmail.com/news/national/?service=rss'),
|
(u'National', u'http://www.theglobeandmail.com/news/national/?service=rss'),
|
||||||
('Politics', 'http://www.theglobeandmail.com/news/politics/?service=rss'),
|
(u'Politics', u'http://www.theglobeandmail.com/news/politics/?service=rss'),
|
||||||
('World', 'http://www.theglobeandmail.com/news/world/?service=rss'),
|
(u'World', u'http://www.theglobeandmail.com/news/world/?service=rss'),
|
||||||
('Business', 'http://www.theglobeandmail.com/report-on-business/?service=rss'),
|
(u'Business', u'http://www.theglobeandmail.com/report-on-business/?service=rss'),
|
||||||
('Opinions', 'http://www.theglobeandmail.com/news/opinions/?service=rss'),
|
(u'Opinions', u'http://www.theglobeandmail.com/news/opinions/?service=rss'),
|
||||||
('Columnists', 'http://www.theglobeandmail.com/news/opinions/columnists/?service=rss'),
|
(u'Columnists', u'http://www.theglobeandmail.com/news/opinions/columnists/?service=rss'),
|
||||||
('Globe Investor', 'http://www.theglobeandmail.com/globe-investor/?service=rss'),
|
(u'Globe Investor', u'http://www.theglobeandmail.com/globe-investor/?service=rss'),
|
||||||
('Sports', 'http://www.theglobeandmail.com/sports/?service=rss'),
|
(u'Sports', u'http://www.theglobeandmail.com/sports/?service=rss'),
|
||||||
('Technology', 'http://www.theglobeandmail.com/news/technology/?service=rss'),
|
(u'Technology', u'http://www.theglobeandmail.com/news/technology/?service=rss'),
|
||||||
('Arts', 'http://www.theglobeandmail.com/news/arts/?service=rss'),
|
(u'Arts', u'http://www.theglobeandmail.com/news/arts/?service=rss'),
|
||||||
('Life', 'http://www.theglobeandmail.com/life/?service=rss'),
|
(u'Life', u'http://www.theglobeandmail.com/life/?service=rss'),
|
||||||
('Blogs', 'http://www.theglobeandmail.com/blogs/?service=rss'),
|
(u'Blogs', u'http://www.theglobeandmail.com/blogs/?service=rss'),
|
||||||
('Real Estate', 'http://www.theglobeandmail.com/real-estate/?service=rss'),
|
(u'Real Estate', u'http://www.theglobeandmail.com/real-estate/?service=rss'),
|
||||||
('Auto', 'http://www.theglobeandmail.com/auto/?service=rss'),
|
(u'Auto', u'http://www.theglobeandmail.com/auto/?service=rss')
|
||||||
]
|
]
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user