mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-08 18:54:09 -04:00
GwR revisions for device profile, tweaks to Recently Added
This commit is contained in:
parent
4b6e4d9ae4
commit
096e53c484
@ -23,7 +23,7 @@ def gui_convert(input, output, recommendations, notification=DummyReporter(),
|
|||||||
|
|
||||||
plumber.run()
|
plumber.run()
|
||||||
|
|
||||||
def gui_catalog(fmt, title, dbspec, ids, out_file_name, sync, fmt_options,
|
def gui_catalog(fmt, title, dbspec, ids, out_file_name, sync, fmt_options, connected_device,
|
||||||
notification=DummyReporter(), log=None):
|
notification=DummyReporter(), log=None):
|
||||||
if log is None:
|
if log is None:
|
||||||
log = Log()
|
log = Log()
|
||||||
@ -44,6 +44,7 @@ def gui_catalog(fmt, title, dbspec, ids, out_file_name, sync, fmt_options,
|
|||||||
# Populate opts
|
# Populate opts
|
||||||
# opts.gui_search_text = something
|
# opts.gui_search_text = something
|
||||||
opts.catalog_title = title
|
opts.catalog_title = title
|
||||||
|
opts.connected_device = connected_device
|
||||||
opts.ids = ids
|
opts.ids = ids
|
||||||
opts.search_text = None
|
opts.search_text = None
|
||||||
opts.sort_by = None
|
opts.sort_by = None
|
||||||
|
@ -7,7 +7,7 @@ __docformat__ = 'restructuredtext en'
|
|||||||
Logic for setting up conversion jobs
|
Logic for setting up conversion jobs
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import cPickle
|
import cPickle, os
|
||||||
|
|
||||||
from PyQt4.Qt import QDialog, QProgressDialog, QString, QTimer, SIGNAL
|
from PyQt4.Qt import QDialog, QProgressDialog, QString, QTimer, SIGNAL
|
||||||
|
|
||||||
@ -236,7 +236,7 @@ def fetch_scheduled_recipe(arg):
|
|||||||
|
|
||||||
return 'gui_convert', args, _('Fetch news from ')+arg['title'], fmt.upper(), [pt]
|
return 'gui_convert', args, _('Fetch news from ')+arg['title'], fmt.upper(), [pt]
|
||||||
|
|
||||||
def generate_catalog(parent, dbspec, ids):
|
def generate_catalog(parent, dbspec, ids, device):
|
||||||
from calibre.gui2.dialogs.catalog import Catalog
|
from calibre.gui2.dialogs.catalog import Catalog
|
||||||
|
|
||||||
# Build the Catalog dialog in gui2.dialogs.catalog
|
# Build the Catalog dialog in gui2.dialogs.catalog
|
||||||
@ -248,6 +248,21 @@ def generate_catalog(parent, dbspec, ids):
|
|||||||
# Create the output file
|
# Create the output file
|
||||||
out = PersistentTemporaryFile(suffix='_catalog_out.'+d.catalog_format.lower())
|
out = PersistentTemporaryFile(suffix='_catalog_out.'+d.catalog_format.lower())
|
||||||
|
|
||||||
|
# Profile the connected device
|
||||||
|
# Parallel initialization in calibre.library.cli:command_catalog()
|
||||||
|
connected_device = { 'storage':None,'serial':None,'name':None}
|
||||||
|
if device:
|
||||||
|
storage = []
|
||||||
|
if device._main_prefix:
|
||||||
|
storage.append(os.path.join(device._main_prefix, device.EBOOK_DIR_MAIN))
|
||||||
|
if device._card_a_prefix:
|
||||||
|
storage.append(os.path.join(device._card_a_prefix, device.EBOOK_DIR_CARD_A))
|
||||||
|
if device._card_b_prefix:
|
||||||
|
storage.append(os.path.join(device._card_b_prefix, device.EBOOK_DIR_CARD_B))
|
||||||
|
connected_device = {'storage': storage,'serial':device.detected_device.serial,
|
||||||
|
'name':device.gui_name}
|
||||||
|
|
||||||
|
# These args are passed inline to gui2.convert.gui_conversion:gui_catalog
|
||||||
args = [
|
args = [
|
||||||
d.catalog_format,
|
d.catalog_format,
|
||||||
d.catalog_title,
|
d.catalog_title,
|
||||||
@ -255,12 +270,13 @@ def generate_catalog(parent, dbspec, ids):
|
|||||||
ids,
|
ids,
|
||||||
out.name,
|
out.name,
|
||||||
d.catalog_sync,
|
d.catalog_sync,
|
||||||
d.fmt_options
|
d.fmt_options,
|
||||||
|
connected_device
|
||||||
]
|
]
|
||||||
out.close()
|
out.close()
|
||||||
|
|
||||||
# This returns to gui2.ui:generate_catalog()
|
# This returns to gui2.ui:generate_catalog()
|
||||||
# Which then calls gui2.convert.gui_conversion:gui_catalog()
|
# Which then calls gui2.convert.gui_conversion:gui_catalog() with the args inline
|
||||||
return 'gui_catalog', args, _('Generate catalog'), out.name, d.catalog_sync, \
|
return 'gui_catalog', args, _('Generate catalog'), out.name, d.catalog_sync, \
|
||||||
d.catalog_title
|
d.catalog_title
|
||||||
|
|
||||||
|
@ -1379,7 +1379,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
|||||||
show=True)
|
show=True)
|
||||||
|
|
||||||
# Calling gui2.tools:generate_catalog()
|
# Calling gui2.tools:generate_catalog()
|
||||||
ret = generate_catalog(self, dbspec, ids)
|
ret = generate_catalog(self, dbspec, ids, self.device_manager.device)
|
||||||
if ret is None:
|
if ret is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import os, re, shutil, htmlentitydefs
|
import datetime, htmlentitydefs, os, re, shutil, time, traceback
|
||||||
|
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from datetime import date
|
from copy import deepcopy
|
||||||
|
|
||||||
from xml.sax.saxutils import escape
|
from xml.sax.saxutils import escape
|
||||||
|
|
||||||
from calibre import filesystem_encoding, prints, prepare_string_for_xml, strftime
|
from calibre import filesystem_encoding, prints, prepare_string_for_xml, strftime
|
||||||
@ -533,10 +534,11 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
catalog.copyResources()
|
catalog.copyResources()
|
||||||
catalog.buildSources()
|
catalog.buildSources()
|
||||||
'''
|
'''
|
||||||
|
# A single number creates 'Last x days' only.
|
||||||
# Number of discrete steps to catalog creation
|
# Multiple numbers create 'Last x days', 'x to y days ago' ...
|
||||||
# current_step = 0.0
|
# e.g, [7,15,30,60], [30]
|
||||||
# total_steps = 10.0
|
# [] = No date ranges added
|
||||||
|
DATE_RANGE=[30]
|
||||||
|
|
||||||
# basename output file basename
|
# basename output file basename
|
||||||
# creator dc:creator in OPF metadata
|
# creator dc:creator in OPF metadata
|
||||||
@ -551,10 +553,12 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
report_progress=DummyReporter(),
|
report_progress=DummyReporter(),
|
||||||
stylesheet="content/stylesheet.css"):
|
stylesheet="content/stylesheet.css"):
|
||||||
self.__opts = opts
|
self.__opts = opts
|
||||||
|
self.__authorClip = opts.authorClip
|
||||||
self.__authors = None
|
self.__authors = None
|
||||||
self.__basename = opts.basename
|
self.__basename = opts.basename
|
||||||
self.__booksByAuthor = None
|
self.__booksByAuthor = None
|
||||||
self.__booksByTitle = None
|
self.__booksByTitle = None
|
||||||
|
self.__booksByTitle_noSeriesPrefix = None
|
||||||
self.__catalogPath = PersistentTemporaryDirectory("_epub_mobi_catalog", prefix='')
|
self.__catalogPath = PersistentTemporaryDirectory("_epub_mobi_catalog", prefix='')
|
||||||
self.__contentDir = os.path.join(self.catalogPath, "content")
|
self.__contentDir = os.path.join(self.catalogPath, "content")
|
||||||
self.__currentStep = 0.0
|
self.__currentStep = 0.0
|
||||||
@ -581,6 +585,7 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
self.__thumbHeight = 0
|
self.__thumbHeight = 0
|
||||||
self.__title = opts.catalog_title
|
self.__title = opts.catalog_title
|
||||||
self.__totalSteps = 11.0
|
self.__totalSteps = 11.0
|
||||||
|
self.__useSeriesPrefixInTitlesSection = False
|
||||||
self.__verbose = opts.verbose
|
self.__verbose = opts.verbose
|
||||||
|
|
||||||
# Tweak build steps based on optional sections. 1 call for HTML, 1 for NCX
|
# Tweak build steps based on optional sections. 1 call for HTML, 1 for NCX
|
||||||
@ -600,6 +605,13 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
return property(fget=fget, fset=fset)
|
return property(fget=fget, fset=fset)
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
@dynamic_property
|
||||||
|
def authorClip(self):
|
||||||
|
def fget(self):
|
||||||
|
return self.__authorClip
|
||||||
|
def fset(self, val):
|
||||||
|
self.__authorClip = val
|
||||||
|
return property(fget=fget, fset=fset)
|
||||||
@dynamic_property
|
@dynamic_property
|
||||||
def authors(self):
|
def authors(self):
|
||||||
def fget(self):
|
def fget(self):
|
||||||
@ -629,6 +641,13 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
self.__booksByTitle = val
|
self.__booksByTitle = val
|
||||||
return property(fget=fget, fset=fset)
|
return property(fget=fget, fset=fset)
|
||||||
@dynamic_property
|
@dynamic_property
|
||||||
|
def booksByTitle_noSeriesPrefix(self):
|
||||||
|
def fget(self):
|
||||||
|
return self.__booksByTitle_noSeriesPrefix
|
||||||
|
def fset(self, val):
|
||||||
|
self.__booksByTitle_noSeriesPrefix = val
|
||||||
|
return property(fget=fget, fset=fset)
|
||||||
|
@dynamic_property
|
||||||
def catalogPath(self):
|
def catalogPath(self):
|
||||||
def fget(self):
|
def fget(self):
|
||||||
return self.__catalogPath
|
return self.__catalogPath
|
||||||
@ -799,6 +818,13 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
return self.__totalSteps
|
return self.__totalSteps
|
||||||
return property(fget=fget)
|
return property(fget=fget)
|
||||||
@dynamic_property
|
@dynamic_property
|
||||||
|
def useSeriesPrefixInTitlesSection(self):
|
||||||
|
def fget(self):
|
||||||
|
return self.__useSeriesPrefixInTitlesSection
|
||||||
|
def fset(self, val):
|
||||||
|
self.__useSeriesPrefixInTitlesSection = val
|
||||||
|
return property(fget=fget, fset=fset)
|
||||||
|
@dynamic_property
|
||||||
def verbose(self):
|
def verbose(self):
|
||||||
def fget(self):
|
def fget(self):
|
||||||
return self.__verbose
|
return self.__verbose
|
||||||
@ -971,7 +997,7 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
for token in p.contents:
|
for token in p.contents:
|
||||||
if token.string is not None:
|
if token.string is not None:
|
||||||
tokens.append(token.string)
|
tokens.append(token.string)
|
||||||
this_title['short_description'] = self.generateShortDescription(' '.join(tokens))
|
this_title['short_description'] = self.generateShortDescription(' '.join(tokens), dest="description")
|
||||||
else:
|
else:
|
||||||
this_title['description'] = None
|
this_title['description'] = None
|
||||||
this_title['short_description'] = None
|
this_title['short_description'] = None
|
||||||
@ -1109,10 +1135,10 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
# title<br />series series_index
|
# title<br />series series_index
|
||||||
brTag = Tag(soup,'br')
|
brTag = Tag(soup,'br')
|
||||||
title_tokens = title['title'].split(': ')
|
title_tokens = title['title'].split(': ')
|
||||||
emTag.insert(0, NavigableString(title_tokens[1]))
|
emTag.insert(0, escape(NavigableString(title_tokens[1])))
|
||||||
emTag.insert(1, brTag)
|
emTag.insert(1, brTag)
|
||||||
smallTag = Tag(soup,'small')
|
smallTag = Tag(soup,'small')
|
||||||
smallTag.insert(0,NavigableString(title_tokens[0]))
|
smallTag.insert(0, escape(NavigableString(title_tokens[0])))
|
||||||
emTag.insert(2, smallTag)
|
emTag.insert(2, smallTag)
|
||||||
else:
|
else:
|
||||||
emTag.insert(0, NavigableString(escape(title['title'])))
|
emTag.insert(0, NavigableString(escape(title['title'])))
|
||||||
@ -1287,8 +1313,29 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
dtc = 0
|
dtc = 0
|
||||||
current_letter = ""
|
current_letter = ""
|
||||||
|
|
||||||
|
# 2/14/10 7:11 AM Experimental: re-sort title list without leading series/series_index
|
||||||
|
if not self.useSeriesPrefixInTitlesSection:
|
||||||
|
nspt = deepcopy(self.booksByTitle)
|
||||||
|
for book in nspt:
|
||||||
|
if book['series']:
|
||||||
|
tokens = book['title'].split(': ')
|
||||||
|
book['title'] = '%s (%s)' % (tokens[1], tokens[0])
|
||||||
|
book['title_sort'] = self.generateSortTitle(book['title'])
|
||||||
|
nspt = sorted(nspt,
|
||||||
|
key=lambda x:(x['title_sort'].upper(), x['title_sort'].upper()))
|
||||||
|
self.booksByTitle_noSeriesPrefix = nspt
|
||||||
|
if False and self.verbose:
|
||||||
|
self.opts.log.info("no_series_prefix_titles: %d books" % len(nspt))
|
||||||
|
self.opts.log.info(" %-40s %-40s" % ('title', 'title_sort'))
|
||||||
|
for title in nspt:
|
||||||
|
self.opts.log.info((u" %-40s %-40s" % (title['title'][0:40],
|
||||||
|
title['title_sort'][0:40])).encode('utf-8'))
|
||||||
|
|
||||||
# Loop through the books by title
|
# Loop through the books by title
|
||||||
for book in self.booksByTitle:
|
title_list = self.booksByTitle
|
||||||
|
if not self.useSeriesPrefixInTitlesSection:
|
||||||
|
title_list = self.booksByTitle_noSeriesPrefix
|
||||||
|
for book in title_list:
|
||||||
if self.letter_or_symbol(book['title_sort'][0]) != current_letter :
|
if self.letter_or_symbol(book['title_sort'][0]) != current_letter :
|
||||||
# Start a new letter
|
# Start a new letter
|
||||||
current_letter = self.letter_or_symbol(book['title_sort'][0])
|
current_letter = self.letter_or_symbol(book['title_sort'][0])
|
||||||
@ -1513,7 +1560,7 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
# Write books by reverse chronological order
|
# Write books by reverse chronological order
|
||||||
self.updateProgressFullStep("'Recently Added'")
|
self.updateProgressFullStep("'Recently Added'")
|
||||||
|
|
||||||
def add_books_to_HTML(this_months_list, dtc):
|
def add_books_to_HTML_by_month(this_months_list, dtc):
|
||||||
if len(this_months_list):
|
if len(this_months_list):
|
||||||
|
|
||||||
this_months_list.sort(self.author_compare)
|
this_months_list.sort(self.author_compare)
|
||||||
@ -1598,10 +1645,56 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
dtc += 1
|
dtc += 1
|
||||||
return dtc
|
return dtc
|
||||||
|
|
||||||
|
def add_books_to_HTML_by_date_range(date_range_list, date_range, dtc):
|
||||||
|
if len(date_range_list):
|
||||||
|
pIndexTag = Tag(soup, "p")
|
||||||
|
pIndexTag['class'] = "date_index"
|
||||||
|
aTag = Tag(soup, "a")
|
||||||
|
aTag['name'] = date_range.replace(' ','')
|
||||||
|
pIndexTag.insert(0,aTag)
|
||||||
|
pIndexTag.insert(1,NavigableString(date_range))
|
||||||
|
divTag.insert(dtc,pIndexTag)
|
||||||
|
dtc += 1
|
||||||
|
|
||||||
# Sort titles case-insensitive
|
for new_entry in date_range_list:
|
||||||
self.booksByDate = sorted(self.booksByTitle,
|
# Add books
|
||||||
key=lambda x:(x['timestamp'], x['timestamp']),reverse=True)
|
pBookTag = Tag(soup, "p")
|
||||||
|
ptc = 0
|
||||||
|
|
||||||
|
# Prefix book with read/unread symbol
|
||||||
|
if new_entry['read']:
|
||||||
|
# check mark
|
||||||
|
pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL))
|
||||||
|
pBookTag['class'] = "read_book"
|
||||||
|
ptc += 1
|
||||||
|
else:
|
||||||
|
# hidden check mark
|
||||||
|
pBookTag['class'] = "unread_book"
|
||||||
|
pBookTag.insert(ptc,NavigableString(self.NOT_READ_SYMBOL))
|
||||||
|
ptc += 1
|
||||||
|
|
||||||
|
aTag = Tag(soup, "a")
|
||||||
|
aTag['href'] = "book_%d.html" % (int(float(new_entry['id'])))
|
||||||
|
aTag.insert(0,escape(new_entry['title']))
|
||||||
|
pBookTag.insert(ptc, aTag)
|
||||||
|
ptc += 1
|
||||||
|
|
||||||
|
# Dot
|
||||||
|
pBookTag.insert(ptc, NavigableString(" · "))
|
||||||
|
ptc += 1
|
||||||
|
|
||||||
|
# Link to author
|
||||||
|
emTag = Tag(soup, "em")
|
||||||
|
aTag = Tag(soup, "a")
|
||||||
|
aTag['href'] = "%s.html#%s" % ("ByAlphaAuthor", self.generateAuthorAnchor(new_entry['author']))
|
||||||
|
aTag.insert(0, NavigableString(new_entry['author']))
|
||||||
|
emTag.insert(0,aTag)
|
||||||
|
pBookTag.insert(ptc, emTag)
|
||||||
|
ptc += 1
|
||||||
|
|
||||||
|
divTag.insert(dtc, pBookTag)
|
||||||
|
dtc += 1
|
||||||
|
return dtc
|
||||||
|
|
||||||
friendly_name = "Recently Added"
|
friendly_name = "Recently Added"
|
||||||
|
|
||||||
@ -1640,20 +1733,63 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
divTag = Tag(soup, "div")
|
divTag = Tag(soup, "div")
|
||||||
dtc = 0
|
dtc = 0
|
||||||
|
|
||||||
current_date = date.fromordinal(1)
|
# Add books by date range
|
||||||
|
if self.useSeriesPrefixInTitlesSection:
|
||||||
|
self.booksByDateRange = sorted(self.booksByTitle,
|
||||||
|
key=lambda x:(x['timestamp'], x['timestamp']),reverse=True)
|
||||||
|
else:
|
||||||
|
nspt = deepcopy(self.booksByTitle)
|
||||||
|
for book in nspt:
|
||||||
|
if book['series']:
|
||||||
|
tokens = book['title'].split(': ')
|
||||||
|
book['title'] = '%s (%s)' % (tokens[1], tokens[0])
|
||||||
|
book['title_sort'] = self.generateSortTitle(book['title'])
|
||||||
|
self.booksByDateRange = sorted(nspt, key=lambda x:(x['timestamp'], x['timestamp']),reverse=True)
|
||||||
|
|
||||||
|
today = datetime.datetime.now()
|
||||||
|
date_range_list = []
|
||||||
|
today_time = datetime.datetime(today.year, today.month, today.day)
|
||||||
|
books_added_in_date_range = False
|
||||||
|
for (i, date) in enumerate(self.DATE_RANGE):
|
||||||
|
date_range_limit = self.DATE_RANGE[i]
|
||||||
|
if i:
|
||||||
|
date_range = '%d to %d days ago' % (self.DATE_RANGE[i-1], self.DATE_RANGE[i])
|
||||||
|
else:
|
||||||
|
date_range = 'Last %d days' % (self.DATE_RANGE[i])
|
||||||
|
for book in self.booksByDateRange:
|
||||||
|
book_time = datetime.datetime(book['timestamp'].year, book['timestamp'].month, book['timestamp'].day)
|
||||||
|
if (today_time-book_time).days <= date_range_limit:
|
||||||
|
#print "generateHTMLByDateAdded: %s added %d days ago" % (book['title'], (today_time-book_time).days)
|
||||||
|
date_range_list.append(book)
|
||||||
|
books_added_in_date_range = True
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
dtc = add_books_to_HTML_by_date_range(date_range_list, date_range, dtc)
|
||||||
|
date_range_list = [book]
|
||||||
|
|
||||||
|
if books_added_in_date_range:
|
||||||
|
# Add an <hr> separating date ranges from months
|
||||||
|
hrTag = Tag(soup,'hr')
|
||||||
|
divTag.insert(dtc,hrTag)
|
||||||
|
dtc += 1
|
||||||
|
|
||||||
|
# Sort titles case-insensitive for by month using series prefix
|
||||||
|
self.booksByMonth = sorted(self.booksByTitle,
|
||||||
|
key=lambda x:(x['timestamp'], x['timestamp']),reverse=True)
|
||||||
|
|
||||||
# Loop through books by date
|
# Loop through books by date
|
||||||
|
current_date = datetime.date.fromordinal(1)
|
||||||
this_months_list = []
|
this_months_list = []
|
||||||
for book in self.booksByDate:
|
for book in self.booksByMonth:
|
||||||
if book['timestamp'].month != current_date.month or \
|
if book['timestamp'].month != current_date.month or \
|
||||||
book['timestamp'].year != current_date.year:
|
book['timestamp'].year != current_date.year:
|
||||||
dtc = add_books_to_HTML(this_months_list, dtc)
|
dtc = add_books_to_HTML_by_month(this_months_list, dtc)
|
||||||
this_months_list = []
|
this_months_list = []
|
||||||
current_date = book['timestamp'].date()
|
current_date = book['timestamp'].date()
|
||||||
this_months_list.append(book)
|
this_months_list.append(book)
|
||||||
|
|
||||||
# Add the last month's list
|
# Add the last month's list
|
||||||
add_books_to_HTML(this_months_list, dtc)
|
add_books_to_HTML_by_month(this_months_list, dtc)
|
||||||
|
|
||||||
# Add the divTag to the body
|
# Add the divTag to the body
|
||||||
body.insert(btc, divTag)
|
body.insert(btc, divTag)
|
||||||
@ -2055,6 +2191,7 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
# Loop over the titles
|
# Loop over the titles
|
||||||
sort_descriptions_by = self.booksByAuthor if self.opts.sort_descriptions_by_author \
|
sort_descriptions_by = self.booksByAuthor if self.opts.sort_descriptions_by_author \
|
||||||
else self.booksByTitle
|
else self.booksByTitle
|
||||||
|
|
||||||
for book in sort_descriptions_by:
|
for book in sort_descriptions_by:
|
||||||
navPointVolumeTag = Tag(ncx_soup, 'navPoint')
|
navPointVolumeTag = Tag(ncx_soup, 'navPoint')
|
||||||
navPointVolumeTag['class'] = "article"
|
navPointVolumeTag['class'] = "article"
|
||||||
@ -2065,9 +2202,9 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
textTag = Tag(ncx_soup, "text")
|
textTag = Tag(ncx_soup, "text")
|
||||||
if book['series']:
|
if book['series']:
|
||||||
tokens = book['title'].split(': ')
|
tokens = book['title'].split(': ')
|
||||||
textTag.insert(0, NavigableString(self.formatNCXText('%s (%s)' % (tokens[1], tokens[0]))))
|
textTag.insert(0, NavigableString(self.formatNCXText('%s (%s)' % (tokens[1], tokens[0]), dest='title')))
|
||||||
else:
|
else:
|
||||||
textTag.insert(0, NavigableString(self.formatNCXText(book['title'])))
|
textTag.insert(0, NavigableString(self.formatNCXText(book['title'], dest='title')))
|
||||||
navLabelTag.insert(0,textTag)
|
navLabelTag.insert(0,textTag)
|
||||||
navPointVolumeTag.insert(0,navLabelTag)
|
navPointVolumeTag.insert(0,navLabelTag)
|
||||||
|
|
||||||
@ -2079,10 +2216,10 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
# Add the author tag
|
# Add the author tag
|
||||||
cmTag = Tag(ncx_soup, '%s' % 'calibre:meta')
|
cmTag = Tag(ncx_soup, '%s' % 'calibre:meta')
|
||||||
cmTag['name'] = "author"
|
cmTag['name'] = "author"
|
||||||
navStr = '%s | %s' % (self.formatNCXText(book['author']),
|
navStr = '%s | %s' % (self.formatNCXText(book['author'], dest='author'),
|
||||||
book['date'].split()[1])
|
book['date'].split()[1])
|
||||||
if 'tags' in book:
|
if 'tags' in book:
|
||||||
navStr += ' | %s' % self.formatNCXText(' · '.join(sorted(book['tags'])))
|
navStr = self.formatNCXText(navStr + ' | ' + ' · '.join(sorted(book['tags'])), dest='author')
|
||||||
cmTag.insert(0, NavigableString(navStr))
|
cmTag.insert(0, NavigableString(navStr))
|
||||||
navPointVolumeTag.insert(2, cmTag)
|
navPointVolumeTag.insert(2, cmTag)
|
||||||
|
|
||||||
@ -2090,7 +2227,7 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
if book['short_description']:
|
if book['short_description']:
|
||||||
cmTag = Tag(ncx_soup, '%s' % 'calibre:meta')
|
cmTag = Tag(ncx_soup, '%s' % 'calibre:meta')
|
||||||
cmTag['name'] = "description"
|
cmTag['name'] = "description"
|
||||||
cmTag.insert(0, NavigableString(self.formatNCXText(book['short_description'])))
|
cmTag.insert(0, NavigableString(self.formatNCXText(book['short_description'], dest='description')))
|
||||||
navPointVolumeTag.insert(3, cmTag)
|
navPointVolumeTag.insert(3, cmTag)
|
||||||
|
|
||||||
# Add this volume to the section tag
|
# Add this volume to the section tag
|
||||||
@ -2108,7 +2245,7 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
|
|
||||||
def add_to_books_by_letter(current_book_list):
|
def add_to_books_by_letter(current_book_list):
|
||||||
current_book_list = " • ".join(current_book_list)
|
current_book_list = " • ".join(current_book_list)
|
||||||
current_book_list = self.generateShortDescription(self.formatNCXText(current_book_list))
|
current_book_list = self.formatNCXText(current_book_list, dest="description")
|
||||||
books_by_letter.append(current_book_list)
|
books_by_letter.append(current_book_list)
|
||||||
|
|
||||||
soup = self.ncxSoup
|
soup = self.ncxSoup
|
||||||
@ -2137,11 +2274,16 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
books_by_letter = []
|
books_by_letter = []
|
||||||
|
|
||||||
# Loop over the titles, find start of each letter, add description_preview_count books
|
# Loop over the titles, find start of each letter, add description_preview_count books
|
||||||
current_letter = self.letter_or_symbol(self.booksByTitle[0]['title_sort'][0])
|
# Special switch for using different title list
|
||||||
|
if self.useSeriesPrefixInTitlesSection:
|
||||||
|
title_list = self.booksByTitle
|
||||||
|
else:
|
||||||
|
title_list = self.booksByTitle_noSeriesPrefix
|
||||||
|
current_letter = self.letter_or_symbol(title_list[0]['title_sort'][0])
|
||||||
title_letters = [current_letter]
|
title_letters = [current_letter]
|
||||||
current_book_list = []
|
current_book_list = []
|
||||||
current_book = ""
|
current_book = ""
|
||||||
for book in self.booksByTitle:
|
for book in title_list:
|
||||||
if self.letter_or_symbol(book['title_sort'][0]) != current_letter:
|
if self.letter_or_symbol(book['title_sort'][0]) != current_letter:
|
||||||
# Save the old list
|
# Save the old list
|
||||||
add_to_books_by_letter(current_book_list)
|
add_to_books_by_letter(current_book_list)
|
||||||
@ -2180,7 +2322,7 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
if self.generateForKindle:
|
if self.generateForKindle:
|
||||||
cmTag = Tag(soup, '%s' % 'calibre:meta')
|
cmTag = Tag(soup, '%s' % 'calibre:meta')
|
||||||
cmTag['name'] = "description"
|
cmTag['name'] = "description"
|
||||||
cmTag.insert(0, NavigableString(self.formatNCXText(books)))
|
cmTag.insert(0, NavigableString(self.formatNCXText(books, dest='description')))
|
||||||
navPointByLetterTag.insert(2, cmTag)
|
navPointByLetterTag.insert(2, cmTag)
|
||||||
|
|
||||||
navPointTag.insert(nptc, navPointByLetterTag)
|
navPointTag.insert(nptc, navPointByLetterTag)
|
||||||
@ -2197,7 +2339,7 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
|
|
||||||
def add_to_author_list(current_author_list, current_letter):
|
def add_to_author_list(current_author_list, current_letter):
|
||||||
current_author_list = " • ".join(current_author_list)
|
current_author_list = " • ".join(current_author_list)
|
||||||
current_author_list = self.generateShortDescription(self.formatNCXText(current_author_list))
|
current_author_list = self.formatNCXText(current_author_list, dest="description")
|
||||||
master_author_list.append((current_author_list, current_letter))
|
master_author_list.append((current_author_list, current_letter))
|
||||||
|
|
||||||
soup = self.ncxSoup
|
soup = self.ncxSoup
|
||||||
@ -2287,9 +2429,15 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
def add_to_master_month_list(current_titles_list):
|
def add_to_master_month_list(current_titles_list):
|
||||||
book_count = len(current_titles_list)
|
book_count = len(current_titles_list)
|
||||||
current_titles_list = " • ".join(current_titles_list)
|
current_titles_list = " • ".join(current_titles_list)
|
||||||
current_titles_list = self.generateShortDescription(self.formatNCXText(current_titles_list))
|
current_titles_list = self.formatNCXText(current_titles_list, dest='description')
|
||||||
master_month_list.append((current_titles_list, current_date, book_count))
|
master_month_list.append((current_titles_list, current_date, book_count))
|
||||||
|
|
||||||
|
def add_to_master_date_range_list(current_titles_list):
|
||||||
|
book_count = len(current_titles_list)
|
||||||
|
current_titles_list = " • ".join(current_titles_list)
|
||||||
|
current_titles_list = self.formatNCXText(current_titles_list, dest='description')
|
||||||
|
master_date_range_list.append((current_titles_list, date_range, book_count))
|
||||||
|
|
||||||
soup = self.ncxSoup
|
soup = self.ncxSoup
|
||||||
HTML_file = "content/ByDateAdded.html"
|
HTML_file = "content/ByDateAdded.html"
|
||||||
body = soup.find("navPoint")
|
body = soup.find("navPoint")
|
||||||
@ -2315,15 +2463,74 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
navPointTag.insert(nptc, contentTag)
|
navPointTag.insert(nptc, contentTag)
|
||||||
nptc += 1
|
nptc += 1
|
||||||
|
|
||||||
|
# Create an NCX article entry for each date range
|
||||||
|
current_titles_list = []
|
||||||
|
master_date_range_list = []
|
||||||
|
today = datetime.datetime.now()
|
||||||
|
today_time = datetime.datetime(today.year, today.month, today.day)
|
||||||
|
for (i,date) in enumerate(self.DATE_RANGE):
|
||||||
|
if i:
|
||||||
|
date_range = '%d to %d days ago' % (self.DATE_RANGE[i-1], self.DATE_RANGE[i])
|
||||||
|
else:
|
||||||
|
date_range = 'Last %d days' % (self.DATE_RANGE[i])
|
||||||
|
date_range_limit = self.DATE_RANGE[i]
|
||||||
|
for book in self.booksByDateRange:
|
||||||
|
book_time = datetime.datetime(book['timestamp'].year, book['timestamp'].month, book['timestamp'].day)
|
||||||
|
if (today_time-book_time).days <= date_range_limit:
|
||||||
|
#print "generateNCXByDateAdded: %s added %d days ago" % (book['title'], (today_time-book_time).days)
|
||||||
|
current_titles_list.append(book['title'])
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
if current_titles_list:
|
||||||
|
add_to_master_date_range_list(current_titles_list)
|
||||||
|
current_titles_list = [book['title']]
|
||||||
|
|
||||||
|
# Add *article* entries for each populated date range
|
||||||
|
# master_date_range_list{}: [0]:titles list [1]:datestr
|
||||||
|
for books_by_date_range in master_date_range_list:
|
||||||
|
navPointByDateRangeTag = Tag(soup, 'navPoint')
|
||||||
|
navPointByDateRangeTag['class'] = "article"
|
||||||
|
navPointByDateRangeTag['id'] = "%s-ID" % books_by_date_range[1].replace(' ','')
|
||||||
|
navPointTag['playOrder'] = self.playOrder
|
||||||
|
self.playOrder += 1
|
||||||
|
navLabelTag = Tag(soup, 'navLabel')
|
||||||
|
textTag = Tag(soup, 'text')
|
||||||
|
textTag.insert(0, NavigableString(books_by_date_range[1]))
|
||||||
|
navLabelTag.insert(0, textTag)
|
||||||
|
navPointByDateRangeTag.insert(0,navLabelTag)
|
||||||
|
contentTag = Tag(soup, 'content')
|
||||||
|
contentTag['src'] = "%s#%s" % (HTML_file,
|
||||||
|
books_by_date_range[1].replace(' ',''))
|
||||||
|
|
||||||
|
navPointByDateRangeTag.insert(1,contentTag)
|
||||||
|
|
||||||
|
if self.generateForKindle:
|
||||||
|
cmTag = Tag(soup, '%s' % 'calibre:meta')
|
||||||
|
cmTag['name'] = "description"
|
||||||
|
cmTag.insert(0, NavigableString(books_by_date_range[0]))
|
||||||
|
navPointByDateRangeTag.insert(2, cmTag)
|
||||||
|
|
||||||
|
cmTag = Tag(soup, '%s' % 'calibre:meta')
|
||||||
|
cmTag['name'] = "author"
|
||||||
|
navStr = '%d titles' % books_by_date_range[2] if books_by_date_range[2] > 1 else \
|
||||||
|
'%d title' % books_by_date_range[2]
|
||||||
|
cmTag.insert(0, NavigableString(navStr))
|
||||||
|
navPointByDateRangeTag.insert(3, cmTag)
|
||||||
|
|
||||||
|
navPointTag.insert(nptc, navPointByDateRangeTag)
|
||||||
|
nptc += 1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Create an NCX article entry for each populated month
|
# Create an NCX article entry for each populated month
|
||||||
# Loop over the booksByDate list, find start of each month,
|
# Loop over the booksByDate list, find start of each month,
|
||||||
# add description_preview_count titles
|
# add description_preview_count titles
|
||||||
# master_month_list(list,date,count)
|
# master_month_list(list,date,count)
|
||||||
current_titles_list = []
|
current_titles_list = []
|
||||||
master_month_list = []
|
master_month_list = []
|
||||||
current_date = self.booksByDate[0]['timestamp']
|
current_date = self.booksByMonth[0]['timestamp']
|
||||||
|
|
||||||
for book in self.booksByDate:
|
for book in self.booksByMonth:
|
||||||
if book['timestamp'].month != current_date.month or \
|
if book['timestamp'].month != current_date.month or \
|
||||||
book['timestamp'].year != current_date.year:
|
book['timestamp'].year != current_date.year:
|
||||||
# Save the old lists
|
# Save the old lists
|
||||||
@ -2432,7 +2639,7 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
if self.genre_tags_dict[friendly_tag] == genre['tag']:
|
if self.genre_tags_dict[friendly_tag] == genre['tag']:
|
||||||
normalized_tag = self.genre_tags_dict[friendly_tag]
|
normalized_tag = self.genre_tags_dict[friendly_tag]
|
||||||
break
|
break
|
||||||
textTag.insert(0, self.formatNCXText(NavigableString(friendly_tag)))
|
textTag.insert(0, self.formatNCXText(NavigableString(friendly_tag), dest='description'))
|
||||||
navLabelTag.insert(0,textTag)
|
navLabelTag.insert(0,textTag)
|
||||||
navPointVolumeTag.insert(0,navLabelTag)
|
navPointVolumeTag.insert(0,navLabelTag)
|
||||||
contentTag = Tag(ncx_soup, "content")
|
contentTag = Tag(ncx_soup, "content")
|
||||||
@ -2463,15 +2670,15 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
title_range = "%s -\n%s" % (genre['titles_spanned'][0][1], genre['titles_spanned'][1][1])
|
title_range = "%s -\n%s" % (genre['titles_spanned'][0][1], genre['titles_spanned'][1][1])
|
||||||
else:
|
else:
|
||||||
title_range = "%s" % (genre['titles_spanned'][0][1])
|
title_range = "%s" % (genre['titles_spanned'][0][1])
|
||||||
cmTag.insert(0, NavigableString(self.formatNCXText(title_range)))
|
cmTag.insert(0, NavigableString(self.formatNCXText(title_range, dest='description')))
|
||||||
else:
|
else:
|
||||||
# Form 2: title • title • title ...
|
# Form 2: title • title • title ...
|
||||||
titles = []
|
titles = []
|
||||||
for title in genre['books']:
|
for title in genre['books']:
|
||||||
titles.append(title['title'])
|
titles.append(title['title'])
|
||||||
titles = sorted(titles, key=lambda x:(self.generateSortTitle(x),self.generateSortTitle(x)))
|
titles = sorted(titles, key=lambda x:(self.generateSortTitle(x),self.generateSortTitle(x)))
|
||||||
titles_list = self.generateShortDescription(u" • ".join(titles))
|
titles_list = self.generateShortDescription(u" • ".join(titles), dest="description")
|
||||||
cmTag.insert(0, NavigableString(self.formatNCXText(titles_list)))
|
cmTag.insert(0, NavigableString(self.formatNCXText(titles_list, dest='description')))
|
||||||
|
|
||||||
navPointVolumeTag.insert(3, cmTag)
|
navPointVolumeTag.insert(3, cmTag)
|
||||||
|
|
||||||
@ -2652,7 +2859,7 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
|
|
||||||
return genre_tags_dict
|
return genre_tags_dict
|
||||||
|
|
||||||
def formatNCXText(self, description):
|
def formatNCXText(self, description, dest=None):
|
||||||
# Kindle TOC descriptions won't render certain characters
|
# Kindle TOC descriptions won't render certain characters
|
||||||
# Fix up
|
# Fix up
|
||||||
massaged = unicode(BeautifulStoneSoup(description, convertEntities=BeautifulStoneSoup.HTML_ENTITIES))
|
massaged = unicode(BeautifulStoneSoup(description, convertEntities=BeautifulStoneSoup.HTML_ENTITIES))
|
||||||
@ -2660,7 +2867,11 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
# Replace '&' with '&'
|
# Replace '&' with '&'
|
||||||
massaged = re.sub("&","&", massaged)
|
massaged = re.sub("&","&", massaged)
|
||||||
|
|
||||||
return massaged.strip()
|
if massaged.strip() and dest:
|
||||||
|
#print traceback.print_stack(limit=3)
|
||||||
|
return self.generateShortDescription(massaged.strip(), dest=dest)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
def generateAuthorAnchor(self, author):
|
def generateAuthorAnchor(self, author):
|
||||||
# Strip white space to ''
|
# Strip white space to ''
|
||||||
@ -2928,27 +3139,41 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
title['title'])
|
title['title'])
|
||||||
return series_title
|
return series_title
|
||||||
|
|
||||||
def generateShortDescription(self, description):
|
def generateShortDescription(self, description, dest=None):
|
||||||
# Truncate the description to description_clip, on word boundaries if necessary
|
# Truncate the description, on word boundaries if necessary
|
||||||
if not description:
|
# Possible destinations:
|
||||||
return None
|
# description NCX summary
|
||||||
|
# title NCX title
|
||||||
|
# author NCX author
|
||||||
|
|
||||||
if not self.descriptionClip:
|
def shortDescription(description, limit):
|
||||||
return description
|
|
||||||
|
|
||||||
if len(description) < self.descriptionClip:
|
|
||||||
return description
|
|
||||||
|
|
||||||
# Start adding words until we reach description_clip
|
|
||||||
short_description = ""
|
short_description = ""
|
||||||
words = description.split()
|
words = description.split()
|
||||||
for word in words:
|
for word in words:
|
||||||
short_description += word + " "
|
short_description += word + " "
|
||||||
if len(short_description) > self.descriptionClip:
|
if len(short_description) > limit:
|
||||||
short_description += "..."
|
short_description += "..."
|
||||||
return short_description
|
return short_description
|
||||||
|
|
||||||
return short_description
|
if not description:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if dest == 'title':
|
||||||
|
# No truncation for titles, let the device deal with it
|
||||||
|
return description
|
||||||
|
elif dest == 'author':
|
||||||
|
if self.authorClip and len(description) < self.authorClip:
|
||||||
|
return description
|
||||||
|
else:
|
||||||
|
return shortDescription(description, self.authorClip)
|
||||||
|
elif dest == 'description':
|
||||||
|
if self.descriptionClip and len(description) < self.descriptionClip:
|
||||||
|
return description
|
||||||
|
else:
|
||||||
|
return shortDescription(description, self.descriptionClip)
|
||||||
|
else:
|
||||||
|
print " returning description with unspecified destination '%s'" % description
|
||||||
|
raise RuntimeError
|
||||||
|
|
||||||
def generateSortTitle(self, title):
|
def generateSortTitle(self, title):
|
||||||
# Convert the actual title to a string suitable for sorting.
|
# Convert the actual title to a string suitable for sorting.
|
||||||
@ -3172,12 +3397,17 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
|
|
||||||
# Add local options
|
# Add local options
|
||||||
opts.creator = "calibre"
|
opts.creator = "calibre"
|
||||||
|
|
||||||
|
# Finalize output_profile
|
||||||
op = self.opts.output_profile
|
op = self.opts.output_profile
|
||||||
if op is None:
|
if op is None:
|
||||||
op = 'default'
|
op = 'default'
|
||||||
|
if opts.connected_device['name'] and 'kindle' in opts.connected_device['name'].lower():
|
||||||
|
op = "kindle_dx" if opts.connected_device['serial'][:4] in ['B004','B005'] else 'kindle'
|
||||||
|
opts.descriptionClip = 380 if op.endswith('dx') or 'kindle' not in op else 100
|
||||||
|
opts.authorClip = 100 if op.endswith('dx') or 'kindle' not in op else 60
|
||||||
self.opts.output_profile = op
|
self.opts.output_profile = op
|
||||||
|
|
||||||
opts.descriptionClip = 380 if op.endswith('dx') or 'kindle' not in op else 90
|
|
||||||
opts.basename = "Catalog"
|
opts.basename = "Catalog"
|
||||||
opts.cli_environment = not hasattr(opts,'sync')
|
opts.cli_environment = not hasattr(opts,'sync')
|
||||||
# GwR *** hardwired to sort by author, could be an option if passed in opts
|
# GwR *** hardwired to sort by author, could be an option if passed in opts
|
||||||
@ -3205,13 +3435,22 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
opts.exclude_genre = '\[^.\]'
|
opts.exclude_genre = '\[^.\]'
|
||||||
log(" converting empty exclude_genre to '\[^.\]'")
|
log(" converting empty exclude_genre to '\[^.\]'")
|
||||||
|
|
||||||
|
if opts.connected_device['name']:
|
||||||
|
log(" connected_device: '%s' #%s%s " % (opts.connected_device['name'],
|
||||||
|
opts.connected_device['serial'][0:4],
|
||||||
|
'x' * (len(opts.connected_device['serial']) - 4)))
|
||||||
|
for storage in opts.connected_device['storage']:
|
||||||
|
if storage:
|
||||||
|
log(" mount point: %s" % storage)
|
||||||
|
# for book in opts.connected_device['books']:
|
||||||
|
# log("%s: %s" % (book.title, book.path))
|
||||||
|
|
||||||
# Display opts
|
# Display opts
|
||||||
keys = opts_dict.keys()
|
keys = opts_dict.keys()
|
||||||
keys.sort()
|
keys.sort()
|
||||||
log(" opts:")
|
log(" opts:")
|
||||||
|
|
||||||
for key in keys:
|
for key in keys:
|
||||||
if key in ['catalog_title','exclude_genre','exclude_tags',
|
if key in ['catalog_title','authorClip','descriptionClip','exclude_genre','exclude_tags',
|
||||||
'note_tag','numbers_as_text','read_tag',
|
'note_tag','numbers_as_text','read_tag',
|
||||||
'search_text','sort_by','sort_descriptions_by_author','sync']:
|
'search_text','sort_by','sort_descriptions_by_author','sync']:
|
||||||
log(" %s: %s" % (key, opts_dict[key]))
|
log(" %s: %s" % (key, opts_dict[key]))
|
||||||
|
@ -673,6 +673,10 @@ def command_catalog(args, dbpath):
|
|||||||
if opts.ids:
|
if opts.ids:
|
||||||
opts.ids = [int(id) for id in opts.ids.split(',')]
|
opts.ids = [int(id) for id in opts.ids.split(',')]
|
||||||
|
|
||||||
|
# No support for connected device in CLI environment
|
||||||
|
# Parallel initialization in calibre.gui2.tools:generate_catalog()
|
||||||
|
opts.connected_device = {'storage':None,'serial':None,'name':None}
|
||||||
|
|
||||||
with plugin:
|
with plugin:
|
||||||
plugin.run(args[1], opts, get_db(dbpath, opts))
|
plugin.run(args[1], opts, get_db(dbpath, opts))
|
||||||
return 0
|
return 0
|
||||||
|
Loading…
x
Reference in New Issue
Block a user