Merge from trunk

This commit is contained in:
Charles Haley 2010-09-08 17:25:09 +01:00
commit a7d1777bf3
12 changed files with 828 additions and 441 deletions

View File

@ -6,7 +6,7 @@ p.title {
text-align:center;
font-style:italic;
font-size:xx-large;
border-bottom: solid black 4px;
border-bottom: solid black 2px;
}
p.author {
@ -17,6 +17,15 @@ p.author {
font-size:large;
}
p.author_index {
font-size:large;
font-weight:bold;
text-align:left;
margin-top:0px;
margin-bottom:-2px;
text-indent: 0em;
}
p.tags {
margin-top:0em;
margin-bottom:0em;
@ -47,19 +56,12 @@ p.letter_index {
margin-bottom:0px;
}
p.author_index {
font-size:large;
text-align:left;
margin-top:0px;
margin-bottom:0px;
text-indent: 0em;
}
p.series {
text-align: left;
margin-top:0px;
font-style:italic;
margin-top:2px;
margin-bottom:0px;
margin-left:2em;
text-align:left;
text-indent:-2em;
}
@ -87,11 +89,13 @@ p.date_read {
text-indent:-6em;
}
hr.series_divider {
width:50%;
margin-left:1em;
margin-top:0em;
margin-bottom:0em;
hr.description_divider {
width:90%;
margin-left:5%;
border-top: solid white 0px;
border-right: solid white 0px;
border-bottom: solid black 1px;
border-left: solid white 0px;
}
hr.annotations_divider {

View File

@ -294,7 +294,7 @@ class CatalogPlugin(Plugin): # {{{
# Return a list of requested fields, with opts.sort_by first
all_fields = set(
['author_sort','authors','comments','cover','formats',
'id','isbn','pubdate','publisher','rating',
'id','isbn','ondevice','pubdate','publisher','rating',
'series_index','series','size','tags','timestamp',
'title','uuid'])
@ -306,6 +306,9 @@ class CatalogPlugin(Plugin): # {{{
else:
fields = list(all_fields)
if not opts.connected_device['is_device_connected'] and 'ondevice' in fields:
fields.pop(int(fields.index('ondevice')))
fields.sort()
if opts.sort_by and opts.sort_by in fields:
fields.insert(0,fields.pop(int(fields.index(opts.sort_by))))

View File

@ -461,7 +461,7 @@ from calibre.devices.hanvon.driver import N516, EB511, ALEX, AZBOOKA, THEBOOK
from calibre.devices.edge.driver import EDGE
from calibre.devices.teclast.driver import TECLAST_K3, NEWSMY, IPAPYRUS
from calibre.devices.sne.driver import SNE
from calibre.devices.misc import PALMPRE, AVANT, SWEEX, PDNOVEL, KOGAN
from calibre.devices.misc import PALMPRE, AVANT, SWEEX, PDNOVEL, KOGAN, GEMEI
from calibre.devices.folder_device.driver import FOLDER_DEVICE_FOR_CONFIG
from calibre.devices.kobo.driver import KOBO
@ -570,6 +570,7 @@ plugins += [
KOGAN,
PDNOVEL,
SPECTRA,
GEMEI,
ITUNES,
]
plugins += [x for x in list(locals().values()) if isinstance(x, type) and \

View File

@ -2303,9 +2303,9 @@ class ITUNES(DriverBase):
# Delete existing from Device|Books, add to self.update_list
# for deletion from booklist[0] during add_books_to_metadata
for book in self.cached_books:
if self.cached_books[book]['uuid'] == metadata.uuid and \
self.cached_books[book]['title'] == metadata.title and \
self.cached_books[book]['author'] == metadata.authors[0]:
if self.cached_books[book]['uuid'] == metadata.uuid or \
(self.cached_books[book]['title'] == metadata.title and \
self.cached_books[book]['author'] == metadata.authors[0]):
self.update_list.append(self.cached_books[book])
self._remove_from_device(self.cached_books[book])
if DEBUG:
@ -2322,9 +2322,9 @@ class ITUNES(DriverBase):
# Delete existing from Library|Books, add to self.update_list
# for deletion from booklist[0] during add_books_to_metadata
for book in self.cached_books:
if self.cached_books[book]['uuid'] == metadata.uuid and \
self.cached_books[book]['title'] == metadata.title and \
self.cached_books[book]['author'] == metadata.authors[0]:
if self.cached_books[book]['uuid'] == metadata.uuid or \
(self.cached_books[book]['title'] == metadata.title and \
self.cached_books[book]['author'] == metadata.authors[0]):
self.update_list.append(self.cached_books[book])
self._remove_from_iTunes(self.cached_books[book])
if DEBUG:
@ -2488,7 +2488,7 @@ class ITUNES(DriverBase):
zf_opf.close()
# If 'News' in tags, tweak the title/author for friendlier display in iBooks
if _('News') in metadata.tags:
if _('News') or _('Catalog') in metadata.tags:
if metadata.title.find('[') > 0:
metadata.title = metadata.title[:metadata.title.find('[')-1]
date_as_author = '%s, %s %s, %s' % (strftime('%A'), strftime('%B'), strftime('%d').lstrip('0'), strftime('%Y'))

View File

@ -108,4 +108,23 @@ class PDNOVEL(USBMS):
with open('%s.jpg' % os.path.join(path, filename), 'wb') as coverfile:
coverfile.write(coverdata[2])
class GEMEI(USBMS):
name = 'Gemei Device Interface'
gui_name = 'GM2000'
description = _('Communicate with the GM2000')
author = 'Kovid Goyal'
supported_platforms = ['windows', 'osx', 'linux']
# Ordered list of supported formats
FORMATS = ['epub', 'chm', 'html', 'pdb', 'pdf', 'txt']
VENDOR_ID = [0x07c4]
PRODUCT_ID = [0xa4a5]
BCD = None
VENDOR_NAME = 'CHINA'
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = 'CHIP'
EBOOK_DIR_MAIN = 'eBooks'
SUPPORTS_SUB_DIRS = True

View File

@ -26,14 +26,18 @@ class GenerateCatalogAction(InterfaceAction):
rows = xrange(self.gui.library_view.model().rowCount(QModelIndex()))
ids = map(self.gui.library_view.model().id, rows)
dbspec = None
if not ids:
return error_dialog(self.gui, _('No books selected'),
_('No books selected to generate catalog for'),
show=True)
db = self.gui.library_view.model().db
dbspec = {}
for id in ids:
dbspec[id] = {'ondevice': db.ondevice(id, index_is_id=True)}
# Calling gui2.tools:generate_catalog()
ret = generate_catalog(self.gui, dbspec, ids, self.gui.device_manager.device)
ret = generate_catalog(self.gui, dbspec, ids, self.gui.device_manager)
if ret is None:
return

View File

@ -19,6 +19,7 @@ class PluginWidget(QWidget,Ui_Form):
OPTION_FIELDS = [('exclude_genre','\[.+\]'),
('exclude_tags','~,'+_('Catalog')),
('generate_titles', True),
('generate_series', True),
('generate_recently_added', True),
('note_tag','*'),
('numbers_as_text', False),
@ -40,7 +41,7 @@ class PluginWidget(QWidget,Ui_Form):
# Update dialog fields from stored options
for opt in self.OPTION_FIELDS:
opt_value = gprefs.get(self.name + '_' + opt[0], opt[1])
if opt[0] in ['numbers_as_text','generate_titles','generate_recently_added']:
if opt[0] in ['numbers_as_text','generate_titles','generate_series','generate_recently_added']:
getattr(self, opt[0]).setChecked(opt_value)
else:
getattr(self, opt[0]).setText(opt_value)
@ -52,13 +53,13 @@ class PluginWidget(QWidget,Ui_Form):
# others store as lists
opts_dict = {}
for opt in self.OPTION_FIELDS:
if opt[0] in ['numbers_as_text','generate_titles','generate_recently_added']:
if opt[0] in ['numbers_as_text','generate_titles','generate_series','generate_recently_added']:
opt_value = getattr(self,opt[0]).isChecked()
else:
opt_value = unicode(getattr(self, opt[0]).text())
gprefs.set(self.name + '_' + opt[0], opt_value)
if opt[0] in ['exclude_genre','numbers_as_text','generate_titles','generate_recently_added']:
if opt[0] in ['exclude_genre','numbers_as_text','generate_titles','generate_series','generate_recently_added']:
opts_dict[opt[0]] = opt_value
else:
opts_dict[opt[0]] = opt_value.split(',')

View File

@ -108,20 +108,27 @@
</property>
</widget>
</item>
<item row="9" column="0">
<item row="10" column="0">
<widget class="QCheckBox" name="generate_recently_added">
<property name="text">
<string>Include 'Recently Added' Section</string>
</property>
</widget>
</item>
<item row="10" column="0">
<item row="11" column="0">
<widget class="QCheckBox" name="numbers_as_text">
<property name="text">
<string>Sort numbers as text</string>
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QCheckBox" name="generate_series">
<property name="text">
<string>Include 'Series' Section</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>

View File

@ -29,6 +29,7 @@ def gui_catalog(fmt, title, dbspec, ids, out_file_name, sync, fmt_options, conne
log = Log()
from calibre.library import db
db = db()
db.catalog_plugin_on_device_temp_mapping = dbspec
# Create a minimal OptionParser that we can append to
parser = OptionParser()

View File

@ -238,7 +238,7 @@ def fetch_scheduled_recipe(arg):
return 'gui_convert', args, _('Fetch news from ')+arg['title'], fmt.upper(), [pt]
def generate_catalog(parent, dbspec, ids, device):
def generate_catalog(parent, dbspec, ids, device_manager):
from calibre.gui2.dialogs.catalog import Catalog
# Build the Catalog dialog in gui2.dialogs.catalog
@ -252,9 +252,18 @@ def generate_catalog(parent, dbspec, ids, device):
# Profile the connected device
# Parallel initialization in calibre.library.cli:command_catalog()
connected_device = { 'storage':None,'serial':None,'save_template':None,'name':None}
connected_device = {
'is_device_connected': device_manager.is_device_connected,
'kind': device_manager.connected_device_kind,
'name': None,
'save_template': None,
'serial': None,
'storage': None
}
if device:
if device_manager.is_device_connected:
device = device_manager.device
connected_device['name'] = device.gui_name
try:
storage = []
if device._main_prefix:
@ -263,11 +272,10 @@ def generate_catalog(parent, dbspec, ids, device):
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 if \
hasattr(device.detected_device,'serial') else None,
'save_template': device.save_template(),
'name': device.gui_name}
connected_device['storage'] = storage
connected_device['serial'] = device.detected_device.serial if \
hasattr(device.detected_device,'serial') else None
connected_device['save_template'] = device.save_template()
except:
pass

View File

@ -20,7 +20,7 @@ from calibre.utils.date import isoformat, now as nowf
from calibre.utils.logging import default_log as log
FIELDS = ['all', 'author_sort', 'authors', 'comments',
'cover', 'formats', 'id', 'isbn', 'pubdate', 'publisher', 'rating',
'cover', 'formats', 'id', 'isbn', 'ondevice', 'pubdate', 'publisher', 'rating',
'series_index', 'series', 'size', 'tags', 'timestamp', 'title',
'uuid']
@ -67,6 +67,8 @@ class CSV_XML(CatalogPlugin):
if opts.verbose:
opts_dict = vars(opts)
log("%s(): Generating %s" % (self.name,self.fmt))
if opts.connected_device['is_device_connected']:
log(" connected_device: %s" % opts.connected_device['name'])
if opts_dict['search_text']:
log(" --search='%s'" % opts_dict['search_text'])
@ -81,7 +83,6 @@ class CSV_XML(CatalogPlugin):
else:
log(" Fields: %s" % opts_dict['fields'])
# If a list of ids are provided, don't use search_text
if opts.ids:
opts.search_text = None
@ -95,6 +96,11 @@ class CSV_XML(CatalogPlugin):
# Get the requested output fields as a list
fields = self.get_output_fields(opts)
# If connected device, add 'On Device' values to data
if opts.connected_device['is_device_connected'] and 'ondevice' in fields:
for entry in data:
entry['ondevice'] = db.catalog_plugin_on_device_temp_mapping[entry['id']]['ondevice']
if self.fmt == 'csv':
outfile = codecs.open(path_to_output, 'w', 'utf8')
@ -140,10 +146,10 @@ class CSV_XML(CatalogPlugin):
root.append(record)
for field in ('id', 'uuid', 'title', 'publisher', 'rating', 'size',
'isbn'):
'isbn','ondevice'):
if field in fields:
val = r[field]
if val is None:
if not val:
continue
if not isinstance(val, (str, unicode)):
val = unicode(val)
@ -561,6 +567,13 @@ class EPUB_MOBI(CatalogPlugin):
help=_("Include 'Titles' section in catalog.\n"
"Default: '%default'\n"
"Applies to: ePub, MOBI output formats")),
Option('--generate-series',
default=False,
dest='generate_series',
action = 'store_true',
help=_("Include 'Series' section in catalog.\n"
"Default: '%default'\n"
"Applies to: ePub, MOBI output formats")),
Option('--generate-recently-added',
default=False,
dest='generate_recently_added',
@ -886,8 +899,12 @@ class EPUB_MOBI(CatalogPlugin):
self.__totalSteps += 2
if self.generateRecentlyRead:
self.__totalSteps += 2
if self.opts.generate_series:
self.__totalSteps += 2
# Accessors
if True:
'''
@dynamic_property
def xxxx(self):
@ -897,7 +914,6 @@ class EPUB_MOBI(CatalogPlugin):
self.__ = val
return property(fget=fget, fset=fset)
'''
@dynamic_property
def authorClip(self):
def fget(self):
@ -1196,6 +1212,8 @@ class EPUB_MOBI(CatalogPlugin):
self.generateHTMLByAuthor()
if self.opts.generate_titles:
self.generateHTMLByTitle()
if self.opts.generate_series:
self.generateHTMLBySeries()
if self.opts.generate_recently_added:
self.generateHTMLByDateAdded()
if self.generateRecentlyRead:
@ -1206,15 +1224,17 @@ class EPUB_MOBI(CatalogPlugin):
self.generateOPF()
self.generateNCXHeader()
self.generateNCXDescriptions("Descriptions")
self.generateNCXByAuthor("Authors")
if self.opts.generate_titles:
self.generateNCXByTitle("Titles")
if self.opts.generate_series:
self.generateNCXBySeries("Series")
if self.opts.generate_recently_added:
self.generateNCXByDateAdded("Recently Added")
if self.generateRecentlyRead:
self.generateNCXByDateRead("Recently Read")
self.generateNCXByGenre("Genres")
self.generateNCXDescriptions("Descriptions")
self.writeNCX()
return True
@ -1569,9 +1589,22 @@ class EPUB_MOBI(CatalogPlugin):
emTag = Tag(soup, "em")
if title['series']:
# title<br />series series_index
if self.opts.generate_series:
brTag = Tag(soup,'br')
title_tokens = title['title'].split(': ')
emTag.insert(0, escape(NavigableString(title_tokens[1])))
title_tokens = list(title['title'].partition(':'))
emTag.insert(0, escape(NavigableString(title_tokens[2].strip())))
emTag.insert(1, brTag)
smallTag = Tag(soup,'small')
aTag = Tag(soup,'a')
aTag['href'] = "%s.html#%s_series" % ('BySeries',
re.sub('\W','',title['series']).lower())
aTag.insert(0, title_tokens[0])
smallTag.insert(0, aTag)
emTag.insert(2, smallTag)
else:
brTag = Tag(soup,'br')
title_tokens = list(title['title'].partition(':'))
emTag.insert(0, escape(NavigableString(title_tokens[2].strip())))
emTag.insert(1, brTag)
smallTag = Tag(soup,'small')
smallTag.insert(0, escape(NavigableString(title_tokens[0])))
@ -1724,17 +1757,17 @@ class EPUB_MOBI(CatalogPlugin):
body.insert(btc, aTag)
btc += 1
'''
if not self.__generateForKindle:
# We don't need this because the Kindle shows section titles
#<h2><a name="byalphatitle" id="byalphatitle"></a>By Title</h2>
h2Tag = Tag(soup, "h2")
pTag = Tag(soup, "p")
pTag['class'] = 'title'
aTag = Tag(soup, "a")
aTag['name'] = "bytitle"
h2Tag.insert(0,aTag)
h2Tag.insert(1,NavigableString('By Title (%d)' % len(self.booksByTitle)))
body.insert(btc,h2Tag)
pTag.insert(0,aTag)
pTag.insert(1,NavigableString('Titles'))
body.insert(btc,pTag)
btc += 1
'''
# <p class="letter_index">
# <p class="book_title">
@ -1742,13 +1775,13 @@ class EPUB_MOBI(CatalogPlugin):
dtc = 0
current_letter = ""
# 2/14/10 7:11 AM Experimental: re-sort title list without leading series/series_index
# 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])
tokens = book['title'].partition(':')
book['title'] = '%s (%s)' % (tokens[2].strip(), tokens[0])
book['title_sort'] = self.generateSortTitle(book['title'])
nspt = sorted(nspt,
key=lambda x:(x['title_sort'].upper(), x['title_sort'].upper()))
@ -1835,7 +1868,7 @@ class EPUB_MOBI(CatalogPlugin):
# Write books by author A-Z
self.updateProgressFullStep("'Authors'")
friendly_name = "By Author"
friendly_name = "Authors"
soup = self.generateHTMLEmptyHeader(friendly_name)
body = soup.find('body')
@ -1906,15 +1939,14 @@ class EPUB_MOBI(CatalogPlugin):
current_series = None
pAuthorTag = Tag(soup, "p")
pAuthorTag['class'] = "author_index"
emTag = Tag(soup, "em")
aTag = Tag(soup, "a")
aTag['name'] = "%s" % self.generateAuthorAnchor(current_author)
aTag.insert(0,NavigableString(current_author))
emTag.insert(0,aTag)
pAuthorTag.insert(0,emTag)
pAuthorTag.insert(0,aTag)
divTag.insert(dtc,pAuthorTag)
dtc += 1
'''
# Insert an <hr /> between non-series and series
if not current_series and non_series_books and book['series']:
# Insert an <hr />
@ -1922,6 +1954,7 @@ class EPUB_MOBI(CatalogPlugin):
hrTag['class'] = "series_divider"
divTag.insert(dtc,hrTag)
dtc += 1
'''
# Check for series
if book['series'] and book['series'] != current_series:
@ -1929,7 +1962,18 @@ class EPUB_MOBI(CatalogPlugin):
current_series = book['series']
pSeriesTag = Tag(soup,'p')
pSeriesTag['class'] = "series"
pSeriesTag.insert(0,NavigableString(self.NOT_READ_SYMBOL + book['series']))
if self.opts.generate_series:
aTag = Tag(soup,'a')
aTag['href'] = "%s.html#%s_series" % ('BySeries',
re.sub('\W','',book['series']).lower())
aTag.insert(0, book['series'])
#pSeriesTag.insert(0, NavigableString(self.NOT_READ_SYMBOL))
pSeriesTag.insert(0, aTag)
else:
#pSeriesTag.insert(0,NavigableString(self.NOT_READ_SYMBOL + '%s' % book['series']))
pSeriesTag.insert(0,NavigableString('%s' % book['series']))
divTag.insert(dtc,pSeriesTag)
dtc += 1
if current_series and not book['series']:
@ -1957,30 +2001,34 @@ class EPUB_MOBI(CatalogPlugin):
aTag = Tag(soup, "a")
aTag['href'] = "book_%d.html" % (int(float(book['id'])))
# Use series, series index if avail else just title
# Use series, series index if avail else just title, + year of publication
if current_series:
aTag.insert(0,escape(book['title'][len(book['series'])+1:]))
aTag.insert(0,'%s (%s)' % (escape(book['title'][len(book['series'])+1:]),
book['date'].split()[1]))
else:
aTag.insert(0,escape(book['title']))
aTag.insert(0,'%s (%s)' % (escape(book['title']),
book['date'].split()[1]))
non_series_books += 1
pBookTag.insert(ptc, aTag)
ptc += 1
divTag.insert(dtc, pBookTag)
dtc += 1
'''
if not self.__generateForKindle:
# Insert the <h2> tag with book_count at the head
#<h2><a name="byalphaauthor" id="byalphaauthor"></a>By Author</h2>
h2Tag = Tag(soup, "h2")
pTag = Tag(soup, "p")
pTag['class'] = 'title'
aTag = Tag(soup, "a")
anchor_name = friendly_name.lower()
aTag['name'] = anchor_name.replace(" ","")
h2Tag.insert(0,aTag)
h2Tag.insert(1,NavigableString('%s (%d)' % (friendly_name, book_count)))
body.insert(btc,h2Tag)
pTag.insert(0,aTag)
#h2Tag.insert(1,NavigableString('%s (%d)' % (friendly_name, book_count)))
pTag.insert(1,NavigableString('%s' % (friendly_name)))
body.insert(btc,pTag)
btc += 1
'''
# Add the divTag to the body
body.insert(btc, divTag)
@ -2023,15 +2071,14 @@ class EPUB_MOBI(CatalogPlugin):
current_series = None
pAuthorTag = Tag(soup, "p")
pAuthorTag['class'] = "author_index"
emTag = Tag(soup, "em")
aTag = Tag(soup, "a")
aTag['name'] = "%s" % self.generateAuthorAnchor(current_author)
aTag.insert(0,NavigableString(current_author))
emTag.insert(0,aTag)
pAuthorTag.insert(0,emTag)
pAuthorTag.insert(0,aTag)
divTag.insert(dtc,pAuthorTag)
dtc += 1
'''
# Insert an <hr /> between non-series and series
if not current_series and non_series_books and new_entry['series']:
# Insert an <hr />
@ -2039,6 +2086,7 @@ class EPUB_MOBI(CatalogPlugin):
hrTag['class'] = "series_divider"
divTag.insert(dtc,hrTag)
dtc += 1
'''
# Check for series
if new_entry['series'] and new_entry['series'] != current_series:
@ -2046,7 +2094,14 @@ class EPUB_MOBI(CatalogPlugin):
current_series = new_entry['series']
pSeriesTag = Tag(soup,'p')
pSeriesTag['class'] = "series"
pSeriesTag.insert(0,NavigableString(self.NOT_READ_SYMBOL + new_entry['series']))
if self.opts.generate_series:
aTag = Tag(soup,'a')
aTag['href'] = "%s.html#%s_series" % ('BySeries',
re.sub('\W','',new_entry['series']).lower())
aTag.insert(0, new_entry['series'])
pSeriesTag.insert(0, aTag)
else:
pSeriesTag.insert(0,NavigableString('%s' % new_entry['series']))
divTag.insert(dtc,pSeriesTag)
dtc += 1
if current_series and not new_entry['series']:
@ -2160,18 +2215,18 @@ class EPUB_MOBI(CatalogPlugin):
aTag['name'] = anchor_name.replace(" ","")
body.insert(btc, aTag)
btc += 1
'''
# We don't need this because the kindle inserts section titles
if not self.__generateForKindle:
#<h2><a name="byalphaauthor" id="byalphaauthor"></a>By Author</h2>
h2Tag = Tag(soup, "h2")
pTag = Tag(soup, "p")
pTag['class'] = 'title'
aTag = Tag(soup, "a")
anchor_name = friendly_name.lower()
aTag['name'] = anchor_name.replace(" ","")
h2Tag.insert(0,aTag)
h2Tag.insert(1,NavigableString('%s' % friendly_name))
body.insert(btc,h2Tag)
pTag.insert(0,aTag)
pTag.insert(1,NavigableString('%s' % friendly_name))
body.insert(btc,pTag)
btc += 1
'''
# <p class="letter_index">
# <p class="author_index">
@ -2186,14 +2241,13 @@ class EPUB_MOBI(CatalogPlugin):
nspt = deepcopy(self.booksByTitle)
for book in nspt:
if book['series']:
tokens = book['title'].split(': ')
book['title'] = '%s (%s)' % (tokens[1], tokens[0])
tokens = book['title'].partition(':')
book['title'] = '%s (%s)' % (tokens[2].strip(), tokens[0])
book['title_sort'] = self.generateSortTitle(book['title'])
self.booksByDateRange = sorted(nspt, key=lambda x:(x['timestamp'], x['timestamp']),reverse=True)
date_range_list = []
today_time = nowf().replace(hour=23, minute=59, second=59)
books_added_in_date_range = False
for (i, date) in enumerate(self.DATE_RANGE):
date_range_limit = self.DATE_RANGE[i]
if i:
@ -2206,18 +2260,20 @@ class EPUB_MOBI(CatalogPlugin):
delta = today_time-book_time
if delta.days <= date_range_limit:
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')
hrTag['class'] = "description_divider"
divTag.insert(dtc,hrTag)
dtc += 1
'''
# >>>> Books by month <<<<
# Sort titles case-insensitive for by month using series prefix
@ -2437,6 +2493,174 @@ class EPUB_MOBI(CatalogPlugin):
outfile.close()
self.htmlFileList.append("content/ByDateRead.html")
def generateHTMLBySeries(self):
'''
Generate a list of series
'''
self.updateProgressFullStep("Fetching series")
self.opts.sort_by = 'series'
# Merge opts.exclude_tags with opts.search_text
# Updated to use exact match syntax
empty_exclude_tags = False if len(self.opts.exclude_tags) else True
search_phrase = 'series:true '
if not empty_exclude_tags:
exclude_tags = self.opts.exclude_tags.split(',')
search_terms = []
for tag in exclude_tags:
search_terms.append("tag:=%s" % tag)
search_phrase += "not (%s)" % " or ".join(search_terms)
# If a list of ids are provided, don't use search_text
if self.opts.ids:
self.opts.search_text = search_phrase
else:
if self.opts.search_text:
self.opts.search_text += " " + search_phrase
else:
self.opts.search_text = search_phrase
# Fetch the database as a dictionary
self.booksBySeries = self.plugin.search_sort_db(self.db, self.opts)
friendly_name = "Series"
soup = self.generateHTMLEmptyHeader(friendly_name)
body = soup.find('body')
btc = 0
# Insert section tag
aTag = Tag(soup,'a')
aTag['name'] = 'section_start'
body.insert(btc, aTag)
btc += 1
# Insert the anchor
aTag = Tag(soup, "a")
anchor_name = friendly_name.lower()
aTag['name'] = anchor_name.replace(" ","")
body.insert(btc, aTag)
btc += 1
# <p class="letter_index">
# <p class="author_index">
divTag = Tag(soup, "div")
dtc = 0
current_letter = ""
current_series = None
# Loop through booksBySeries
series_count = 0
for book in self.booksBySeries:
# Check for initial letter change
sort_title = self.generateSortTitle(book['series'])
if self.letter_or_symbol(sort_title[0].upper()) != current_letter :
'''
# Start a new letter - anchor only, hidden
current_letter = book['author_sort'][0].upper()
aTag = Tag(soup, "a")
aTag['name'] = "%sseries" % current_letter
divTag.insert(dtc, aTag)
dtc += 1
'''
# Start a new letter with Index letter
current_letter = self.letter_or_symbol(sort_title[0].upper())
pIndexTag = Tag(soup, "p")
pIndexTag['class'] = "letter_index"
aTag = Tag(soup, "a")
aTag['name'] = "%s_series" % self.letter_or_symbol(current_letter)
pIndexTag.insert(0,aTag)
pIndexTag.insert(1,NavigableString(self.letter_or_symbol(sort_title[0].upper())))
divTag.insert(dtc,pIndexTag)
dtc += 1
# Check for series change
if book['series'] != current_series:
# Start a new series
series_count += 1
current_series = book['series']
pSeriesTag = Tag(soup,'p')
pSeriesTag['class'] = "series"
aTag = Tag(soup, 'a')
aTag['name'] = "%s_series" % re.sub('\W','',book['series']).lower()
pSeriesTag.insert(0,aTag)
pSeriesTag.insert(1,NavigableString(self.NOT_READ_SYMBOL + '%s' % book['series']))
divTag.insert(dtc,pSeriesTag)
dtc += 1
# Add books
pBookTag = Tag(soup, "p")
ptc = 0
# book with read/reading/unread symbol
if 'read' in book and book['read']:
# check mark
pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL))
pBookTag['class'] = "read_book"
ptc += 1
elif book['id'] in self.bookmarked_books:
pBookTag.insert(ptc,NavigableString(self.READING_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(book['id'])))
# Use series, series index if avail else just title
#aTag.insert(0,'%d. %s &middot; %s' % (book['series_index'],escape(book['title']), ' & '.join(book['authors'])))
# Link to book
aTag.insert(0,'%d. %s (%s)' % (book['series_index'],
escape(book['title']),
strftime(u'%Y', book['pubdate'].timetuple())))
pBookTag.insert(ptc, aTag)
ptc += 1
# &middot;
pBookTag.insert(ptc, NavigableString(' &middot; '))
ptc += 1
# Link to author
aTag = Tag(soup, "a")
aTag['href'] = "%s.html#%s" % ("ByAlphaAuthor",
self.generateAuthorAnchor(escape(' & '.join(book['authors']))))
aTag.insert(0, NavigableString(' &amp; '.join(book['authors'])))
pBookTag.insert(ptc, aTag)
ptc += 1
divTag.insert(dtc, pBookTag)
dtc += 1
if not self.__generateForKindle:
# Insert the <h2> tag with book_count at the head
#<h2><a name="byseries" id="byseries"></a>By Series</h2>
pTag = Tag(soup, "p")
pTag['class'] = 'title'
aTag = Tag(soup, "a")
anchor_name = friendly_name.lower()
aTag['name'] = anchor_name.replace(" ","")
pTag.insert(0,aTag)
#h2Tag.insert(1,NavigableString('%s (%d)' % (friendly_name, series_count)))
pTag.insert(1,NavigableString('%s' % friendly_name))
body.insert(btc,pTag)
btc += 1
# Add the divTag to the body
body.insert(btc, divTag)
# Write the generated file to contentdir
outfile_spec = "%s/BySeries.html" % (self.contentDir)
outfile = open(outfile_spec, 'w')
outfile.write(soup.prettify())
outfile.close()
self.htmlFileList.append("content/BySeries.html")
def generateHTMLByTags(self):
# Generate individual HTML files for each tag, e.g. Fiction, Nonfiction ...
# Note that special tags - ~+*[] - have already been filtered from books[]
@ -2683,22 +2907,7 @@ class EPUB_MOBI(CatalogPlugin):
# HTML files - add books to manifest and spine
sort_descriptions_by = self.booksByAuthor if self.opts.sort_descriptions_by_author \
else self.booksByTitle
for book in sort_descriptions_by:
# manifest
itemTag = Tag(soup, "item")
itemTag['href'] = "content/book_%d.html" % int(book['id'])
itemTag['id'] = "book%d" % int(book['id'])
itemTag['media-type'] = "application/xhtml+xml"
manifest.insert(mtc, itemTag)
mtc += 1
# spine
itemrefTag = Tag(soup, "itemref")
itemrefTag['idref'] = "book%d" % int(book['id'])
spine.insert(stc, itemrefTag)
stc += 1
# Add other html_files to manifest and spine
# Add html_files to manifest and spine
for file in self.htmlFileList:
itemTag = Tag(soup, "item")
@ -2734,6 +2943,21 @@ class EPUB_MOBI(CatalogPlugin):
spine.insert(stc, itemrefTag)
stc += 1
for book in sort_descriptions_by:
# manifest
itemTag = Tag(soup, "item")
itemTag['href'] = "content/book_%d.html" % int(book['id'])
itemTag['id'] = "book%d" % int(book['id'])
itemTag['media-type'] = "application/xhtml+xml"
manifest.insert(mtc, itemTag)
mtc += 1
# spine
itemrefTag = Tag(soup, "itemref")
itemrefTag['idref'] = "book%d" % int(book['id'])
spine.insert(stc, itemrefTag)
stc += 1
# Guide
referenceTag = Tag(soup, "reference")
referenceTag['type'] = 'masthead'
@ -2769,7 +2993,8 @@ class EPUB_MOBI(CatalogPlugin):
navLabelTag.insert(0, textTag)
navPointTag.insert(0, navLabelTag)
contentTag = Tag(soup, 'content')
contentTag['src'] = "content/book_%d.html" % int(self.booksByTitle[0]['id'])
#contentTag['src'] = "content/book_%d.html" % int(self.booksByTitle[0]['id'])
contentTag['src'] = "content/ByAlphaAuthor.html"
navPointTag.insert(1, contentTag)
cmiTag = Tag(soup, '%s' % 'calibre:meta-img')
cmiTag['name'] = "mastheadImage"
@ -2821,15 +3046,15 @@ class EPUB_MOBI(CatalogPlugin):
navLabelTag = Tag(ncx_soup, "navLabel")
textTag = Tag(ncx_soup, "text")
if book['series']:
tokens = book['title'].split(': ')
tokens = list(book['title'].partition(':'))
if self.generateForKindle:
# Don't include Author for Kindle
textTag.insert(0, NavigableString(self.formatNCXText('%s (%s)' % \
(tokens[1], tokens[0]), dest='title')))
(tokens[2].strip(), tokens[0]), dest='title')))
else:
# Include Author for non-Kindle
textTag.insert(0, NavigableString(self.formatNCXText('%s &middot; %s (%s)' % \
(tokens[1], book['author'], tokens[0]), dest='title')))
(tokens[2].strip(), book['author'], tokens[0]), dest='title')))
else:
if self.generateForKindle:
# Don't include Author for Kindle
@ -2882,6 +3107,98 @@ class EPUB_MOBI(CatalogPlugin):
self.ncxSoup = ncx_soup
def generateNCXBySeries(self, tocTitle):
self.updateProgressFullStep("NCX 'Series'")
def add_to_series_by_letter(current_series_list):
current_series_list = " &bull; ".join(current_series_list)
current_series_list = self.formatNCXText(current_series_list, dest="description")
series_by_letter.append(current_series_list)
soup = self.ncxSoup
output = "BySeries"
body = soup.find("navPoint")
btc = len(body.contents)
# --- Construct the 'Books By Series' section ---
navPointTag = Tag(soup, 'navPoint')
navPointTag['class'] = "section"
navPointTag['id'] = "byseries-ID"
navPointTag['playOrder'] = self.playOrder
self.playOrder += 1
navLabelTag = Tag(soup, 'navLabel')
textTag = Tag(soup, 'text')
textTag.insert(0, NavigableString(tocTitle))
navLabelTag.insert(0, textTag)
nptc = 0
navPointTag.insert(nptc, navLabelTag)
nptc += 1
contentTag = Tag(soup,"content")
contentTag['src'] = "content/%s.html#section_start" % (output)
navPointTag.insert(nptc, contentTag)
nptc += 1
series_by_letter = []
# Loop over the series titles, find start of each letter, add description_preview_count books
# Special switch for using different title list
title_list = self.booksBySeries
current_letter = self.letter_or_symbol(title_list[0]['series'][0])
title_letters = [current_letter]
current_series_list = []
current_series = ""
for book in title_list:
sort_title = self.generateSortTitle(book['series'])
if self.letter_or_symbol(sort_title[0]) != current_letter:
# Save the old list
add_to_series_by_letter(current_series_list)
# Start the new list
current_letter = self.letter_or_symbol(sort_title[0])
title_letters.append(current_letter)
current_series = book['series']
current_series_list = [book['series']]
else:
if len(current_series_list) < self.descriptionClip and \
book['series'] != current_series :
current_series = book['series']
current_series_list.append(book['series'])
# Add the last book list
add_to_series_by_letter(current_series_list)
# Add *article* entries for each populated series title letter
for (i,books) in enumerate(series_by_letter):
navPointByLetterTag = Tag(soup, 'navPoint')
navPointByLetterTag['class'] = "article"
navPointByLetterTag['id'] = "%sSeries-ID" % (title_letters[i].upper())
navPointTag['playOrder'] = self.playOrder
self.playOrder += 1
navLabelTag = Tag(soup, 'navLabel')
textTag = Tag(soup, 'text')
textTag.insert(0, NavigableString(u"Series beginning with %s" % \
(title_letters[i] if len(title_letters[i])>1 else "'" + title_letters[i] + "'")))
navLabelTag.insert(0, textTag)
navPointByLetterTag.insert(0,navLabelTag)
contentTag = Tag(soup, 'content')
contentTag['src'] = "content/%s.html#%s_series" % (output, title_letters[i])
navPointByLetterTag.insert(1,contentTag)
if self.generateForKindle:
cmTag = Tag(soup, '%s' % 'calibre:meta')
cmTag['name'] = "description"
cmTag.insert(0, NavigableString(self.formatNCXText(books, dest='description')))
navPointByLetterTag.insert(2, cmTag)
navPointTag.insert(nptc, navPointByLetterTag)
nptc += 1
# Add this section to the body
body.insert(btc, navPointTag)
btc += 1
self.ncxSoup = soup
def generateNCXByTitle(self, tocTitle):
self.updateProgressFullStep("NCX 'Titles'")
@ -3713,7 +4030,7 @@ class EPUB_MOBI(CatalogPlugin):
btc += 1
titleTag = body.find(attrs={'class':'title'})
titleTag.insert(0,NavigableString('<b><i>%s</i></b>' % escape(self.getFriendlyGenreTag(genre))))
titleTag.insert(0,NavigableString('%s' % escape(self.getFriendlyGenreTag(genre))))
# Insert the books by author list
divTag = body.find(attrs={'class':'authors'})
@ -3729,15 +4046,14 @@ class EPUB_MOBI(CatalogPlugin):
current_series = None
pAuthorTag = Tag(soup, "p")
pAuthorTag['class'] = "author_index"
emTag = Tag(soup, "em")
aTag = Tag(soup, "a")
aTag['href'] = "%s.html#%s" % ("ByAlphaAuthor", self.generateAuthorAnchor(book['author']))
aTag.insert(0, book['author'])
emTag.insert(0,aTag)
pAuthorTag.insert(0,emTag)
pAuthorTag.insert(0,aTag)
divTag.insert(dtc,pAuthorTag)
dtc += 1
'''
# Insert an <hr /> between non-series and series
if not current_series and non_series_books and book['series']:
# Insert an <hr />
@ -3745,6 +4061,7 @@ class EPUB_MOBI(CatalogPlugin):
hrTag['class'] = "series_divider"
divTag.insert(dtc,hrTag)
dtc += 1
'''
# Check for series
if book['series'] and book['series'] != current_series:
@ -3752,7 +4069,14 @@ class EPUB_MOBI(CatalogPlugin):
current_series = book['series']
pSeriesTag = Tag(soup,'p')
pSeriesTag['class'] = "series"
pSeriesTag.insert(0,NavigableString(self.NOT_READ_SYMBOL + book['series']))
if self.opts.generate_series:
aTag = Tag(soup,'a')
aTag['href'] = "%s.html#%s_series" % ('BySeries',
re.sub('\W','',book['series']).lower())
aTag.insert(0, book['series'])
pSeriesTag.insert(0, aTag)
else:
pSeriesTag.insert(0,NavigableString('%s' % book['series']))
divTag.insert(dtc,pSeriesTag)
dtc += 1
@ -3809,7 +4133,7 @@ class EPUB_MOBI(CatalogPlugin):
def generateHTMLDescriptionHeader(self, title):
title_border = '' if self.opts.fmt == 'epub' else \
'<div class="hr"><blockquote><hr/></blockquote></div>'
'<hr class="description_divider"/>'
header = '''
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:calibre="http://calibre.kovidgoyal.net/2009/metadata">
@ -3855,7 +4179,7 @@ class EPUB_MOBI(CatalogPlugin):
<td>&nbsp;</td>
</tr>
</table>
<blockquote><hr/></blockquote>
<hr class="description_divider" />
<div class="description"></div>
</body>
</html>
@ -3897,7 +4221,7 @@ class EPUB_MOBI(CatalogPlugin):
</head>
<body>
<p class="title"></p>
<div class="hr"><blockquote><hr/></blockquote></div>
<!--div class="hr"><blockquote><hr/></blockquote></div-->
<div class="authors"></div>
</body>
</html>
@ -4049,6 +4373,12 @@ class EPUB_MOBI(CatalogPlugin):
except:
self.opts.log.error("generateThumbnail(): Error with %s" % title['title'])
def getFriendlyGenreTag(self, genre):
# Find the first instance of friendly_tag matching genre
for friendly_tag in self.genre_tags_dict:
if self.genre_tags_dict[friendly_tag] == genre:
return friendly_tag
def getMarkerTags(self):
''' Return a list of special marker tags to be excluded from genre list '''
markerTags = []
@ -4063,12 +4393,6 @@ class EPUB_MOBI(CatalogPlugin):
else:
return char
def getFriendlyGenreTag(self, genre):
# Find the first instance of friendly_tag matching genre
for friendly_tag in self.genre_tags_dict:
if self.genre_tags_dict[friendly_tag] == genre:
return friendly_tag
def markdownComments(self, comments):
'''
Convert random comment text to normalized, xml-legal block of <p>s
@ -4224,13 +4548,15 @@ class EPUB_MOBI(CatalogPlugin):
opts.fmt = self.fmt = path_to_output.rpartition('.')[2]
# Add local options
opts.creator = "calibre"
opts.creator = '%s, %s %s, %s' % (strftime('%A'), strftime('%B'), strftime('%d').lstrip('0'), strftime('%Y'))
opts.creator_sort_as = '%s %s' % ('calibre', strftime('%Y-%m-%d'))
opts.connected_kindle = False
# Finalize output_profile
op = opts.output_profile
if op is None:
op = 'default'
if opts.connected_device['name'] and 'kindle' in opts.connected_device['name'].lower():
opts.connected_kindle = True
if opts.connected_device['serial'] and opts.connected_device['serial'][:4] in ['B004','B005']:
@ -4256,7 +4582,8 @@ class EPUB_MOBI(CatalogPlugin):
opts.exclude_genre = '\[^.\]'
build_log.append(" converting empty exclude_genre to '\[^.\]'")
if opts.connected_device['name']:
if opts.connected_device['is_device_connected'] and \
opts.connected_device['kind'] == 'device':
if opts.connected_device['serial']:
build_log.append(u" connected_device: '%s' #%s%s " % \
(opts.connected_device['name'],
@ -4267,9 +4594,14 @@ class EPUB_MOBI(CatalogPlugin):
build_log.append(u" mount point: %s" % storage)
else:
build_log.append(u" connected_device: '%s'" % opts.connected_device['name'])
try:
for storage in opts.connected_device['storage']:
if storage:
build_log.append(u" mount point: %s" % storage)
except:
build_log.append(u" (no mount points)")
else:
build_log.append(u" connected_device: '%s'" % opts.connected_device['name'])
opts_dict = vars(opts)
if opts_dict['ids']:

View File

@ -677,7 +677,14 @@ def command_catalog(args, dbpath):
# No support for connected device in CLI environment
# Parallel initialization in calibre.gui2.tools:generate_catalog()
opts.connected_device = { 'storage':None,'serial':None,'save_template':None,'name':None}
opts.connected_device = {
'is_device_connected': False,
'kind': None,
'name': None,
'save_template': None,
'serial': None,
'storage': None,
}
with plugin:
plugin.run(args[1], opts, get_db(dbpath, opts))