Sync to trunk.

This commit is contained in:
John Schember 2009-06-21 20:18:45 -04:00
commit 12bce0197b
22 changed files with 186 additions and 107 deletions

View File

@ -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

View File

@ -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'

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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):

View File

@ -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:

View File

@ -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')

View File

@ -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

View File

@ -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

View File

@ -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):

View File

@ -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,9 +41,10 @@ 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:

View File

@ -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

View File

@ -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:

View File

@ -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)

View File

@ -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&amp;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>

View File

@ -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)'),

View File

@ -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)

View File

@ -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)

View File

@ -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'] },
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'}),
] ]
remove_tags_after = dict(id='article-content')
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')
] ]