From 53cbe4814d3b27e6d98cb819b2a2b47a0b234e0e Mon Sep 17 00:00:00 2001 From: "Marshall T. Vandegrift" Date: Sat, 31 Jan 2009 11:01:31 -0500 Subject: [PATCH 1/6] Improve Python-attribute access of metadata XML-attributes. --- src/calibre/ebooks/oeb/base.py | 37 ++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/src/calibre/ebooks/oeb/base.py b/src/calibre/ebooks/oeb/base.py index 106a091c71..be557703ae 100644 --- a/src/calibre/ebooks/oeb/base.py +++ b/src/calibre/ebooks/oeb/base.py @@ -52,6 +52,8 @@ def XML(name): return '{%s}%s' % (XML_NS, name) def XHTML(name): return '{%s}%s' % (XHTML_NS, name) def OPF(name): return '{%s}%s' % (OPF2_NS, name) def DC(name): return '{%s}%s' % (DC11_NS, name) +def XSI(name): return '{%s}%s' % (XSI_NS, name) +def DCTERMS(name): return '{%s}%s' % (DCTERMS_NS, name) def NCX(name): return '{%s}%s' % (NCX_NS, name) def SVG(name): return '{%s}%s' % (SVG_NS, name) def XLINK(name): return '{%s}%s' % (XLINK_NS, name) @@ -211,11 +213,14 @@ class DirWriter(object): class Metadata(object): - DC_TERMS = set(['contributor', 'coverage', 'creator', 'date', 'description', - 'format', 'identifier', 'language', 'publisher', 'relation', - 'rights', 'source', 'subject', 'title', 'type']) + DC_TERMS = set(['contributor', 'coverage', 'creator', 'date', + 'description', 'format', 'identifier', 'language', + 'publisher', 'relation', 'rights', 'source', 'subject', + 'title', 'type']) CALIBRE_TERMS = set(['series', 'series_index', 'rating']) - OPF_ATTRS = set(['role', 'file-as', 'scheme', 'event']) + OPF_ATTRS = {'role': OPF('role'), 'file-as': OPF('file-as'), + 'scheme': OPF('scheme'), 'event': OPF('event'), + 'type': XSI('type'), 'id': 'id'} OPF1_NSMAP = {'dc': DC11_NS, 'oebpackage': OPF1_NS} OPF2_NSMAP = {'opf': OPF2_NS, 'dc': DC11_NS, 'dcterms': DCTERMS_NS, 'xsi': XSI_NS, 'calibre': CALIBRE_NS} @@ -240,30 +245,28 @@ class Metadata(object): for attr, value in attrib.items(): if isprefixname(value): attrib[attr] = qname(value, nsmap) - if attr in Metadata.OPF_ATTRS: - attrib[OPF(attr)] = attrib.pop(attr) + nsattr = Metadata.OPF_ATTRS.get(attr, attr) + if attr != nsattr: + attrib[nsattr] = attrib.pop(attr) self.__setattr__ = self._setattr def __getattr__(self, name): attr = name.replace('_', '-') if attr in Metadata.OPF_ATTRS: - attr = OPF(attr) - try: - return self.attrib[attr] - except KeyError: - raise AttributeError( - '%r object has no attribute %r' \ - % (self.__class__.__name__, name)) - + attr = Metadata.OPF_ATTRS[attr] + return self.attrib.get(attr, None) + raise AttributeError( + '%r object has no attribute %r' \ + % (self.__class__.__name__, name)) + def _setattr(self, name, value): attr = name.replace('_', '-') if attr in Metadata.OPF_ATTRS: - attr = OPF(attr) - if attr in self.attrib: + attr = Metadata.OPF_ATTRS[attr] self.attrib[attr] = value return super(Item, self).__setattr__(self, name, value) - + def __getitem__(self, key): return self.attrib[key] From daabc8bd5b3ab5e2d1aff98d33cca7a780149541 Mon Sep 17 00:00:00 2001 From: "Marshall T. Vandegrift" Date: Sat, 31 Jan 2009 14:04:13 -0500 Subject: [PATCH 2/6] Add option to prefer the author sort when generating Mobipocket books. --- src/calibre/ebooks/mobi/writer.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/calibre/ebooks/mobi/writer.py b/src/calibre/ebooks/mobi/writer.py index 49f4e076a4..800152748e 100644 --- a/src/calibre/ebooks/mobi/writer.py +++ b/src/calibre/ebooks/mobi/writer.py @@ -296,9 +296,11 @@ class Serializer(object): class MobiWriter(object): COLLAPSE_RE = re.compile(r'[ \t\r\n\v]+') - def __init__(self, compression=None, imagemax=None): + def __init__(self, compression=None, imagemax=None, + prefer_author_sort=False): self._compression = compression or UNCOMPRESSED self._imagemax = imagemax or OTHER_MAX_IMAGE_SIZE + self._prefer_author_sort = prefer_author_sort def dump(self, oeb, path): if hasattr(path, 'write'): @@ -457,7 +459,14 @@ class MobiWriter(object): for term in oeb.metadata: if term not in EXTH_CODES: continue code = EXTH_CODES[term] - for item in oeb.metadata[term]: + items = oeb.metadata[term] + if term == 'creator': + if self._prefer_author_sort: + creators = [unicode(c.file_as or c) for c in items] + else: + creators = [unicode(c) for c in items] + items = ['; '.join(creators)] + for item in items: data = self.COLLAPSE_RE.sub(' ', unicode(item)) if term == 'identifier': if data.lower().startswith('urn:isbn:'): @@ -535,6 +544,9 @@ def config(defaults=None): help=_('Render HTML tables as blocks of text instead of actual ' 'tables. This is neccessary if the HTML contains very large ' 'or complex tables.')) + mobi('prefer_author_sort', ['--prefer-author-sort'], default=False, + help=_('When present, use the author sorting information for ' + 'generating the Mobipocket author metadata.')) profiles = c.add_group('profiles', _('Device renderer profiles. ' 'Affects conversion of font sizes, image rescaling and rasterization ' 'of tables. Valid profiles are: %s.') % ', '.join(_profiles)) @@ -594,7 +606,8 @@ def oeb2mobi(opts, inpath): trimmer.transform(oeb, context) mobimlizer = MobiMLizer(ignore_tables=opts.ignore_tables) mobimlizer.transform(oeb, context) - writer = MobiWriter(compression=compression, imagemax=imagemax) + writer = MobiWriter(compression=compression, imagemax=imagemax, + prefer_author_sort=opts.prefer_author_sort) writer.dump(oeb, outpath) run_plugins_on_postprocess(outpath, 'mobi') logger.info(_('Output written to ') + outpath) From 047814a7a595539d0391b4be8b37aa5a62280a2b Mon Sep 17 00:00:00 2001 From: "Marshall T. Vandegrift" Date: Sat, 31 Jan 2009 14:06:16 -0500 Subject: [PATCH 3/6] Use attribute objects for Pythonic access to all allowed (and only allowed) OPF 2.0 metadata attributes. --- src/calibre/ebooks/mobi/writer.py | 2 +- src/calibre/ebooks/oeb/base.py | 62 ++++++++++++++++++++++--------- 2 files changed, 45 insertions(+), 19 deletions(-) diff --git a/src/calibre/ebooks/mobi/writer.py b/src/calibre/ebooks/mobi/writer.py index 800152748e..380bdbf518 100644 --- a/src/calibre/ebooks/mobi/writer.py +++ b/src/calibre/ebooks/mobi/writer.py @@ -471,7 +471,7 @@ class MobiWriter(object): if term == 'identifier': if data.lower().startswith('urn:isbn:'): data = data[9:] - elif item.get('scheme', '').lower() == 'isbn': + elif item.scheme.lower() == 'isbn': pass else: continue diff --git a/src/calibre/ebooks/oeb/base.py b/src/calibre/ebooks/oeb/base.py index be557703ae..b89be6b1ec 100644 --- a/src/calibre/ebooks/oeb/base.py +++ b/src/calibre/ebooks/oeb/base.py @@ -220,12 +220,37 @@ class Metadata(object): CALIBRE_TERMS = set(['series', 'series_index', 'rating']) OPF_ATTRS = {'role': OPF('role'), 'file-as': OPF('file-as'), 'scheme': OPF('scheme'), 'event': OPF('event'), - 'type': XSI('type'), 'id': 'id'} + 'type': XSI('type'), 'lang': XML('lang'), 'id': 'id'} OPF1_NSMAP = {'dc': DC11_NS, 'oebpackage': OPF1_NS} OPF2_NSMAP = {'opf': OPF2_NS, 'dc': DC11_NS, 'dcterms': DCTERMS_NS, 'xsi': XSI_NS, 'calibre': CALIBRE_NS} class Item(object): + class Attribute(object): + def __init__(self, attr, allowed=None): + if not callable(attr): + attr_, attr = attr, lambda term: attr_ + self.attr = attr + self.allowed = allowed + + def term_attr(self, obj): + term = obj.term + if namespace(term) != DC11_NS: + term = OPF('meta') + allowed = self.allowed + if allowed is not None and term not in allowed: + raise AttributeError( + 'attribute %r not valid for metadata term %r' \ + % (self.attr(term), barename(obj.term))) + return self.attr(term) + + def __get__(self, obj, cls): + if obj is None: return None + return obj.attrib.get(self.term_attr(obj), '') + + def __set__(self, obj, value): + obj.attrib[self.term_attr(obj)] = value + def __init__(self, term, value, attrib={}, nsmap={}, **kwargs): self.attrib = attrib = dict(attrib) self.nsmap = nsmap = dict(nsmap) @@ -246,26 +271,27 @@ class Metadata(object): if isprefixname(value): attrib[attr] = qname(value, nsmap) nsattr = Metadata.OPF_ATTRS.get(attr, attr) + if nsattr == OPF('scheme') and namespace(term) != DC11_NS: + # The opf:meta element takes @scheme, not @opf:scheme + nsattr = 'scheme' if attr != nsattr: attrib[nsattr] = attrib.pop(attr) - self.__setattr__ = self._setattr - def __getattr__(self, name): - attr = name.replace('_', '-') - if attr in Metadata.OPF_ATTRS: - attr = Metadata.OPF_ATTRS[attr] - return self.attrib.get(attr, None) - raise AttributeError( - '%r object has no attribute %r' \ - % (self.__class__.__name__, name)) - - def _setattr(self, name, value): - attr = name.replace('_', '-') - if attr in Metadata.OPF_ATTRS: - attr = Metadata.OPF_ATTRS[attr] - self.attrib[attr] = value - return - super(Item, self).__setattr__(self, name, value) + def scheme(term): + if term == OPF('meta'): + return 'scheme' + return OPF('scheme') + scheme = Attribute(scheme, [DC('identifier'), OPF('meta')]) + file_as = Attribute(OPF('file-as'), [DC('creator'), DC('contributor')]) + role = Attribute(OPF('role'), [DC('creator'), DC('contributor')]) + event = Attribute(OPF('event'), [DC('date')]) + id = Attribute('id') + type = Attribute(XSI('type'), [DC('date'), DC('format'), DC('type')]) + lang = Attribute(XML('lang'), [DC('contributor'), DC('coverage'), + DC('creator'), DC('publisher'), + DC('relation'), DC('rights'), + DC('source'), DC('subject'), + OPF('meta')]) def __getitem__(self, key): return self.attrib[key] From 24b8e1ba74203b0fe8ef90657d1dc73392e02e39 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 31 Jan 2009 13:00:09 -0800 Subject: [PATCH 4/6] Fix regression that prevented the rating from being updated in the main library view --- src/calibre/gui2/library.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/calibre/gui2/library.py b/src/calibre/gui2/library.py index 0e426aaf42..2b0148a194 100644 --- a/src/calibre/gui2/library.py +++ b/src/calibre/gui2/library.py @@ -575,6 +575,7 @@ class BooksModel(QAbstractTableModel): if column == 'rating': val = 0 if val < 0 else 5 if val > 5 else val val *= 2 + self.db.set_rating(id, val) elif column == 'series': pat = re.compile(r'\[(\d+)\]') match = pat.search(val) From 7fd614ec157acf5dab43ee5b1cedfe301589dd12 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 31 Jan 2009 16:33:09 -0800 Subject: [PATCH 5/6] Fix #1745 (Task details windows) --- src/calibre/gui2/dialogs/job_view.ui | 28 ++++++++++++++++++++++++++-- src/calibre/linux.py | 4 ++-- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/calibre/gui2/dialogs/job_view.ui b/src/calibre/gui2/dialogs/job_view.ui index ffd4419da9..f4b0086497 100644 --- a/src/calibre/gui2/dialogs/job_view.ui +++ b/src/calibre/gui2/dialogs/job_view.ui @@ -16,7 +16,7 @@ :/images/view.svg:/images/view.svg - + @@ -30,10 +30,34 @@ + + + + QDialogButtonBox::Ok + + + - + + + buttonBox + accepted() + Dialog + accept() + + + 617 + 442 + + + 206 + -5 + + + + diff --git a/src/calibre/linux.py b/src/calibre/linux.py index ffd4c46d7f..edcfa99342 100644 --- a/src/calibre/linux.py +++ b/src/calibre/linux.py @@ -485,14 +485,14 @@ def post_install(): os.unlink(f) def binary_install(): - manifest = os.path.join(sys.frozen_path, 'manifest') + manifest = os.path.join(getattr(sys, 'frozen_path'), 'manifest') exes = [x.strip() for x in open(manifest).readlines()] print 'Creating symlinks...' for exe in exes: dest = os.path.join('/usr', 'bin', exe) if os.path.exists(dest): os.remove(dest) - os.symlink(os.path.join(sys.frozen_path, exe), dest) + os.symlink(os.path.join(getattr(sys, 'frozen_path'), exe), dest) post_install() return 0 From 0bced7741e36afc00d3cf84c03f45b373e4e3459 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 31 Jan 2009 18:47:49 -0800 Subject: [PATCH 6/6] IGN:... --- src/calibre/web/fetch/simple.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/calibre/web/fetch/simple.py b/src/calibre/web/fetch/simple.py index 8dee540e01..f5ffaf08b8 100644 --- a/src/calibre/web/fetch/simple.py +++ b/src/calibre/web/fetch/simple.py @@ -139,6 +139,8 @@ class RecursiveFetcher(object, LoggingInterface): if self.keep_only_tags: body = Tag(soup, 'body') try: + if isinstance(self.keep_only_tags, dict): + self.keep_only_tags = [self.keep_only_tags] for spec in self.keep_only_tags: for tag in soup.find('body').findAll(**spec): body.insert(len(body.contents), tag)