mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
0.8.67+, catalog wip
This commit is contained in:
commit
e5935b6f90
6
resources/catalog/debug/META-INF/container.xml
Normal file
6
resources/catalog/debug/META-INF/container.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">
|
||||||
|
<rootfiles>
|
||||||
|
<rootfile full-path="content.opf" media-type="application/oebps-package+xml"/>
|
||||||
|
</rootfiles>
|
||||||
|
</container>
|
1
resources/catalog/debug/mimetype
Normal file
1
resources/catalog/debug/mimetype
Normal file
@ -0,0 +1 @@
|
|||||||
|
application/epub+zip
|
@ -43,14 +43,14 @@ h3 {
|
|||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<h1><a name="toc" id="toc"></a>AZW3 • EPUB • MOBI Catalogs </h1>
|
<h1><a name="toc" id="toc"></a>Creating AZW3 • EPUB • MOBI Catalogs </h1>
|
||||||
<h2><a href="#selecting_books_to_catalog">Selecting books to catalog</a></h2>
|
<h2><a href="#selecting_books_to_catalog">Selecting books to catalog</a></h2>
|
||||||
<h2><a href="#included_sections">Included sections: Selecting which sections to include in the catalog</a></h2>
|
<h2><a href="#included_sections">Included sections: Selecting which sections to include in the catalog</a></h2>
|
||||||
<h2><a href="#prefixes">Prefixes: Adding a prefix indicating book status</a></h2>
|
<h2><a href="#prefixes">Prefixes: Adding a prefix indicating book status</a></h2>
|
||||||
<h2><a href="#excluded_books">Excluded books: Ignoring certain books when generating the catalog</a></h2>
|
<h2><a href="#excluded_books">Excluded books: Ignoring certain books when generating the catalog</a></h2>
|
||||||
<h2><a href="#excluded_genres">Excluded genres: Ignoring certain tags as genres when generating the catalog</a></h2>
|
<h2><a href="#excluded_genres">Excluded genres: Ignoring certain tags as genres when generating the catalog</a></h2>
|
||||||
<h2><a href="#other_options">Other options: Specifying thumb size, adding extra information to Descriptions</a></h2>
|
<h2><a href="#other_options">Other options: Specifying thumb size, adding extra information to Descriptions</a></h2>
|
||||||
<h2><a href="#customizing_catalog_appearance">Customizing the appearance of the generated catalog</a></h2>
|
<h2><a href="#additional_help_resources">Additional help resources</a></h2>
|
||||||
<hr />
|
<hr />
|
||||||
<h3><a name="selecting_books_to_catalog" id="selecting_books_to_catalog"></a><a href="#toc">Selecting books to catalog</a></h3>
|
<h3><a name="selecting_books_to_catalog" id="selecting_books_to_catalog"></a><a href="#toc">Selecting books to catalog</a></h3>
|
||||||
<p>If you want all of your library cataloged, remove any search or filtering criteria by clearing the <span class="ui_element">Search:</span> field in the main window. With a single book selected, all books in your library will be candidates for inclusion in the generated catalog. Individual books may be excluded by various criteria; see the <a href="#excluded_genres">Excluded genres</a> section below for more information.</p>
|
<p>If you want all of your library cataloged, remove any search or filtering criteria by clearing the <span class="ui_element">Search:</span> field in the main window. With a single book selected, all books in your library will be candidates for inclusion in the generated catalog. Individual books may be excluded by various criteria; see the <a href="#excluded_genres">Excluded genres</a> section below for more information.</p>
|
||||||
@ -107,117 +107,8 @@ h3 {
|
|||||||
<p><span class="ui_element">Extra note</span> specifies a custom column's contents to be inserted into the Description, opposite the cover. For example, you might want to display the date you last read a book using a <span class="user-input">Last Read</span> custom column. For advanced use of the Description note feature, see <a href="http://www.mobileread.com/forums/showpost.php?p=1335767&postcount=395" target="new">this post in the calibre forum</a>.</p>
|
<p><span class="ui_element">Extra note</span> specifies a custom column's contents to be inserted into the Description, opposite the cover. For example, you might want to display the date you last read a book using a <span class="user-input">Last Read</span> custom column. For advanced use of the Description note feature, see <a href="http://www.mobileread.com/forums/showpost.php?p=1335767&postcount=395" target="new">this post in the calibre forum</a>.</p>
|
||||||
<p><span class="ui_element">Merge with Comments</span> specifies a custom column whose content will be non-destructively merged with the Comments metadata during catalog generation. For example, you might have a custom column <span class="user-input">Author Bio</span> that you'd like to append to the Comments metadata. You can choose to insert the custom column contents before or after the Comments section, and optionally separate the appended content with a horizontal rule. Eligible custom column types include <span class="user-input">text</span>, <span class="user-input">comments</span>, and <span class="user-input">composite</span>.</p>
|
<p><span class="ui_element">Merge with Comments</span> specifies a custom column whose content will be non-destructively merged with the Comments metadata during catalog generation. For example, you might have a custom column <span class="user-input">Author Bio</span> that you'd like to append to the Comments metadata. You can choose to insert the custom column contents before or after the Comments section, and optionally separate the appended content with a horizontal rule. Eligible custom column types include <span class="user-input">text</span>, <span class="user-input">comments</span>, and <span class="user-input">composite</span>.</p>
|
||||||
<hr />
|
<hr />
|
||||||
<h3><a name="customizing_catalog_appearance" id="customizing_catalog_appearance"></a><a href="#toc">Customizing catalog appearance</a></h3>
|
<h3><a name="additional_help_resources" id="additional_help_resources"></a>Additional help resources</h3>
|
||||||
<p>If you wish to change the default appearance of the Description pages or Section lists, you can do so by modifying a local copy of the catalog's template and CSS files, overriding the default layout. This requires familiarity with HTML and CSS.<br />
|
<p>For more information on calibre's Catalog feature, see the MobileRead forum sticky <a href="http://www.mobileread.com/forums/showthread.php?t=118556">Creating Catalogs - Start here</a>, where you can also find information on how to submit a bug report.</p>
|
||||||
</p>
|
<p>To ask questions or discuss calibre's Catalog feature with other users, visit the MobileRead forum <a href="http://www.mobileread.com/forums/forumdisplay.php?f=238">Calibre Catalogs</a>.</p>
|
||||||
<ul>
|
|
||||||
<li>Open your calibre configuration directory - <span class="ui_element">Preferences|Advanced|Miscellaneous|Open calibre configuration directory</span>.<br />
|
|
||||||
</li>
|
|
||||||
<li>Within this directory, create a subdirectory <span class="user-input">resources</span> (if it doesn't already exist).<br />
|
|
||||||
</li>
|
|
||||||
<li>Within the <span class="user-input">resources</span> directory, create a subdirectory <span class="user-input">catalog</span>.<br />
|
|
||||||
</li>
|
|
||||||
<li>Open a new file system window, navigate to the default catalog resources folder:<br />
|
|
||||||
<ul>
|
|
||||||
<li>OSX: <span class="user-input">Applications/calibre/Contents/Resources/resources/catalog</span><br />
|
|
||||||
</li>
|
|
||||||
<li>Windows: <span class="user-input">Program Files\Calibre2\resources\catalog</span><br />
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li>Copy all of the files from the default catalog resources folder to the newly created <span class="user-input">resources/catalog</span> directory within your calibre configuration directory.<br />
|
|
||||||
</li>
|
|
||||||
<li>Edit <span class="user-input">template.xhtml</span> and <span class="user-input">stylesheet.css</span> to your preferences. If you want to start over, copy from the default template and CSS files again. If you want to discard your customizations, delete the <span class="user-input">catalog</span> subfolder from your calibre configuration resources directory.<br />
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<p>Customizing the Description page. Available fields in <span class="user-input">template.xhtml</span>:<br />
|
|
||||||
</p>
|
|
||||||
<ul>
|
|
||||||
<li>{author} - Book author(s)<br />
|
|
||||||
</li>
|
|
||||||
<li>{author_prefix} - Checkmark if read, 'X' if wishlist, else blank<br />
|
|
||||||
</li>
|
|
||||||
<li>{comments} - Contents of the book's Comments field<br />
|
|
||||||
</li>
|
|
||||||
<li>{formats} - List of installed formats<br />
|
|
||||||
</li>
|
|
||||||
<li>{genres} - List of genres<br />
|
|
||||||
</li>
|
|
||||||
<li>{note_content} - Note content as specified in options<br />
|
|
||||||
</li>
|
|
||||||
<li>{note_source} - Note source field as specified in options<br />
|
|
||||||
</li>
|
|
||||||
<li>{pubdate} - Month/Year published<br />
|
|
||||||
</li>
|
|
||||||
<li>{publisher} - Publisher<br />
|
|
||||||
</li>
|
|
||||||
<li>{pubmonth} - Month published<br />
|
|
||||||
</li>
|
|
||||||
<li>{pubyear} - Year published<br />
|
|
||||||
</li>
|
|
||||||
<li>{rating} - Graphical rating<br />
|
|
||||||
</li>
|
|
||||||
<li>{series} - Series name<br />
|
|
||||||
</li>
|
|
||||||
<li>{series_index} - Series index<br />
|
|
||||||
</li>
|
|
||||||
<li>{thumb} - Cover thumb url<br />
|
|
||||||
</li>
|
|
||||||
<li>{title} - Book title<br />
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<p>Example: Changing year of publication in Description header<br />
|
|
||||||
</p>
|
|
||||||
<ul>
|
|
||||||
<li>Default: <td class="date">{pubyear}</td><br />
|
|
||||||
</li>
|
|
||||||
<li>Removed: <td class="date"></td><br />
|
|
||||||
</li>
|
|
||||||
<li>Augmented: <td class="date">Year of publication: {pubyear}</td><br />
|
|
||||||
</li>
|
|
||||||
<li>Modified: <td class="date">{pubmonth} {pubyear}</td></li>
|
|
||||||
</ul>
|
|
||||||
<p>Customizing the Section lists. Templates controlling the display of book titles in the various Section lists (Books by Author, Books by Title, etc) may be edited to taste. <br />
|
|
||||||
Available fields in <span class="user-input">section_list_templates.py</span>:<br />
|
|
||||||
</p>
|
|
||||||
<ul>
|
|
||||||
<li>{title} - Title of the book<br />
|
|
||||||
</li>
|
|
||||||
<li>{series} - Series name<br />
|
|
||||||
</li>
|
|
||||||
<li>{series_index} - Number of the book in the series<br />
|
|
||||||
</li>
|
|
||||||
<li>{rating} - 0-5 stars<br />
|
|
||||||
</li>
|
|
||||||
<li>{rating_parens} - (0-5 stars)<br />
|
|
||||||
</li>
|
|
||||||
<li>{pubyear} - Year the book was published<br />
|
|
||||||
</li>
|
|
||||||
<li>{pubyear_parens} - (Year the book was published)<br />
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<p>Example: Changing Books by Author to remove year of publication:<br />
|
|
||||||
Default:</p>
|
|
||||||
<p><img> </p>
|
|
||||||
<p class="code_header">Code:<br />
|
|
||||||
</p>
|
|
||||||
<p class="code_body">by_authors_normal_title_template = '{title} {pubyear_parens}'<br />
|
|
||||||
</p>
|
|
||||||
<p class="code_body">by_authors_series_title_template = '[{series_index}] {title} {pubyear_parens}'<br />
|
|
||||||
</p>
|
|
||||||
<p>Year of publication removed:</p>
|
|
||||||
<p>Code:<br />
|
|
||||||
by_authors_normal_title_template = '{title}'<br />
|
|
||||||
by_authors_series_title_template = '[{series_index}] {title}'<br />
|
|
||||||
Rating added:</p>
|
|
||||||
<p>Code:<br />
|
|
||||||
by_authors_normal_title_template = '{title} {rating}'<br />
|
|
||||||
by_authors_series_title_template = '[{series_index}] {title} {rating}'</p>
|
|
||||||
<p>Tips for experimenting with customization:<br />
|
|
||||||
Work with a small subset of your catalog, 5-10 books<br />
|
|
||||||
If you are experimenting with Section list templates, disable Descriptions in E-book options - catalog generation will be much faster.<br />
|
|
||||||
If you are experimenting with CSS, build an EPUB version of your catalog, explode it with the Tweak EPUB feature to find the class of the element you want to change.</p>
|
|
||||||
<p> </p>
|
|
||||||
<p> </p>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -406,9 +406,9 @@ class ITUNES(DriverBase):
|
|||||||
@return: A BookList.
|
@return: A BookList.
|
||||||
|
|
||||||
Implementation notes:
|
Implementation notes:
|
||||||
iTunes does not sync purchased books, they are only on the device. They are
|
iTunes does not sync purchased books, they are only on the device. They are visible, but
|
||||||
visible, but they are not backed up to iTunes. Since calibre can't manage them,
|
they are not backed up to iTunes. Since calibre can't manage them, don't show them in the
|
||||||
don't show them in the list of device books.
|
list of device books.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if not oncard:
|
if not oncard:
|
||||||
@ -1638,20 +1638,9 @@ class ITUNES(DriverBase):
|
|||||||
logger().info('%s%s' % (' '*indent,'-' * len(msg)))
|
logger().info('%s%s' % (' '*indent,'-' * len(msg)))
|
||||||
|
|
||||||
for book in booklist:
|
for book in booklist:
|
||||||
tl = [i.title for i in booklist]
|
|
||||||
lt = max(tl, key=len)
|
|
||||||
al = [i.author for i in booklist]
|
|
||||||
la = max(al, key=len)
|
|
||||||
asl = [i.author_sort for i in booklist]
|
|
||||||
las = max(asl, key=len)
|
|
||||||
if isosx:
|
if isosx:
|
||||||
fs = '{!s}{:<%d} {:<%d} {:<%d} {:<10} {!s}' % (len(lt),
|
logger().info("%s%-40.40s %-30.30s %-10.10s %s" %
|
||||||
len(la), len(las))
|
(' '*indent,book.title, book.author, str(book.library_id)[-9:], book.uuid))
|
||||||
logger().info(fs.format(' '*indent, book.title, book.author,
|
|
||||||
book.author_sort, str(book.library_id)[-9:],
|
|
||||||
book.uuid))
|
|
||||||
#logger().info("%s%-40.40s %-30.30s %-10.10s %s" %
|
|
||||||
# (' '*indent,book.title, book.author, str(book.library_id)[-9:], book.uuid))
|
|
||||||
elif iswindows:
|
elif iswindows:
|
||||||
logger().info("%s%-40.40s %-30.30s" %
|
logger().info("%s%-40.40s %-30.30s" %
|
||||||
(' '*indent,book.title, book.author))
|
(' '*indent,book.title, book.author))
|
||||||
|
@ -27,7 +27,8 @@ def fingerprint(d):
|
|||||||
|
|
||||||
class MTP_DEVICE(MTPDeviceBase):
|
class MTP_DEVICE(MTPDeviceBase):
|
||||||
|
|
||||||
supported_platforms = ['linux', 'osx']
|
# libusb(x) does not work on OS X. So no MTP support for OS X
|
||||||
|
supported_platforms = ['linux']
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
MTPDeviceBase.__init__(self, *args, **kwargs)
|
MTPDeviceBase.__init__(self, *args, **kwargs)
|
||||||
|
@ -292,7 +292,15 @@ if islinux:
|
|||||||
|
|
||||||
libusb_scanner = LibUSBScanner()
|
libusb_scanner = LibUSBScanner()
|
||||||
if isosx:
|
if isosx:
|
||||||
osx_scanner = libusb_scanner
|
# Apparently libusb causes mem leaks on some Macs and hangs on others and
|
||||||
|
# works on a few. OS X users will just have to live without MTP support.
|
||||||
|
# See https://bugs.launchpad.net/calibre/+bug/1044706
|
||||||
|
# See https://bugs.launchpad.net/calibre/+bug/1044758
|
||||||
|
# osx_scanner = libusb_scanner
|
||||||
|
usbobserver, usbobserver_err = plugins['usbobserver']
|
||||||
|
if usbobserver is None:
|
||||||
|
raise RuntimeError('Failed to load usbobserver: %s'%usbobserver_err)
|
||||||
|
osx_scanner = usbobserver.get_usb_devices
|
||||||
|
|
||||||
if isfreebsd:
|
if isfreebsd:
|
||||||
freebsd_scanner = FreeBSDScanner()
|
freebsd_scanner = FreeBSDScanner()
|
||||||
|
@ -67,7 +67,7 @@ class GenerateCatalogAction(InterfaceAction):
|
|||||||
# jobs.results is a list - the first entry is the intended title for the dialog
|
# jobs.results is a list - the first entry is the intended title for the dialog
|
||||||
# Subsequent strings are error messages
|
# Subsequent strings are error messages
|
||||||
dialog_title = job.result.pop(0)
|
dialog_title = job.result.pop(0)
|
||||||
if re.match('warning:', job.result[0].lower()):
|
if re.search('warning', job.result[0].lower()):
|
||||||
msg = _("Catalog generation complete, with warnings.")
|
msg = _("Catalog generation complete, with warnings.")
|
||||||
warning_dialog(self.gui, dialog_title, msg, det_msg='\n'.join(job.result), show=True)
|
warning_dialog(self.gui, dialog_title, msg, det_msg='\n'.join(job.result), show=True)
|
||||||
else:
|
else:
|
||||||
|
@ -204,14 +204,14 @@ class PluginWidget(QWidget,Ui_Form):
|
|||||||
|
|
||||||
CheckBoxControls (c_type: check_box):
|
CheckBoxControls (c_type: check_box):
|
||||||
['generate_titles','generate_series','generate_genres',
|
['generate_titles','generate_series','generate_genres',
|
||||||
'generate_recently_added','generate_descriptions','include_hr']
|
'generate_recently_added','generate_descriptions','include_hr']
|
||||||
ComboBoxControls (c_type: combo_box):
|
ComboBoxControls (c_type: combo_box):
|
||||||
['exclude_source_field','header_note_source_field',
|
['exclude_source_field','header_note_source_field',
|
||||||
'merge_source_field']
|
'merge_source_field']
|
||||||
LineEditControls (c_type: line_edit):
|
LineEditControls (c_type: line_edit):
|
||||||
['exclude_genre']
|
['exclude_genre']
|
||||||
RadioButtonControls (c_type: radio_button):
|
RadioButtonControls (c_type: radio_button):
|
||||||
['merge_before','merge_after']
|
['merge_before','merge_after','generate_new_cover', 'use_existing_cover']
|
||||||
SpinBoxControls (c_type: spin_box):
|
SpinBoxControls (c_type: spin_box):
|
||||||
['thumb_width']
|
['thumb_width']
|
||||||
TableWidgetControls (c_type: table_widget):
|
TableWidgetControls (c_type: table_widget):
|
||||||
|
@ -177,7 +177,7 @@ The default pattern \[.+\]|\+ excludes tags of the form [tag], e.g., [Test book]
|
|||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Tags to &exclude (regex)</string>
|
<string>Tags to &exclude (regex):</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="textFormat">
|
<property name="textFormat">
|
||||||
<enum>Qt::AutoText</enum>
|
<enum>Qt::AutoText</enum>
|
||||||
@ -237,7 +237,7 @@ The default pattern \[.+\]|\+ excludes tags of the form [tag], e.g., [Test book]
|
|||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Results of regex</string>
|
<string>Results of regex:</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="textFormat">
|
<property name="textFormat">
|
||||||
<enum>Qt::AutoText</enum>
|
<enum>Qt::AutoText</enum>
|
||||||
@ -325,7 +325,7 @@ The default pattern \[.+\]|\+ excludes tags of the form [tag], e.g., [Test book]
|
|||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>&Thumb width</string>
|
<string>&Thumb width:</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="alignment">
|
<property name="alignment">
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
@ -379,7 +379,7 @@ The default pattern \[.+\]|\+ excludes tags of the form [tag], e.g., [Test book]
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="label_3">
|
<widget class="QLabel" name="label_3">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>E&xtra note</string>
|
<string>E&xtra note:</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="buddy">
|
<property name="buddy">
|
||||||
<cstring>header_note_source_field</cstring>
|
<cstring>header_note_source_field</cstring>
|
||||||
@ -430,7 +430,7 @@ The default pattern \[.+\]|\+ excludes tags of the form [tag], e.g., [Test book]
|
|||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>&Merge with Comments</string>
|
<string>&Merge with Comments:</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="alignment">
|
<property name="alignment">
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
@ -499,6 +499,43 @@ The default pattern \[.+\]|\+ excludes tags of the form [tag], e.g., [Test book]
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<layout class="QHBoxLayout" name="replace_cover_hl">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>175</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Catalog cover:</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="generate_new_cover">
|
||||||
|
<property name="text">
|
||||||
|
<string>Generate new cover</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="use_existing_cover">
|
||||||
|
<property name="text">
|
||||||
|
<string>Use existing cover</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -7,7 +7,7 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2012, Kovid Goyal <kovid@kovidgoyal.net>'
|
__copyright__ = '2012, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import os
|
import os, shutil, time
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
|
||||||
from calibre import strftime
|
from calibre import strftime
|
||||||
@ -324,6 +324,8 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
|
|
||||||
if opts.verbose:
|
if opts.verbose:
|
||||||
log.info(" Begin catalog source generation")
|
log.info(" Begin catalog source generation")
|
||||||
|
|
||||||
|
# Returns False if nothing to catalog or author_sort mismatches while building MOBI
|
||||||
catalog_source_built = catalog.build_sources()
|
catalog_source_built = catalog.build_sources()
|
||||||
|
|
||||||
if opts.verbose:
|
if opts.verbose:
|
||||||
@ -338,9 +340,13 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
OptionRecommendation.HIGH))
|
OptionRecommendation.HIGH))
|
||||||
recommendations.append(('comments', '', OptionRecommendation.HIGH))
|
recommendations.append(('comments', '', OptionRecommendation.HIGH))
|
||||||
|
|
||||||
# >>> Use to debug generated catalog code before conversion <<<
|
"""
|
||||||
if False:
|
>>> Use to debug generated catalog code before pipeline conversion <<<
|
||||||
setattr(opts,'debug_pipeline',os.path.expanduser("~/Desktop/Catalog debug"))
|
"""
|
||||||
|
GENERATE_DEBUG_EPUB = False
|
||||||
|
if GENERATE_DEBUG_EPUB:
|
||||||
|
catalog_debug_path = os.path.join(os.path.expanduser('~'),'Desktop','Catalog debug')
|
||||||
|
setattr(opts,'debug_pipeline',os.path.expanduser(catalog_debug_path))
|
||||||
|
|
||||||
dp = getattr(opts, 'debug_pipeline', None)
|
dp = getattr(opts, 'debug_pipeline', None)
|
||||||
if dp is not None:
|
if dp is not None:
|
||||||
@ -355,9 +361,9 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
recommendations.append(('book_producer',opts.output_profile,
|
recommendations.append(('book_producer',opts.output_profile,
|
||||||
OptionRecommendation.HIGH))
|
OptionRecommendation.HIGH))
|
||||||
|
|
||||||
# If cover exists, use it
|
# Use existing cover or generate new cover
|
||||||
cpath = None
|
cpath = None
|
||||||
generate_new_cover = False
|
existing_cover = False
|
||||||
try:
|
try:
|
||||||
search_text = 'title:"%s" author:%s' % (
|
search_text = 'title:"%s" author:%s' % (
|
||||||
opts.catalog_title.replace('"', '\\"'), 'calibre')
|
opts.catalog_title.replace('"', '\\"'), 'calibre')
|
||||||
@ -365,19 +371,18 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
if matches:
|
if matches:
|
||||||
cpath = db.cover(matches[0], index_is_id=True, as_path=True)
|
cpath = db.cover(matches[0], index_is_id=True, as_path=True)
|
||||||
if cpath and os.path.exists(cpath):
|
if cpath and os.path.exists(cpath):
|
||||||
recommendations.append(('cover', cpath,
|
existing_cover = True
|
||||||
OptionRecommendation.HIGH))
|
|
||||||
log.info("using existing cover")
|
|
||||||
else:
|
|
||||||
log.info("no existing cover, generating new cover")
|
|
||||||
generate_new_cover = True
|
|
||||||
else:
|
|
||||||
log.info("no existing cover, generating new cover")
|
|
||||||
generate_new_cover = True
|
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if generate_new_cover:
|
if self.opts.use_existing_cover and not existing_cover:
|
||||||
|
log.warning("no existing catalog cover found")
|
||||||
|
|
||||||
|
if self.opts.use_existing_cover and existing_cover:
|
||||||
|
recommendations.append(('cover', cpath, OptionRecommendation.HIGH))
|
||||||
|
log.info("using existing catalog cover")
|
||||||
|
else:
|
||||||
|
log.info("generating new catalog cover")
|
||||||
new_cover_path = PersistentTemporaryFile(suffix='.jpg')
|
new_cover_path = PersistentTemporaryFile(suffix='.jpg')
|
||||||
new_cover = calibre_cover(opts.catalog_title.replace('"', '\\"'), 'calibre')
|
new_cover = calibre_cover(opts.catalog_title.replace('"', '\\"'), 'calibre')
|
||||||
new_cover_path.write(new_cover)
|
new_cover_path.write(new_cover)
|
||||||
@ -397,6 +402,13 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
if GENERATE_DEBUG_EPUB:
|
||||||
|
from calibre.ebooks.tweak import zip_rebuilder
|
||||||
|
input_path = os.path.join(catalog_debug_path,'input')
|
||||||
|
shutil.copy(P('catalog/mimetype'),input_path)
|
||||||
|
shutil.copytree(P('catalog/META-INF'),os.path.join(input_path,'META-INF'))
|
||||||
|
zip_rebuilder(input_path, os.path.join(catalog_debug_path,'input.epub'))
|
||||||
|
|
||||||
# returns to gui2.actions.catalog:catalog_generated()
|
# returns to gui2.actions.catalog:catalog_generated()
|
||||||
return catalog.error
|
return catalog.error
|
||||||
|
|
||||||
|
@ -20,6 +20,9 @@ from calibre.utils.icu import capitalize, collation_order, sort_key
|
|||||||
from calibre.utils.magick.draw import thumbnail
|
from calibre.utils.magick.draw import thumbnail
|
||||||
from calibre.utils.zipfile import ZipFile
|
from calibre.utils.zipfile import ZipFile
|
||||||
|
|
||||||
|
class AuthorSortMismatchException(Exception): pass
|
||||||
|
class EmptyCatalogException(Exception): pass
|
||||||
|
|
||||||
class CatalogBuilder(object):
|
class CatalogBuilder(object):
|
||||||
'''
|
'''
|
||||||
Generates catalog source files from calibre database
|
Generates catalog source files from calibre database
|
||||||
@ -506,11 +509,16 @@ class CatalogBuilder(object):
|
|||||||
False: failed to build
|
False: failed to build
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if self.books_by_title is None:
|
try:
|
||||||
if not self.fetch_books_by_title():
|
self.fetch_books_by_title()
|
||||||
return False
|
except EmptyCatalogException:
|
||||||
if not self.fetch_books_by_author():
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.fetch_books_by_author()
|
||||||
|
except AuthorSortMismatchException:
|
||||||
|
return False
|
||||||
|
|
||||||
self.fetch_bookmarks()
|
self.fetch_bookmarks()
|
||||||
if self.opts.generate_descriptions:
|
if self.opts.generate_descriptions:
|
||||||
self.generate_thumbnails()
|
self.generate_thumbnails()
|
||||||
@ -525,9 +533,9 @@ class CatalogBuilder(object):
|
|||||||
self.generate_html_by_genres()
|
self.generate_html_by_genres()
|
||||||
# If this is the only Section, and there are no genres, bail
|
# If this is the only Section, and there are no genres, bail
|
||||||
if self.opts.section_list == ['Genres'] and not self.genres:
|
if self.opts.section_list == ['Genres'] and not self.genres:
|
||||||
error_msg = _("No enabled genres found to catalog.\n")
|
error_msg = _("No genres to catalog.\n")
|
||||||
if not self.opts.cli_environment:
|
if not self.opts.cli_environment:
|
||||||
error_msg += "Check 'Excluded genres'\nin E-book options.\n"
|
error_msg += "Check 'Excluded genres' regex in E-book options.\n"
|
||||||
self.opts.log.error(error_msg)
|
self.opts.log.error(error_msg)
|
||||||
self.error.append(_('No books available to catalog'))
|
self.error.append(_('No books available to catalog'))
|
||||||
self.error.append(error_msg)
|
self.error.append(error_msg)
|
||||||
@ -755,6 +763,56 @@ class CatalogBuilder(object):
|
|||||||
if not os.path.isdir(images_path):
|
if not os.path.isdir(images_path):
|
||||||
os.makedirs(images_path)
|
os.makedirs(images_path)
|
||||||
|
|
||||||
|
def detect_author_sort_mismatches(self):
|
||||||
|
""" Detect author_sort mismatches.
|
||||||
|
|
||||||
|
Sort by author, look for inconsistencies in author_sort among
|
||||||
|
similarly-named authors. Fatal for MOBI generation, a mere
|
||||||
|
annoyance for EPUB.
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
self.books_by_title (list): list of books to catalog
|
||||||
|
|
||||||
|
Output:
|
||||||
|
self.books_by_author (list): sorted by author
|
||||||
|
|
||||||
|
Exceptions:
|
||||||
|
AuthorSortMismatchException: author_sort mismatch detected
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.books_by_author = sorted(list(self.books_by_title), key=self._kf_books_by_author_sorter_author)
|
||||||
|
authors = [(record['author'], record['author_sort']) for record in self.books_by_author]
|
||||||
|
current_author = authors[0]
|
||||||
|
for (i,author) in enumerate(authors):
|
||||||
|
if author != current_author and i:
|
||||||
|
if author[0] == current_author[0]:
|
||||||
|
if self.opts.fmt == 'mobi':
|
||||||
|
# Exit if building MOBI
|
||||||
|
error_msg = _("<p>Inconsistent Author Sort values for Author<br/>" +
|
||||||
|
"'{!s}':</p>".format(author[0]) +
|
||||||
|
"<p><center><b>{!s}</b> != <b>{!s}</b></center></p>".format(author[1],current_author[1]) +
|
||||||
|
"<p>Unable to build MOBI catalog.<br/>" +
|
||||||
|
"Select all books by '{!s}', apply correct Author Sort value in Edit Metadata dialog, then rebuild the catalog.\n<p>".format(author[0]))
|
||||||
|
|
||||||
|
self.opts.log.warn('\n*** Metadata error ***')
|
||||||
|
self.opts.log.warn(error_msg)
|
||||||
|
|
||||||
|
self.error.append('Author Sort mismatch')
|
||||||
|
self.error.append(error_msg)
|
||||||
|
raise AuthorSortMismatchException
|
||||||
|
else:
|
||||||
|
# Warning if building non-MOBI
|
||||||
|
if not self.error:
|
||||||
|
self.error.append('Author Sort mismatch')
|
||||||
|
|
||||||
|
error_msg = _("Warning: Inconsistent Author Sort values for Author '{!s}':\n".format(author[0]) +
|
||||||
|
" {!s} != {!s}\n".format(author[1],current_author[1]))
|
||||||
|
self.opts.log.warn('\n*** Metadata warning ***')
|
||||||
|
self.opts.log.warn(error_msg)
|
||||||
|
self.error.append(error_msg)
|
||||||
|
|
||||||
|
current_author = author
|
||||||
|
|
||||||
def discover_prefix(self, record):
|
def discover_prefix(self, record):
|
||||||
""" Return a prefix for record.
|
""" Return a prefix for record.
|
||||||
|
|
||||||
@ -883,44 +941,9 @@ class CatalogBuilder(object):
|
|||||||
|
|
||||||
self.update_progress_full_step(_("Sorting database"))
|
self.update_progress_full_step(_("Sorting database"))
|
||||||
|
|
||||||
# Test for author_sort mismatches
|
self.detect_author_sort_mismatches()
|
||||||
self.books_by_author = sorted(list(self.books_by_title), key=self._kf_books_by_author_sorter_author)
|
|
||||||
|
|
||||||
authors = [(record['author'], record['author_sort']) for record in self.books_by_author]
|
# Sort authors using sort_key to normalize accented letters
|
||||||
current_author = authors[0]
|
|
||||||
for (i,author) in enumerate(authors):
|
|
||||||
if author != current_author and i:
|
|
||||||
if author[0] == current_author[0]:
|
|
||||||
if self.opts.fmt == 'mobi':
|
|
||||||
# Exit if building MOBI
|
|
||||||
error_msg = _(
|
|
||||||
'''Inconsistent Author Sort values for
|
|
||||||
Author '{0}':
|
|
||||||
'{1}' <> '{2}'
|
|
||||||
Unable to build MOBI catalog.\n
|
|
||||||
Select all books by '{0}', apply correct Author Sort value in Edit Metadata dialog, then rebuild the catalog.\n''').format(author[0],author[1],current_author[1])
|
|
||||||
self.opts.log.warn('\n*** Metadata error ***')
|
|
||||||
self.opts.log.warn(error_msg)
|
|
||||||
|
|
||||||
self.error.append('Author Sort mismatch')
|
|
||||||
self.error.append(error_msg)
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
# Warning if building non-MOBI
|
|
||||||
if not self.error:
|
|
||||||
self.error.append('Author Sort mismatch')
|
|
||||||
|
|
||||||
error_msg = _(
|
|
||||||
'''Warning: inconsistent Author Sort values for
|
|
||||||
Author '{0}':
|
|
||||||
'{1}' <> '{2}'\n''').format(author[0],author[1],current_author[1])
|
|
||||||
self.opts.log.warn('\n*** Metadata warning ***')
|
|
||||||
self.opts.log.warn(error_msg)
|
|
||||||
self.error.append(error_msg)
|
|
||||||
|
|
||||||
current_author = author
|
|
||||||
|
|
||||||
# Second pass: Sort using sort_key to normalize accented letters
|
|
||||||
# Determine the longest author_sort length before sorting
|
# Determine the longest author_sort length before sorting
|
||||||
asl = [i['author_sort'] for i in self.books_by_author]
|
asl = [i['author_sort'] for i in self.books_by_author]
|
||||||
las = max(asl, key=len)
|
las = max(asl, key=len)
|
||||||
@ -1156,13 +1179,12 @@ Author '{0}':
|
|||||||
for title in self.books_by_title:
|
for title in self.books_by_title:
|
||||||
self.opts.log.info((u" %-40s %-40s" % (title['title'][0:40],
|
self.opts.log.info((u" %-40s %-40s" % (title['title'][0:40],
|
||||||
title['title_sort'][0:40])).encode('utf-8'))
|
title['title_sort'][0:40])).encode('utf-8'))
|
||||||
return True
|
|
||||||
else:
|
else:
|
||||||
error_msg = _("No books found to catalog.\nCheck 'Excluded books' criteria in E-book options.\n")
|
error_msg = _("No books to catalog.\nCheck 'Excluded books' rules in E-book options.\n")
|
||||||
self.opts.log.error('*** ' + error_msg + ' ***')
|
self.opts.log.error('*** ' + error_msg + ' ***')
|
||||||
self.error.append(_('No books available to include in catalog'))
|
self.error.append(_('No books available to include in catalog'))
|
||||||
self.error.append(error_msg)
|
self.error.append(error_msg)
|
||||||
return False
|
raise EmptyCatalogException
|
||||||
|
|
||||||
def fetch_bookmarks(self):
|
def fetch_bookmarks(self):
|
||||||
""" Interrogate connected Kindle for bookmarks.
|
""" Interrogate connected Kindle for bookmarks.
|
||||||
|
@ -37,14 +37,16 @@ class OutputDevice : public PdfOutputDevice {
|
|||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
return _vscprintf(pszFormat, args);
|
return _vscprintf(pszFormat, args);
|
||||||
#else
|
#else
|
||||||
char buf[10];
|
char *buf;
|
||||||
int res;
|
int res, len=1024;
|
||||||
res = vsnprintf(buf, 1, pszFormat, args);
|
while(true) {
|
||||||
if (res < 0) {
|
buf = new (std::nothrow) char[len+1];
|
||||||
PyErr_SetString(PyExc_Exception, "Something bad happened while calling vsnprintf to get buffer length");
|
if (buf == NULL) { PyErr_NoMemory(); throw pyerr(); }
|
||||||
throw pyerr();
|
res = vsnprintf(buf, len, pszFormat, args);
|
||||||
|
delete[] buf;
|
||||||
|
if (res >= 0) return res + 1;
|
||||||
|
len *= 2;
|
||||||
}
|
}
|
||||||
return static_cast<long>(res+1);
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user