Save more partial code for metadata saved in epubs and existing cal columns passed into update. Epub metadata version.

This commit is contained in:
Jim Miller 2015-05-05 16:13:27 -05:00
parent cf826b3e36
commit 52487456d7
7 changed files with 129 additions and 68 deletions

View file

@ -55,6 +55,9 @@ try:
except NameError:
pass # load_translations() added in calibre 1.9
from calibre.library.field_metadata import FieldMetadata
field_metadata = FieldMetadata()
# There are a number of things used several times that shouldn't be
# translated. This is just a way to make that easier by keeping them
# out of the _() strings.
@ -646,6 +649,11 @@ class PersonalIniTab(QWidget):
label.setWordWrap(True)
self.l.addWidget(label)
self.showcalcols = QPushButton(_('Show Calibre Column Names'), self)
self.showcalcols.setToolTip(_("FanFicFare passes the Calibre columns into the download/update process. This will show you the columns available by name."))
self.showcalcols.clicked.connect(self.show_showcalcols)
self.l.addWidget(self.showcalcols)
self.l.insertStretch(-1)
# let edit box fill the space.
@ -671,6 +679,26 @@ class PersonalIniTab(QWidget):
if d.result() == d.Accepted:
self.personalini = d.get_plain_text()
def show_showcalcols(self):
lines=[]
for k,f in field_metadata.iteritems():
if f['name']: # only if it has a human readable name.
lines.append(('calibre_std_'+k,f['name']))
for k, column in self.plugin_action.gui.library_view.model().custom_columns.iteritems():
# custom always have name.
lines.append(('calibre_cust_'+k[1:],column['name']))
lines.sort() # sort by key.
EditTextDialog(self,
'\n'.join(['%s (%s)'%(l,k) for (k,l) in lines]),
icon=self.windowIcon(),
title=_('Calibre Column Entry Names'),
label=_('Label (entry_name)'),
read_only=True,
save_size_name='fff:showcalcols').exec_()
class ReadingListTab(QWidget):
def __init__(self, parent_dialog, plugin_action):

View file

@ -1132,6 +1132,7 @@ class EditTextDialog(SizePersistedDialog):
def __init__(self, parent, text,
icon=None, title=None, label=None, tooltip=None,
read_only=False,
rejectreasons=[],reasonslabel=None,
save_size_name='fff:edit text dialog',
):
@ -1148,6 +1149,7 @@ class EditTextDialog(SizePersistedDialog):
self.textedit = QTextEdit(self)
self.textedit.setLineWrapMode(QTextEdit.NoWrap)
self.textedit.setReadOnly(read_only)
self.textedit.setText(text)
self.l.addWidget(self.textedit)

View file

@ -13,7 +13,7 @@ logger = logging.getLogger(__name__)
import time, os, copy, threading, re, platform, sys
from StringIO import StringIO
from functools import partial
from datetime import datetime, time
from datetime import datetime, time, date
from string import Template
import urllib
import email
@ -55,6 +55,9 @@ try:
except:
HAS_CALGC=False
from calibre.library.field_metadata import FieldMetadata
field_metadata = FieldMetadata()
from calibre_plugins.fanficfare_plugin.common_utils import (
set_plugin_icon_resources, get_icon, create_menu_action_unique,
get_library_uuid)
@ -64,7 +67,7 @@ from calibre_plugins.fanficfare_plugin.fanficfare import (
from calibre_plugins.fanficfare_plugin.fanficfare.epubutils import (
get_dcsource, get_dcsource_chaptercount, get_story_url_from_html,
get_epub_metadata)
get_epub_metadatas)
from calibre_plugins.fanficfare_plugin.fanficfare.geturls import (
get_urls_from_page, get_urls_from_html,get_urls_from_text,
@ -904,8 +907,8 @@ class FanFicFarePlugin(InterfaceAction):
if 1==0 and collision in (CALIBREONLY) and \
fileform == 'epub' and \
db.has_format(book['calibre_id'],'EPUB',index_is_id=True):
adapter.setStoryMetadata(get_epub_metadata(StringIO(db.format(book['calibre_id'],'EPUB',
index_is_id=True))))
adapter.setStoryMetadata(get_epub_metadatas(StringIO(db.format(book['calibre_id'],'EPUB',
index_is_id=True))))
# let other exceptions percolate up.
story = adapter.getStoryMetadataOnly(get_cover=False)
else:
@ -970,18 +973,40 @@ class FanFicFarePlugin(InterfaceAction):
book['status'] = _('Skipped')
return
## if existing book, populate existing custom column values in
## if existing book, populate existing calibre column values in
## metadata.
if 'calibre_id' in book:
book['calibre_columns']={}
for key, column in self.gui.library_view.model().custom_columns.iteritems():
val = db.get_custom(book['calibre_id'],
# std columns
mi = db.get_metadata(book['calibre_id'],index_is_id=True)
# book['calibre_columns']['calibre_std_identifiers']=\
# {'val':', '.join(["%s:%s"%(k,v) for (k,v) in mi.get_identifiers().iteritems()]),
# 'label':_('Ids')}
for k in mi.standard_field_keys():
# for k in mi:
(label,value,v,fmd) = mi.format_field_extended(k)
if not label and k in field_metadata:
label=field_metadata[k]['name']
key='calibre_std_'+k
if k == 'user_categories':
value=u', '.join(mi.get(k))
label=_('User Categories')
if label: # only if it has a human readable name.
book['calibre_columns'][key]={'val':value,'label':label}
logger.debug("%s(%s): %s"%(label,key,value))
# custom columns
for k, column in self.gui.library_view.model().custom_columns.iteritems():
key='calibre_cust_'+k[1:]
label=column['name']
value=db.get_custom(book['calibre_id'],
label=column['label'],
index_is_id=True)
if val:
#print("(%s)->(%s)"%('calibre.'+key,val))
# name: calibre.cust.namewithouthash
book['calibre_columns']['calibre_cust_'+key[1:]]={'val':val,'label':column['name']}
# custom always have name.
book['calibre_columns'][key]={'val':value,'label':label}
logger.debug("%s(%s): %s"%(label,key,value))
################################################################################################################################################33

View file

@ -404,11 +404,12 @@ class BaseSiteAdapter(Configurable):
self.metadataDone = True
return self.story
def setStoryMetadata(self,metadata):
self.story.metadata = metadata
self.metadataDone = True
if not self.story.getMetadataRaw('dateUpdated'):
self.story.setMetadata('dateUpdated',self.story.getMetadataRaw('datePublished'))
def setStoryMetadata(self,metadatas):
if metadatas:
self.story.loads_metadata(metadatas)
self.metadataDone = True
if not self.story.getMetadataRaw('dateUpdated'):
self.story.setMetadata('dateUpdated',self.story.getMetadataRaw('datePublished'))
def hookForUpdates(self,chaptercount):
"Usually not needed."

View file

@ -20,7 +20,7 @@ import bs4 as bs
UpdateData = namedtuple('UpdateData',
'source filecount soups images oldcover '
'calibrebookmark logfile metadata')
'calibrebookmark logfile metadatas')
def get_dcsource(inputio):
return get_update_data(inputio,getfilecount=False,getsoups=False).source
@ -29,8 +29,8 @@ def get_dcsource_chaptercount(inputio):
nt = get_update_data(inputio,getfilecount=True,getsoups=False)
return (nt.source,nt.filecount)
def get_epub_metadata(inputio):
return get_update_data(inputio,getfilecount=False,getsoups=False).metadata
def get_epub_metadatas(inputio):
return get_update_data(inputio,getfilecount=False,getsoups=False).metadatas
def get_update_data(inputio,
getfilecount=True,
@ -156,13 +156,12 @@ def get_update_data(inputio,
except:
pass
metadata = {}
metadatas = None
try:
for meta in firstmetadom.getElementsByTagName("meta"):
if meta.getAttribute("name")=="fanficfare:story_metadata":
#print("meta.getAttribute(content):%s"%meta.getAttribute("content"))
metadata=jsonloads(meta.getAttribute("content"))
metadatas=meta.getAttribute("content")
except Exception as e:
pass
# logger.info("metadata %s not found")
@ -172,7 +171,7 @@ def get_update_data(inputio,
#for k in images.keys():
#print("\tlongdesc:%s\n\tData len:%s\n"%(k,len(images[k])))
return UpdateData(source,filecount,soups,images,oldcover,
calibrebookmark,logfile,metadata)
calibrebookmark,logfile,metadatas)
def get_path_part(n):
relpath = os.path.dirname(n)
@ -217,36 +216,3 @@ def get_story_url_from_html(inputio,_is_good_url=None):
return ahref
return None
## why json doesn't define a date/time format is beyond me...
def datetime_decoder(d):
if isinstance(d, list):
pairs = enumerate(d)
elif isinstance(d, dict):
pairs = d.items()
result = []
for k,v in pairs:
if isinstance(v, basestring):
try:
# The %f format code is only supported in Python >= 2.6.
# For Python <= 2.5 strip off microseconds
# v = datetime.datetime.strptime(v.rsplit('.', 1)[0],
# '%Y-%m-%dT%H:%M:%S')
v = datetime.datetime.strptime(v, '%Y-%m-%dT%H:%M:%S.%f')
except ValueError:
try:
v = datetime.datetime.strptime(v, '%Y-%m-%dT%H:%M:%S')
except ValueError:
try:
v = datetime.datetime.strptime(v, '%Y-%m-%d')
except ValueError:
pass
elif isinstance(v, (dict, list)):
v = datetime_decoder(v)
result.append((k, v))
if isinstance(d, list):
return [x[1] for x in result]
elif isinstance(d, dict):
return dict(result)
def jsonloads(obj):
return json.loads(obj, object_hook=datetime_decoder)

View file

@ -18,6 +18,8 @@
import os, re
import urlparse
import string
import json
import datetime
from math import floor
from functools import partial
import logging
@ -544,6 +546,18 @@ class Story(Configurable):
else:
return self.join_list(key,retlist)
# for saving a string-ified copy of metadata.
def dumps_metadata(self):
md = {}
for k,v in self.metadata.iteritems():
if not k.startswith('calibre_'): # don't include items passed in for calibre cols.
md[k]=v
return json.dumps(md, default=datetime_encoder)
# for loading a string-ified copy of metadata.
def loads_metadata(self,s):
self.metadata = json.loads(s, object_hook=datetime_decoder)
def getMetadataRaw(self,key):
if self.isValidMetaEntry(key) and self.metadata.has_key(key):
return self.metadata[key]
@ -930,3 +944,39 @@ def commaGroups(s):
s = s[:-3]
return s + ','.join(reversed(groups))
## why json doesn't define a date/time format is beyond me...
## Also need to decode when reading back in.
def datetime_encoder(o):
if isinstance(o, (datetime.date, datetime.datetime, datetime.time)):
return o.isoformat()
## why json doesn't define a date/time format is beyond me...
def datetime_decoder(d):
if isinstance(d, list):
pairs = enumerate(d)
elif isinstance(d, dict):
pairs = d.items()
result = []
for k,v in pairs:
if isinstance(v, basestring):
try:
# The %f format code is only supported in Python >= 2.6.
# For Python <= 2.5 strip off microseconds
# v = datetime.datetime.strptime(v.rsplit('.', 1)[0],
# '%Y-%m-%dT%H:%M:%S')
v = datetime.datetime.strptime(v, '%Y-%m-%dT%H:%M:%S.%f')
except ValueError:
try:
v = datetime.datetime.strptime(v, '%Y-%m-%dT%H:%M:%S')
except ValueError:
try:
v = datetime.datetime.strptime(v, '%Y-%m-%d')
except ValueError:
pass
elif isinstance(v, (dict, list)):
v = datetime_decoder(v)
result.append((k, v))
if isinstance(d, list):
return [x[1] for x in result]
elif isinstance(d, dict):
return dict(result)

View file

@ -22,8 +22,6 @@ import zipfile
from zipfile import ZipFile, ZIP_STORED, ZIP_DEFLATED
import urllib
import re
import json
import datetime
## XML isn't as forgiving as HTML, so rather than generate as strings,
## use DOM to generate the XML files.
@ -494,7 +492,7 @@ div { margin: 0pt; padding: 0pt; }
## save a copy of *all*raw* metadata for future use
metadata.appendChild(newTag(contentdom,"meta",
attrs={"name":"fanficfare:story_metadata",
"content":jsondumps(self.story.metadata)}))
"content":self.story.dumps_metadata()}))
if self.getConfig("include_titlepage"):
items.append(("title_page","OEBPS/title_page.xhtml","application/xhtml+xml","Title Page"))
@ -697,12 +695,3 @@ def newTag(dom,name,attrs=None,text=None):
if( text is not None ):
tag.appendChild(dom.createTextNode(text))
return tag
## why json doesn't define a date/time format is beyond me...
## Also need to decode when reading back in.
def datetime_encoder(o):
if isinstance(o, (datetime.date, datetime.datetime, datetime.time)):
return o.isoformat()
def jsondumps(o):
return json.dumps(o, default=datetime_encoder)