mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Add support for updating metadata in FB2 files. Fixes #1001176 (Save metadata (title, authors, series) into FB2 file)
This commit is contained in:
commit
5d8edbe564
@ -421,6 +421,16 @@ class EPUBMetadataWriter(MetadataWriterPlugin):
|
|||||||
from calibre.ebooks.metadata.epub import set_metadata
|
from calibre.ebooks.metadata.epub import set_metadata
|
||||||
set_metadata(stream, mi, apply_null=self.apply_null)
|
set_metadata(stream, mi, apply_null=self.apply_null)
|
||||||
|
|
||||||
|
class FB2MetadataWriter(MetadataWriterPlugin):
|
||||||
|
|
||||||
|
name = 'Set FB2 metadata'
|
||||||
|
file_types = set(['fb2'])
|
||||||
|
description = _('Set metadata in %s files')%'FB2'
|
||||||
|
|
||||||
|
def set_metadata(self, stream, mi, type):
|
||||||
|
from calibre.ebooks.metadata.fb2 import set_metadata
|
||||||
|
set_metadata(stream, mi, apply_null=self.apply_null)
|
||||||
|
|
||||||
class HTMLZMetadataWriter(MetadataWriterPlugin):
|
class HTMLZMetadataWriter(MetadataWriterPlugin):
|
||||||
|
|
||||||
name = 'Set HTMLZ metadata'
|
name = 'Set HTMLZ metadata'
|
||||||
|
@ -22,6 +22,9 @@ NAMESPACES = {
|
|||||||
XPath = partial(etree.XPath, namespaces=NAMESPACES)
|
XPath = partial(etree.XPath, namespaces=NAMESPACES)
|
||||||
tostring = partial(etree.tostring, method='text', encoding=unicode)
|
tostring = partial(etree.tostring, method='text', encoding=unicode)
|
||||||
|
|
||||||
|
def FB2(tag):
|
||||||
|
return '{%s}%s'%(NAMESPACES['fb2'], tag)
|
||||||
|
|
||||||
def get_metadata(stream):
|
def get_metadata(stream):
|
||||||
''' Return fb2 metadata as a L{MetaInformation} object '''
|
''' Return fb2 metadata as a L{MetaInformation} object '''
|
||||||
|
|
||||||
@ -85,6 +88,7 @@ def _parse_authors(root):
|
|||||||
authors = []
|
authors = []
|
||||||
# pick up authors but only from 1 secrion <title-info>; otherwise it is not consistent!
|
# pick up authors but only from 1 secrion <title-info>; otherwise it is not consistent!
|
||||||
# Those are fallbacks: <src-title-info>, <document-info>
|
# Those are fallbacks: <src-title-info>, <document-info>
|
||||||
|
author = None
|
||||||
for author_sec in ['title-info', 'src-title-info', 'document-info']:
|
for author_sec in ['title-info', 'src-title-info', 'document-info']:
|
||||||
for au in XPath('//fb2:%s/fb2:author'%author_sec)(root):
|
for au in XPath('//fb2:%s/fb2:author'%author_sec)(root):
|
||||||
author = _parse_author(au)
|
author = _parse_author(au)
|
||||||
@ -211,8 +215,8 @@ def _parse_publisher(root, mi):
|
|||||||
def _parse_pubdate(root, mi):
|
def _parse_pubdate(root, mi):
|
||||||
year = XPath('number(//fb2:publish-info/fb2:year/text())')(root)
|
year = XPath('number(//fb2:publish-info/fb2:year/text())')(root)
|
||||||
if float.is_integer(year):
|
if float.is_integer(year):
|
||||||
# only year is available, so use 1-st of Jan
|
# only year is available, so use 2nd of June
|
||||||
mi.pubdate = datetime.date(int(year), 1, 1)
|
mi.pubdate = datetime.date(int(year), 6, 2)
|
||||||
|
|
||||||
def _parse_timestamp(root, mi):
|
def _parse_timestamp(root, mi):
|
||||||
#<date value="1996-12-03">03.12.1996</date>
|
#<date value="1996-12-03">03.12.1996</date>
|
||||||
@ -239,3 +243,93 @@ def _get_fbroot(stream):
|
|||||||
raw = xml_to_unicode(raw, strip_encoding_pats=True)[0]
|
raw = xml_to_unicode(raw, strip_encoding_pats=True)[0]
|
||||||
root = etree.fromstring(raw, parser=parser)
|
root = etree.fromstring(raw, parser=parser)
|
||||||
return root
|
return root
|
||||||
|
|
||||||
|
def _clear_meta_tags(doc, tag):
|
||||||
|
for parent in ('title-info', 'src-title-info', 'publish-info'):
|
||||||
|
for x in XPath('//fb2:%s/fb2:%s'%(parent, tag))(doc):
|
||||||
|
x.getparent().remove(x)
|
||||||
|
|
||||||
|
def _set_title(title_info, mi):
|
||||||
|
if not mi.is_null('title'):
|
||||||
|
_clear_meta_tags(title_info, 'book-title')
|
||||||
|
title = _get_or_create(title_info, 'book-title')
|
||||||
|
title.text = mi.title
|
||||||
|
|
||||||
|
def _set_comments(title_info, mi):
|
||||||
|
if not mi.is_null('comments'):
|
||||||
|
_clear_meta_tags(title_info, 'annotation')
|
||||||
|
title = _get_or_create(title_info, 'annotation')
|
||||||
|
title.text = mi.comments
|
||||||
|
|
||||||
|
|
||||||
|
def _set_authors(title_info, mi):
|
||||||
|
if not mi.is_null('authors'):
|
||||||
|
_clear_meta_tags(title_info, 'author')
|
||||||
|
for author in mi.authors:
|
||||||
|
author_parts = author.split()
|
||||||
|
if not author_parts: continue
|
||||||
|
atag = title_info.makeelement(FB2('author'))
|
||||||
|
title_info.insert(0, atag)
|
||||||
|
if len(author_parts) == 1:
|
||||||
|
_get_or_create(atag, 'nickname').text = author
|
||||||
|
else:
|
||||||
|
_get_or_create(atag, 'first-name').text = author_parts[0]
|
||||||
|
author_parts = author_parts[1:]
|
||||||
|
if len(author_parts) > 1:
|
||||||
|
_get_or_create(atag, 'middle-name', at_start=False).text = author_parts[0]
|
||||||
|
author_parts = author_parts[1:]
|
||||||
|
if author_parts:
|
||||||
|
_get_or_create(atag, 'last-name', at_start=False).text = ' '.join(author_parts)
|
||||||
|
|
||||||
|
def _set_tags(title_info, mi):
|
||||||
|
if not mi.is_null('tags'):
|
||||||
|
_clear_meta_tags(title_info, 'genre')
|
||||||
|
for t in mi.tags:
|
||||||
|
tag = title_info.makeelement(FB2('genre'))
|
||||||
|
tag.text = t
|
||||||
|
title_info.insert(0, tag)
|
||||||
|
|
||||||
|
def _set_series(title_info, mi):
|
||||||
|
if not mi.is_null('series'):
|
||||||
|
_clear_meta_tags(title_info, 'sequence')
|
||||||
|
seq = _get_or_create(title_info, 'sequence')
|
||||||
|
seq.set('name', mi.series)
|
||||||
|
try:
|
||||||
|
seq.set('number', '%g'%mi.series_index)
|
||||||
|
except:
|
||||||
|
seq.set('number', '1')
|
||||||
|
|
||||||
|
def _get_or_create(parent, tag, at_start=True):
|
||||||
|
ans = XPath('./fb2:'+tag)(parent)
|
||||||
|
if ans:
|
||||||
|
ans = ans[0]
|
||||||
|
else:
|
||||||
|
ans = parent.makeelement(FB2(tag))
|
||||||
|
if at_start:
|
||||||
|
parent.insert(0, ans)
|
||||||
|
else:
|
||||||
|
parent.append(ans)
|
||||||
|
return ans
|
||||||
|
|
||||||
|
def set_metadata(stream, mi, apply_null=False, update_timestamp=False):
|
||||||
|
stream.seek(0)
|
||||||
|
root = _get_fbroot(stream)
|
||||||
|
desc = _get_or_create(root, 'description')
|
||||||
|
ti = _get_or_create(desc, 'title-info')
|
||||||
|
|
||||||
|
indent = ti.text
|
||||||
|
|
||||||
|
_set_comments(ti, mi)
|
||||||
|
_set_series(ti, mi)
|
||||||
|
_set_tags(ti, mi)
|
||||||
|
_set_authors(ti, mi)
|
||||||
|
_set_title(ti, mi)
|
||||||
|
|
||||||
|
for child in ti:
|
||||||
|
child.tail = indent
|
||||||
|
|
||||||
|
stream.seek(0)
|
||||||
|
stream.truncate()
|
||||||
|
stream.write(etree.tostring(root, method='xml', encoding='utf-8',
|
||||||
|
xml_declaration=True))
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user