mirror of
https://github.com/JimmXinu/FanFicFare.git
synced 2026-05-09 05:21:13 +02:00
Save more partial code for metadata saved in epubs and existing cal columns passed into update. Epub metadata version.
This commit is contained in:
parent
cf826b3e36
commit
52487456d7
7 changed files with 129 additions and 68 deletions
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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."
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue