Sync to trunk.

This commit is contained in:
John Schember 2011-01-21 07:49:43 -05:00
commit 91e87adbd8
19 changed files with 1563 additions and 698 deletions

View File

@ -1,6 +1,6 @@
__license__ = 'GPL v3' __license__ = 'GPL v3'
__copyright__ = '2008-2010, Darko Miletic <darko.miletic at gmail.com>' __copyright__ = '2008-2011, Darko Miletic <darko.miletic at gmail.com>'
''' '''
blic.rs blic.rs
''' '''
@ -21,21 +21,53 @@ class Blic(BasicNewsRecipe):
masthead_url = 'http://www.blic.rs/resources/images/header/header_back.png' masthead_url = 'http://www.blic.rs/resources/images/header/header_back.png'
language = 'sr' language = 'sr'
publication_type = 'newspaper' publication_type = 'newspaper'
extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} body{font-family: Georgia, serif1, serif} .article_description{font-family: Arial, sans1, sans-serif} .img_full{float: none} img{margin-bottom: 0.8em} ' extra_css = """
@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)}
@font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)}
body{font-family: Georgia, serif1, serif}
.articledescription,#nadnaslov,.article_info{font-family: Arial, sans1, sans-serif}
.img_full{float: none}
#nadnaslov{font-size: small}
#article_lead{font-size: 1.5em}
h1{color: red}
.potpis{font-size: x-small; color: gray}
.article_info{font-size: small}
img{margin-bottom: 0.8em; margin-top: 0.8em; display: block}
"""
conversion_options = { conversion_options = {
'comment' : description 'comment' : description
, 'tags' : category , 'tags' : category
, 'publisher': publisher , 'publisher': publisher
, 'language' : language , 'language' : language
, 'linearize_tables' : True
} }
preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')] preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')]
remove_tags_before = dict(name='div', attrs={'id':'article_info'}) remove_tags_before = dict(name='div', attrs={'id':'article_info'})
remove_tags = [dict(name=['object','link'])] remove_tags = [dict(name=['object','link','meta','base','object','embed'])]
remove_attributes = ['width','height'] remove_attributes = ['width','height','m_id','m_ext','mlg_id','poll_id','v_id']
feeds = [(u'Danasnje Vesti', u'http://www.blic.rs/rss/danasnje-vesti')] feeds = [
(u'Politika' , u'http://www.blic.rs/rss/Vesti/Politika')
,(u'Tema Dana' , u'http://www.blic.rs/rss/Vesti/Tema-Dana')
,(u'Svet' , u'http://www.blic.rs/rss/Vesti/Svet')
,(u'Drustvo' , u'http://www.blic.rs/rss/Vesti/Drustvo')
,(u'Ekonomija' , u'http://www.blic.rs/rss/Vesti/Ekonomija')
,(u'Hronika' , u'http://www.blic.rs/rss/Vesti/Hronika')
,(u'Beograd' , u'http://www.blic.rs/rss/Vesti/Beograd')
,(u'Srbija' , u'http://www.blic.rs/rss/Vesti/Srbija')
,(u'Vojvodina' , u'http://www.blic.rs/rss/Vesti/Vojvodina')
,(u'Republika Srpska' , u'http://www.blic.rs/rss/Vesti/Republika-Srpska')
,(u'Reportaza' , u'http://www.blic.rs/rss/Vesti/Reportaza')
,(u'Dodatak' , u'http://www.blic.rs/rss/Vesti/Dodatak')
,(u'Zabava' , u'http://www.blic.rs/rss/Zabava')
,(u'Kultura' , u'http://www.blic.rs/rss/Kultura')
,(u'Slobodno Vreme' , u'http://www.blic.rs/rss/Slobodno-vreme')
,(u'IT' , u'http://www.blic.rs/rss/IT')
,(u'Komentar' , u'http://www.blic.rs/rss/Komentar')
,(u'Intervju' , u'http://www.blic.rs/rss/Intervju')
]
def print_version(self, url): def print_version(self, url):
@ -44,4 +76,4 @@ class Blic(BasicNewsRecipe):
def preprocess_html(self, soup): def preprocess_html(self, soup):
for item in soup.findAll(style=True): for item in soup.findAll(style=True):
del item['style'] del item['style']
return self.adeify_images(soup) return soup

View File

@ -241,7 +241,7 @@ def get_parsed_proxy(typ='http', debug=True):
return ans return ans
def browser(honor_time=True, max_time=2, mobile_browser=False): def browser(honor_time=True, max_time=2, mobile_browser=False, user_agent=None):
''' '''
Create a mechanize browser for web scraping. The browser handles cookies, Create a mechanize browser for web scraping. The browser handles cookies,
refresh requests and ignores robots.txt. Also uses proxy if avaialable. refresh requests and ignores robots.txt. Also uses proxy if avaialable.
@ -253,8 +253,10 @@ def browser(honor_time=True, max_time=2, mobile_browser=False):
opener = Browser() opener = Browser()
opener.set_handle_refresh(True, max_time=max_time, honor_time=honor_time) opener.set_handle_refresh(True, max_time=max_time, honor_time=honor_time)
opener.set_handle_robots(False) opener.set_handle_robots(False)
opener.addheaders = [('User-agent', ' Mozilla/5.0 (Windows; U; Windows CE 5.1; rv:1.8.1a3) Gecko/20060610 Minimo/0.016' if mobile_browser else \ if user_agent is None:
'Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.13) Gecko/20101210 Gentoo Firefox/3.6.13')] user_agent = ' Mozilla/5.0 (Windows; U; Windows CE 5.1; rv:1.8.1a3) Gecko/20060610 Minimo/0.016' if mobile_browser else \
'Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.13) Gecko/20101210 Gentoo Firefox/3.6.13'
opener.addheaders = [('User-agent', user_agent)]
http_proxy = get_proxies().get('http', None) http_proxy = get_proxies().get('http', None)
if http_proxy: if http_proxy:
opener.set_proxies({'http':http_proxy}) opener.set_proxies({'http':http_proxy})

View File

@ -21,7 +21,7 @@ class ANDROID(USBMS):
# HTC # HTC
0x0bb4 : { 0x0c02 : [0x100, 0x0227, 0x0226], 0x0c01 : [0x100, 0x0227], 0x0ff9 0x0bb4 : { 0x0c02 : [0x100, 0x0227, 0x0226], 0x0c01 : [0x100, 0x0227], 0x0ff9
: [0x0100, 0x0227, 0x0226], 0x0c87: [0x0100, 0x0227, 0x0226], : [0x0100, 0x0227, 0x0226], 0x0c87: [0x0100, 0x0227, 0x0226],
0xc92 : [0x100], 0xc97: [0x226]}, 0xc92 : [0x100], 0xc97: [0x226], 0xc99 : [0x0100]},
# Eken # Eken
0x040d : { 0x8510 : [0x0001], 0x0851 : [0x1] }, 0x040d : { 0x8510 : [0x0001], 0x0851 : [0x1] },
@ -54,7 +54,7 @@ class ANDROID(USBMS):
0x1004 : { 0x61cc : [0x100] }, 0x1004 : { 0x61cc : [0x100] },
# Archos # Archos
0x0e79 : { 0x1420 : [0x0216]}, 0x0e79 : { 0x1419: [0x0216], 0x1420 : [0x0216]},
} }
EBOOK_DIR_MAIN = ['eBooks/import', 'wordplayer/calibretransfer', 'Books'] EBOOK_DIR_MAIN = ['eBooks/import', 'wordplayer/calibretransfer', 'Books']
@ -70,10 +70,10 @@ class ANDROID(USBMS):
'__UMS_COMPOSITE', '_MB200', 'MASS_STORAGE', '_-_CARD', 'SGH-I897', '__UMS_COMPOSITE', '_MB200', 'MASS_STORAGE', '_-_CARD', 'SGH-I897',
'GT-I9000', 'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-I9000', 'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID',
'SCH-I500_CARD', 'SPH-D700_CARD', 'MB810', 'GT-P1000', 'DESIRE', 'SCH-I500_CARD', 'SPH-D700_CARD', 'MB810', 'GT-P1000', 'DESIRE',
'SGH-T849', '_MB300', 'A70S', 'S_ANDROID'] 'SGH-T849', '_MB300', 'A70S', 'S_ANDROID', 'A101IT']
WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD', 'SGH-I897', WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD', 'SGH-I897',
'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_CARD', 'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_CARD',
'A70S'] 'A70S', 'A101IT']
OSX_MAIN_MEM = 'Android Device Main Memory' OSX_MAIN_MEM = 'Android Device Main Memory'

View File

@ -224,6 +224,10 @@ class Dehyphenator(object):
return firsthalf+u'\u2014'+wraptags+secondhalf return firsthalf+u'\u2014'+wraptags+secondhalf
else: else:
if self.format == 'individual_words' and len(firsthalf) + len(secondhalf) <= 6:
if self.verbose > 2:
self.log("too short, returned hyphenated word: " + str(hyphenated))
return hyphenated
if len(firsthalf) <= 2 and len(secondhalf) <= 2: if len(firsthalf) <= 2 and len(secondhalf) <= 2:
if self.verbose > 2: if self.verbose > 2:
self.log("too short, returned hyphenated word: " + str(hyphenated)) self.log("too short, returned hyphenated word: " + str(hyphenated))

View File

@ -4,7 +4,7 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
Fetch cover from LibraryThing.com based on ISBN number. Fetch cover from LibraryThing.com based on ISBN number.
''' '''
import sys, socket, os, re import sys, socket, os, re, random
from lxml import html from lxml import html
import mechanize import mechanize
@ -16,13 +16,26 @@ from calibre.ebooks.chardet import strip_encoding_declarations
OPENLIBRARY = 'http://covers.openlibrary.org/b/isbn/%s-L.jpg?default=false' OPENLIBRARY = 'http://covers.openlibrary.org/b/isbn/%s-L.jpg?default=false'
def get_ua():
choices = [
'Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.2.11) Gecko/20101012 Firefox/3.6.11'
'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)'
'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)'
'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1)'
'Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_0 like Mac OS X; en-us) AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0 Mobile/7A341 Safari/528.16'
'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/0.2.153.1 Safari/525.19'
'Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.2.11) Gecko/20101012 Firefox/3.6.11'
]
return choices[random.randint(0, len(choices)-1)]
class HeadRequest(mechanize.Request): class HeadRequest(mechanize.Request):
def get_method(self): def get_method(self):
return 'HEAD' return 'HEAD'
def check_for_cover(isbn, timeout=5.): def check_for_cover(isbn, timeout=5.):
br = browser() br = browser(user_agent=get_ua())
br.set_handle_redirect(False) br.set_handle_redirect(False)
try: try:
br.open_novisit(HeadRequest(OPENLIBRARY%isbn), timeout=timeout) br.open_novisit(HeadRequest(OPENLIBRARY%isbn), timeout=timeout)
@ -51,7 +64,7 @@ def login(br, username, password, force=True):
def cover_from_isbn(isbn, timeout=5., username=None, password=None): def cover_from_isbn(isbn, timeout=5., username=None, password=None):
src = None src = None
br = browser() br = browser(user_agent=get_ua())
try: try:
return br.open(OPENLIBRARY%isbn, timeout=timeout).read(), 'jpg' return br.open(OPENLIBRARY%isbn, timeout=timeout).read(), 'jpg'
except: except:
@ -100,7 +113,7 @@ def get_social_metadata(title, authors, publisher, isbn, username=None,
from calibre.ebooks.metadata import MetaInformation from calibre.ebooks.metadata import MetaInformation
mi = MetaInformation(title, authors) mi = MetaInformation(title, authors)
if isbn: if isbn:
br = browser() br = browser(user_agent=get_ua())
if username and password: if username and password:
try: try:
login(br, username, password, force=False) login(br, username, password, force=False)

View File

@ -175,9 +175,9 @@ def detect_formatting_type(txt):
# Block quote. # Block quote.
textile_count += len(re.findall(r'(?mu)^bq\.', txt)) textile_count += len(re.findall(r'(?mu)^bq\.', txt))
# Images # Images
textile_count += len(re.findall(r'\![^\s]+(:[^\s]+)*', txt)) textile_count += len(re.findall(r'\![^\s]+(?=.*?/)(:[^\s]+)*', txt))
# Links # Links
textile_count += len(re.findall(r'"(\(.+?\))*[^\(]+?(\(.+?\))*":[^\s]+', txt)) textile_count += len(re.findall(r'"(?=".*?\()(\(.+?\))*[^\(]+?(\(.+?\))*":[^\s]+', txt))
if markdown_count > 5 or textile_count > 5: if markdown_count > 5 or textile_count > 5:
if markdown_count > textile_count: if markdown_count > textile_count:

View File

@ -8,11 +8,12 @@ __docformat__ = 'restructuredtext en'
import os import os
from functools import partial from functools import partial
from PyQt4.Qt import QInputDialog, QPixmap, QMenu from PyQt4.Qt import QPixmap, QMenu
from calibre.gui2 import error_dialog, choose_files, \ from calibre.gui2 import error_dialog, choose_files, \
choose_dir, warning_dialog, info_dialog choose_dir, warning_dialog, info_dialog
from calibre.gui2.dialogs.add_empty_book import AddEmptyBookDialog
from calibre.gui2.widgets import IMAGE_EXTENSIONS from calibre.gui2.widgets import IMAGE_EXTENSIONS
from calibre.ebooks import BOOK_EXTENSIONS from calibre.ebooks import BOOK_EXTENSIONS
from calibre.utils.filenames import ascii_filename from calibre.utils.filenames import ascii_filename
@ -42,7 +43,7 @@ class AddAction(InterfaceAction):
'ebook file is a different book)'), self.add_recursive_multiple) 'ebook file is a different book)'), self.add_recursive_multiple)
self.add_menu.addSeparator() self.add_menu.addSeparator()
self.add_menu.addAction(_('Add Empty book. (Book entry with no ' self.add_menu.addAction(_('Add Empty book. (Book entry with no '
'formats)'), self.add_empty) 'formats)'), self.add_empty, _('Shift+Ctrl+E'))
self.add_menu.addAction(_('Add from ISBN'), self.add_from_isbn) self.add_menu.addAction(_('Add from ISBN'), self.add_from_isbn)
self.qaction.setMenu(self.add_menu) self.qaction.setMenu(self.add_menu)
self.qaction.triggered.connect(self.add_books) self.qaction.triggered.connect(self.add_books)
@ -83,12 +84,21 @@ class AddAction(InterfaceAction):
Add an empty book item to the library. This does not import any formats Add an empty book item to the library. This does not import any formats
from a book file. from a book file.
''' '''
num, ok = QInputDialog.getInt(self.gui, _('How many empty books?'), author = None
_('How many empty books should be added?'), 1, 1, 100) index = self.gui.library_view.currentIndex()
if ok: if index.isValid():
raw = index.model().db.authors(index.row())
if raw:
authors = [a.strip().replace('|', ',') for a in raw.split(',')]
if authors:
author = authors[0]
dlg = AddEmptyBookDialog(self.gui, self.gui.library_view.model().db, author)
if dlg.exec_() == dlg.Accepted:
num = dlg.qty_to_add
from calibre.ebooks.metadata import MetaInformation from calibre.ebooks.metadata import MetaInformation
for x in xrange(num): for x in xrange(num):
self.gui.library_view.model().db.import_book(MetaInformation(None), []) mi = MetaInformation(_('Unknown'), dlg.selected_authors)
self.gui.library_view.model().db.import_book(mi, [])
self.gui.library_view.model().books_added(num) self.gui.library_view.model().books_added(num)
def add_isbns(self, books, add_tags=[]): def add_isbns(self, books, add_tags=[]):

View File

@ -32,7 +32,7 @@ class LibraryUsageStats(object): # {{{
locs = list(self.stats.keys()) locs = list(self.stats.keys())
locs.sort(cmp=lambda x, y: cmp(self.stats[x], self.stats[y]), locs.sort(cmp=lambda x, y: cmp(self.stats[x], self.stats[y]),
reverse=True) reverse=True)
for key in locs[15:]: for key in locs[25:]:
self.stats.pop(key) self.stats.pop(key)
gprefs.set('library_usage_stats', self.stats) gprefs.set('library_usage_stats', self.stats)

View File

@ -379,7 +379,8 @@ def populate_metadata_page(layout, db, book_id, bulk=False, two_column=False, pa
w = bulk_widgets[type](db, col, parent) w = bulk_widgets[type](db, col, parent)
else: else:
w = widgets[type](db, col, parent) w = widgets[type](db, col, parent)
w.initialize(book_id) if book_id is not None:
w.initialize(book_id)
return w return w
x = db.custom_column_num_map x = db.custom_column_num_map
cols = list(x) cols = list(x)

View File

@ -0,0 +1,85 @@
#!/usr/bin/env python
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en'
__license__ = 'GPL v3'
from PyQt4.Qt import QDialog, QGridLayout, QLabel, QDialogButtonBox, \
QApplication, QSpinBox, QToolButton, QIcon
from calibre.ebooks.metadata import authors_to_string, string_to_authors
from calibre.gui2.widgets import CompleteComboBox
from calibre.utils.icu import sort_key
class AddEmptyBookDialog(QDialog):
def __init__(self, parent, db, author):
QDialog.__init__(self, parent)
self.db = db
self.setWindowTitle(_('How many empty books?'))
self._layout = QGridLayout(self)
self.setLayout(self._layout)
self.qty_label = QLabel(_('How many empty books should be added?'))
self._layout.addWidget(self.qty_label, 0, 0, 1, 2)
self.qty_spinbox = QSpinBox(self)
self.qty_spinbox.setRange(1, 10000)
self.qty_spinbox.setValue(1)
self._layout.addWidget(self.qty_spinbox, 1, 0, 1, 2)
self.author_label = QLabel(_('Set the author of the new books to:'))
self._layout.addWidget(self.author_label, 2, 0, 1, 2)
self.authors_combo = CompleteComboBox(self)
self.authors_combo.setSizeAdjustPolicy(
self.authors_combo.AdjustToMinimumContentsLengthWithIcon)
self.authors_combo.setEditable(True)
self._layout.addWidget(self.authors_combo, 3, 0, 1, 1)
self.initialize_authors(db, author)
self.clear_button = QToolButton(self)
self.clear_button.setIcon(QIcon(I('trash.png')))
self.clear_button.setToolTip(_('Reset author to Unknown'))
self.clear_button.clicked.connect(self.reset_author)
self._layout.addWidget(self.clear_button, 3, 1, 1, 1)
button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
button_box.accepted.connect(self.accept)
button_box.rejected.connect(self.reject)
self._layout.addWidget(button_box)
self.resize(self.sizeHint())
def reset_author(self, *args):
self.authors_combo.setEditText(_('Unknown'))
def initialize_authors(self, db, author):
all_authors = db.all_authors()
all_authors.sort(key=lambda x : sort_key(x[1]))
for i in all_authors:
id, name = i
name = [name.strip().replace('|', ',') for n in name.split(',')]
self.authors_combo.addItem(authors_to_string(name))
au = author
if not au:
au = _('Unknown')
self.authors_combo.setEditText(au.replace('|', ','))
self.authors_combo.set_separator('&')
self.authors_combo.set_space_before_sep(True)
self.authors_combo.update_items_cache(db.all_author_names())
@property
def qty_to_add(self):
return self.qty_spinbox.value()
@property
def selected_authors(self):
return string_to_authors(unicode(self.authors_combo.text()))
if __name__ == '__main__':
app = QApplication([])
d = AddEmptyBookDialog()
d.exec_()

View File

@ -775,7 +775,7 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
self.original_tags = unicode(self.tags.text()) self.original_tags = unicode(self.tags.text())
else: else:
self.tags.setText(self.original_tags) self.tags.setText(self.original_tags)
d = TagEditor(self, self.db, self.row) d = TagEditor(self, self.db, self.id)
d.exec_() d.exec_()
if d.result() == QDialog.Accepted: if d.result() == QDialog.Accepted:
tag_string = ', '.join(d.tags) tag_string = ', '.join(d.tags)

View File

@ -10,13 +10,13 @@ from calibre.utils.icu import sort_key
class TagEditor(QDialog, Ui_TagEditor): class TagEditor(QDialog, Ui_TagEditor):
def __init__(self, window, db, index=None): def __init__(self, window, db, id_=None):
QDialog.__init__(self, window) QDialog.__init__(self, window)
Ui_TagEditor.__init__(self) Ui_TagEditor.__init__(self)
self.setupUi(self) self.setupUi(self)
self.db = db self.db = db
self.index = index self.index = db.row(id_)
if self.index is not None: if self.index is not None:
tags = self.db.tags(self.index) tags = self.db.tags(self.index)
else: else:

View File

@ -356,6 +356,13 @@ class %(classname)s(%(base_class)s):
self.populate_options(AutomaticNewsRecipe) self.populate_options(AutomaticNewsRecipe)
self.source_code.setText('') self.source_code.setText('')
def reject(self):
if question_dialog(self, _('Are you sure?'),
_('You will lose any unsaved changes. To save your'
' changes, click the Add/Update recipe button.'
' Continue?'), show_copy_button=False):
ResizableDialog.reject(self)
if __name__ == '__main__': if __name__ == '__main__':
from calibre.gui2 import is_ok_to_use_qt from calibre.gui2 import is_ok_to_use_qt
is_ok_to_use_qt() is_ok_to_use_qt()

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -449,7 +449,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
def set_window_title(self): def set_window_title(self):
self.setWindowTitle(__appname__ + u' - ||%s||'%self.iactions['Choose Library'].library_name()) self.setWindowTitle(__appname__ + u' - || %s ||'%self.iactions['Choose Library'].library_name())
def location_selected(self, location): def location_selected(self, location):
''' '''

View File

@ -580,7 +580,7 @@ class EPUB_MOBI(CatalogPlugin):
"pipeline to the specified " "pipeline to the specified "
"directory. Useful if you are unsure at which stage " "directory. Useful if you are unsure at which stage "
"of the conversion process a bug is occurring.\n" "of the conversion process a bug is occurring.\n"
"Default: '%default'None\n" "Default: '%default'\n"
"Applies to: ePub, MOBI output formats")), "Applies to: ePub, MOBI output formats")),
Option('--exclude-book-marker', Option('--exclude-book-marker',
default=':', default=':',
@ -1370,7 +1370,9 @@ class EPUB_MOBI(CatalogPlugin):
self.generateHTMLByTags() self.generateHTMLByTags()
# 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 Genres found to catalog.\nCheck 'Excluded genres'\nin E-book options.\n") error_msg = _("No enabled genres found to catalog.\n")
if not self.opts.cli_environment:
error_msg += "Check 'Excluded genres'\nin 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)
@ -2792,14 +2794,16 @@ then rebuild the catalog.\n''').format(author[0],author[1],current_author[1])
genre_list.append(tag_list) genre_list.append(tag_list)
if self.opts.verbose: if self.opts.verbose:
self.opts.log.info(" Genre summary: %d active genre tags used in generating catalog with %d titles" % if len(genre_list):
self.opts.log.info(" Genre summary: %d active genre tags used in generating catalog with %d titles" %
(len(genre_list), len(self.booksByTitle))) (len(genre_list), len(self.booksByTitle)))
for genre in genre_list: for genre in genre_list:
for key in genre: for key in genre:
self.opts.log.info(" %s: %d %s" % (self.getFriendlyGenreTag(key), self.opts.log.info(" %s: %d %s" % (self.getFriendlyGenreTag(key),
len(genre[key]), len(genre[key]),
'titles' if len(genre[key]) > 1 else 'title')) 'titles' if len(genre[key]) > 1 else 'title'))
# Write the results # Write the results
# genre_list = [ {friendly_tag:[{book},{book}]}, {friendly_tag:[{book},{book}]}, ...] # genre_list = [ {friendly_tag:[{book},{book}]}, {friendly_tag:[{book},{book}]}, ...]
@ -3105,13 +3109,15 @@ then rebuild the catalog.\n''').format(author[0],author[1],current_author[1])
navPointTag.insert(1, contentTag) navPointTag.insert(1, contentTag)
elif self.opts.generate_genres: elif self.opts.generate_genres:
contentTag = Tag(soup, 'content') contentTag = Tag(soup, 'content')
contentTag['src'] = "content/ByGenres.html" #contentTag['src'] = "content/ByGenres.html"
contentTag['src'] = "%s" % self.genres[0]['file']
navPointTag.insert(1, contentTag) navPointTag.insert(1, contentTag)
elif self.opts.generate_recently_added: elif self.opts.generate_recently_added:
contentTag = Tag(soup, 'content') contentTag = Tag(soup, 'content')
contentTag['src'] = "content/ByDateAdded.html" contentTag['src'] = "content/ByDateAdded.html"
navPointTag.insert(1, contentTag) navPointTag.insert(1, contentTag)
else: else:
# Descriptions only
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
contentTag = Tag(soup, 'content') contentTag = Tag(soup, 'content')
@ -3125,7 +3131,6 @@ then rebuild the catalog.\n''').format(author[0],author[1],current_author[1])
navMapTag.insert(0,navPointTag) navMapTag.insert(0,navPointTag)
ncx.insert(0,navMapTag) ncx.insert(0,navMapTag)
self.ncxSoup = soup self.ncxSoup = soup
def generateNCXDescriptions(self, tocTitle): def generateNCXDescriptions(self, tocTitle):
@ -3911,7 +3916,6 @@ then rebuild the catalog.\n''').format(author[0],author[1],current_author[1])
# Add this section to the body # Add this section to the body
body.insert(btc, navPointTag) body.insert(btc, navPointTag)
btc += 1 btc += 1
self.ncxSoup = ncx_soup self.ncxSoup = ncx_soup
def writeNCX(self): def writeNCX(self):
@ -4055,12 +4059,34 @@ then rebuild the catalog.\n''').format(author[0],author[1],current_author[1])
# Remove the special marker tags from the database's tag list, # Remove the special marker tags from the database's tag list,
# return sorted list of normalized genre tags # return sorted list of normalized genre tags
def format_tag_list(tags, indent=5, line_break=70, header='Tag list'):
def next_tag(sorted_tags):
for (i, tag) in enumerate(sorted_tags):
if i < len(tags) - 1:
yield tag + ", "
else:
yield tag
ans = '%s%d %s:\n' % (' ' * indent, len(tags), header)
ans += ' ' * (indent + 1)
out_str = ''
sorted_tags = sorted(tags)
for tag in next_tag(sorted_tags):
out_str += tag
if len(out_str) >= line_break:
ans += out_str + '\n'
out_str = ' ' * (indent + 1)
return ans + out_str
normalized_tags = [] normalized_tags = []
friendly_tags = [] friendly_tags = []
excluded_tags = []
for tag in tags: for tag in tags:
if tag[0] in self.markerTags: if tag in self.markerTags:
excluded_tags.append(tag)
continue continue
if re.search(self.opts.exclude_genre, tag): if re.search(self.opts.exclude_genre, tag):
excluded_tags.append(tag)
continue continue
if tag == ' ': if tag == ' ':
continue continue
@ -4079,32 +4105,8 @@ then rebuild the catalog.\n''').format(author[0],author[1],current_author[1])
if genre_tags_dict[key] == normalized: if genre_tags_dict[key] == normalized:
self.opts.log.warn(" %s" % key) self.opts.log.warn(" %s" % key)
if self.verbose: if self.verbose:
def next_tag(tags): self.opts.log.info('%s' % format_tag_list(genre_tags_dict, header="enabled genre tags in database"))
for (i, tag) in enumerate(tags): self.opts.log.info('%s' % format_tag_list(excluded_tags, header="excluded genre tags"))
if i < len(tags) - 1:
yield tag + ", "
else:
yield tag
self.opts.log.info(u' %d genre tags in database (excluding genres matching %s):' % \
(len(genre_tags_dict), self.opts.exclude_genre))
# Display friendly/normalized genres
# friendly => normalized
if False:
sorted_tags = ['%s => %s' % (key, genre_tags_dict[key]) for key in sorted(genre_tags_dict.keys())]
for tag in next_tag(sorted_tags):
self.opts.log(u' %s' % tag)
else:
sorted_tags = ['%s' % (key) for key in sorted(genre_tags_dict.keys())]
out_str = ''
line_break = 70
for tag in next_tag(sorted_tags):
out_str += tag
if len(out_str) >= line_break:
self.opts.log.info(' %s' % out_str)
out_str = ''
self.opts.log.info(' %s' % out_str)
return genre_tags_dict return genre_tags_dict
@ -4969,7 +4971,13 @@ then rebuild the catalog.\n''').format(author[0],author[1],current_author[1])
else: else:
opts.log.warn('\n*** No enabled Sections, terminating catalog generation ***') opts.log.warn('\n*** No enabled Sections, terminating catalog generation ***')
return ["No Included Sections","No enabled Sections.\nCheck E-book options tab\n'Included sections'\n"] return ["No Included Sections","No enabled Sections.\nCheck E-book options tab\n'Included sections'\n"]
build_log.append(u" Sections: %s" % ', '.join(sections_list)) if opts.fmt == 'mobi' and sections_list == ['Descriptions']:
warning = _("\n*** Adding 'By Authors' Section required for MOBI output ***")
opts.log.warn(warning)
sections_list.insert(0,'Authors')
opts.generate_authors = True
opts.log(u" Sections: %s" % ', '.join(sections_list))
opts.section_list = sections_list opts.section_list = sections_list
# Limit thumb_width to 1.0" - 2.0" # Limit thumb_width to 1.0" - 2.0"
@ -5017,7 +5025,7 @@ then rebuild the catalog.\n''').format(author[0],author[1],current_author[1])
if catalog_source_built: if catalog_source_built:
log.info(" Completed catalog source generation\n") log.info(" Completed catalog source generation\n")
else: else:
log.warn(" *** Errors during catalog generation, check log for details ***") log.error(" *** Terminated catalog generation, check log for details ***")
if catalog_source_built: if catalog_source_built:
recommendations = [] recommendations = []

View File

@ -441,7 +441,7 @@ menu, choose "Validate fonts".
I downloaded the installer, but it is not working? I downloaded the installer, but it is not working?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Downloading from the internet can sometimes result in a corrupted download. If the |app| installer you downloaded is not opening, try downloading it again. If re-downloading it does not work, download it from `an alternate location <http://sourceforge.net/projects/calibre/files/>`_. If the installer still doesn't work, then something on your computer is preventing it from running. Best place to ask for more help is in the `forums <http://www.mobileread.com/forums/usercp.php>`_. Downloading from the internet can sometimes result in a corrupted download. If the |app| installer you downloaded is not opening, try downloading it again. If re-downloading it does not work, download it from `an alternate location <http://sourceforge.net/projects/calibre/files/>`_. If the installer still doesn't work, then something on your computer is preventing it from running. Try rebooting your computer and running a registry cleaner like `Wise registry cleaner <http://www.wisecleaner.com>`_. Best place to ask for more help is in the `forums <http://www.mobileread.com/forums/usercp.php>`_.
My antivirus program claims |app| is a virus/trojan? My antivirus program claims |app| is a virus/trojan?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -104,12 +104,12 @@ class cmd_commit(_cmd_commit):
def close_bug(self, bug, action, url, config): def close_bug(self, bug, action, url, config):
print 'Closing bug #%s'% bug print 'Closing bug #%s'% bug
nick = config.get_nickname() #nick = config.get_nickname()
suffix = config.get_user_option('bug_close_comment') suffix = config.get_user_option('bug_close_comment')
if suffix is None: if suffix is None:
suffix = 'The fix will be in the next release.' suffix = 'The fix will be in the next release.'
action = action+'ed' action = action+'ed'
msg = '%s in branch %s. %s'%(action, nick, suffix) msg = '%s in branch %s. %s'%(action, 'lp:calibre', suffix)
msg = msg.replace('Fixesed', 'Fixed') msg = msg.replace('Fixesed', 'Fixed')
server = xmlrpclib.ServerProxy(url) server = xmlrpclib.ServerProxy(url)
server.ticket.update(int(bug), msg, server.ticket.update(int(bug), msg,