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
|
||||
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):
|
||||
|
||||
name = 'Set HTMLZ metadata'
|
||||
|
@ -22,6 +22,9 @@ NAMESPACES = {
|
||||
XPath = partial(etree.XPath, namespaces=NAMESPACES)
|
||||
tostring = partial(etree.tostring, method='text', encoding=unicode)
|
||||
|
||||
def FB2(tag):
|
||||
return '{%s}%s'%(NAMESPACES['fb2'], tag)
|
||||
|
||||
def get_metadata(stream):
|
||||
''' Return fb2 metadata as a L{MetaInformation} object '''
|
||||
|
||||
@ -85,6 +88,7 @@ def _parse_authors(root):
|
||||
authors = []
|
||||
# pick up authors but only from 1 secrion <title-info>; otherwise it is not consistent!
|
||||
# Those are fallbacks: <src-title-info>, <document-info>
|
||||
author = None
|
||||
for author_sec in ['title-info', 'src-title-info', 'document-info']:
|
||||
for au in XPath('//fb2:%s/fb2:author'%author_sec)(root):
|
||||
author = _parse_author(au)
|
||||
@ -211,8 +215,8 @@ def _parse_publisher(root, mi):
|
||||
def _parse_pubdate(root, mi):
|
||||
year = XPath('number(//fb2:publish-info/fb2:year/text())')(root)
|
||||
if float.is_integer(year):
|
||||
# only year is available, so use 1-st of Jan
|
||||
mi.pubdate = datetime.date(int(year), 1, 1)
|
||||
# only year is available, so use 2nd of June
|
||||
mi.pubdate = datetime.date(int(year), 6, 2)
|
||||
|
||||
def _parse_timestamp(root, mi):
|
||||
#<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]
|
||||
root = etree.fromstring(raw, parser=parser)
|
||||
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