Sync to trunk

This commit is contained in:
John Schember 2009-08-19 06:38:10 -04:00
commit 4d97671e26
12 changed files with 597 additions and 650 deletions

View File

@ -76,6 +76,15 @@ def _config():
'only take place when the Enter or Return key is pressed.') 'only take place when the Enter or Return key is pressed.')
c.add_opt('save_to_disk_template_history', default=[], c.add_opt('save_to_disk_template_history', default=[],
help='Previously used Save to Disk templates') help='Previously used Save to Disk templates')
c.add_opt('main_search_history', default=[],
help='Search history for the main GUI')
c.add_opt('viewer_search_history', default=[],
help='Search history for the ebook viewer')
c.add_opt('lrf_viewer_search_history', default=[],
help='Search history for the LRF viewer')
c.add_opt('scheduler_search_history', default=[],
help='Search history for the recipe scheduler')
return ConfigProxy(c) return ConfigProxy(c)
config = _config() config = _config()

View File

@ -156,7 +156,7 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
self.formats.takeItem(row.row()) self.formats.takeItem(row.row())
self.formats_changed = True self.formats_changed = True
def set_cover(self): def get_selected_format_metadata(self):
row = self.formats.currentRow() row = self.formats.currentRow()
fmt = self.formats.item(row) fmt = self.formats.item(row)
if fmt is None: if fmt is None:
@ -165,7 +165,7 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
if fmt is None: if fmt is None:
error_dialog(self, _('No format selected'), error_dialog(self, _('No format selected'),
_('No format selected')).exec_() _('No format selected')).exec_()
return return None, None
ext = fmt.ext.lower() ext = fmt.ext.lower()
if fmt.path is None: if fmt.path is None:
stream = self.db.format(self.row, ext, as_file=True) stream = self.db.format(self.row, ext, as_file=True)
@ -173,9 +173,44 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
stream = open(fmt.path, 'r+b') stream = open(fmt.path, 'r+b')
try: try:
mi = get_metadata(stream, ext) mi = get_metadata(stream, ext)
return mi, ext
except: except:
error_dialog(self, _('Could not read metadata'), error_dialog(self, _('Could not read metadata'),
_('Could not read metadata from %s format')%ext).exec_() _('Could not read metadata from %s format')%ext).exec_()
return None, None
def set_metadata_from_format(self):
mi, ext = self.get_selected_format_metadata()
if mi is None:
return
if mi.title:
self.title.setText(mi.title)
if mi.authors:
self.authors.setEditText(authors_to_string(mi.authors))
if mi.author_sort:
self.author_sort.setText(mi.author_sort)
if mi.rating is not None:
try:
self.rating.setValue(mi.rating)
except:
pass
if mi.publisher:
self.publisher.setEditText(mi.publisher)
if mi.tags:
self.tags.setText(', '.join(mi.tags))
if mi.isbn:
self.isbn.setText(mi.isbn)
if mi.pubdate:
self.pubdate.setDate(QDate(mi.pubdate.year, mi.pubdate.month,
mi.pubdate.day))
if mi.series:
self.series.setEditText(mi.series)
if mi.series_index is not None:
self.series_index.setValue(float(mi.series_index))
def set_cover(self):
mi, ext = self.get_selected_format_metadata()
if mi is None:
return return
cdata = None cdata = None
if mi.cover and os.access(mi.cover, os.R_OK): if mi.cover and os.access(mi.cover, os.R_OK):
@ -253,6 +288,8 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
self.connect(self.formats, SIGNAL('itemDoubleClicked(QListWidgetItem*)'), self.connect(self.formats, SIGNAL('itemDoubleClicked(QListWidgetItem*)'),
self.show_format) self.show_format)
self.connect(self.button_set_cover, SIGNAL('clicked()'), self.set_cover) self.connect(self.button_set_cover, SIGNAL('clicked()'), self.set_cover)
self.connect(self.button_set_metadata, SIGNAL('clicked()'),
self.set_metadata_from_format)
self.connect(self.reset_cover, SIGNAL('clicked()'), self.do_reset_cover) self.connect(self.reset_cover, SIGNAL('clicked()'), self.do_reset_cover)
self.connect(self.swap_button, SIGNAL('clicked()'), self.swap_title_author) self.connect(self.swap_button, SIGNAL('clicked()'), self.swap_title_author)
self.timeout = float(prefs['network_timeout']) self.timeout = float(prefs['network_timeout'])

View File

@ -44,7 +44,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>879</width> <width>879</width>
<height>710</height> <height>711</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_5"> <layout class="QVBoxLayout" name="verticalLayout_5">
@ -415,7 +415,7 @@
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<item> <item>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" rowspan="3"> <item row="0" column="1" rowspan="3">
<widget class="QListWidget" name="formats"> <widget class="QListWidget" name="formats">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum"> <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
@ -437,7 +437,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="1"> <item row="0" column="2">
<widget class="QToolButton" name="add_format_button"> <widget class="QToolButton" name="add_format_button">
<property name="toolTip"> <property name="toolTip">
<string>Add a new format for this book to the database</string> <string>Add a new format for this book to the database</string>
@ -457,7 +457,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="1"> <item row="2" column="2">
<widget class="QToolButton" name="remove_format_button"> <widget class="QToolButton" name="remove_format_button">
<property name="toolTip"> <property name="toolTip">
<string>Remove the selected formats for this book from the database.</string> <string>Remove the selected formats for this book from the database.</string>
@ -477,7 +477,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="1"> <item row="0" column="0">
<widget class="QToolButton" name="button_set_cover"> <widget class="QToolButton" name="button_set_cover">
<property name="toolTip"> <property name="toolTip">
<string>Set the cover for the book from the selected format</string> <string>Set the cover for the book from the selected format</string>
@ -497,6 +497,26 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="0">
<widget class="QToolButton" name="button_set_metadata">
<property name="toolTip">
<string>Update metadata from the metadata in the selected format</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../images.qrc">
<normaloff>:/images/edit_input.svg</normaloff>:/images/edit_input.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
</widget>
</item>
</layout> </layout>
</item> </item>
</layout> </layout>
@ -680,7 +700,6 @@
<tabstop>fetch_metadata_button</tabstop> <tabstop>fetch_metadata_button</tabstop>
<tabstop>formats</tabstop> <tabstop>formats</tabstop>
<tabstop>add_format_button</tabstop> <tabstop>add_format_button</tabstop>
<tabstop>button_set_cover</tabstop>
<tabstop>remove_format_button</tabstop> <tabstop>remove_format_button</tabstop>
<tabstop>cover_path</tabstop> <tabstop>cover_path</tabstop>
<tabstop>cover_button</tabstop> <tabstop>cover_button</tabstop>

View File

@ -9,12 +9,13 @@ Scheduler for automated recipe downloads
import sys, copy, time import sys, copy, time
from datetime import datetime, timedelta, date from datetime import datetime, timedelta, date
from PyQt4.Qt import QDialog, QApplication, QLineEdit, QPalette, SIGNAL, QBrush, \ from PyQt4.Qt import QDialog, QApplication, SIGNAL, \
QColor, QAbstractItemModel, Qt, QVariant, QFont, QIcon, \ QColor, QAbstractItemModel, Qt, QVariant, QFont, QIcon, \
QFile, QObject, QTimer, QMutex, QMenu, QAction, QTime, QModelIndex QFile, QObject, QTimer, QMutex, QMenu, QAction, QTime, QModelIndex
from calibre import english_sort from calibre import english_sort
from calibre.gui2.dialogs.scheduler_ui import Ui_Dialog from calibre.gui2.dialogs.scheduler_ui import Ui_Dialog
from calibre.gui2.search_box import SearchBox2
from calibre.web.feeds.recipes import recipes, recipe_modules, compile_recipe from calibre.web.feeds.recipes import recipes, recipe_modules, compile_recipe
from calibre.utils.search_query_parser import SearchQueryParser from calibre.utils.search_query_parser import SearchQueryParser
from calibre.utils.pyparsing import ParseException from calibre.utils.pyparsing import ParseException
@ -163,7 +164,7 @@ class RecipeModel(QAbstractItemModel, SearchQueryParser):
results.add(recipe) results.add(recipe)
return results return results
def search(self, query): def search(self, query, refinement):
try: try:
results = self.parse(unicode(query)) results = self.parse(unicode(query))
except ParseException: except ParseException:
@ -176,6 +177,7 @@ class RecipeModel(QAbstractItemModel, SearchQueryParser):
if recipe in results: if recipe in results:
self._map[category].append(recipe) self._map[category].append(recipe)
self.reset() self.reset()
self.emit(SIGNAL('searched(PyQt_PyObject)'), True)
def resort(self): def resort(self):
self.recipes.sort() self.recipes.sort()
@ -235,45 +237,6 @@ class RecipeModel(QAbstractItemModel, SearchQueryParser):
srecipe.schedule = recipe.schedule srecipe.schedule = recipe.schedule
class Search(QLineEdit):
HELP_TEXT = _('Search')
INTERVAL = 500 #: Time to wait before emitting search signal
def __init__(self, *args):
QLineEdit.__init__(self, *args)
self.default_palette = QApplication.palette(self)
self.gray = QPalette(self.default_palette)
self.gray.setBrush(QPalette.Text, QBrush(QColor('gray')))
self.connect(self, SIGNAL('editingFinished()'),
lambda : self.emit(SIGNAL('goto(PyQt_PyObject)'), unicode(self.text())))
self.clear_to_help_mode()
self.timer = None
self.connect(self, SIGNAL('textEdited(QString)'), self.text_edited_slot)
def focusInEvent(self, ev):
self.setPalette(QApplication.palette(self))
if self.in_help_mode():
self.setText('')
return QLineEdit.focusInEvent(self, ev)
def in_help_mode(self):
return unicode(self.text()) == self.HELP_TEXT
def clear_to_help_mode(self):
self.setPalette(self.gray)
self.setText(self.HELP_TEXT)
def text_edited_slot(self, text):
text = unicode(text)
self.timer = self.startTimer(self.INTERVAL)
def timerEvent(self, event):
self.killTimer(event.timerId())
if event.timerId() == self.timer:
text = unicode(self.text())
self.emit(SIGNAL('search(PyQt_PyObject)'), text)
def encode_schedule(day, hour, minute): def encode_schedule(day, hour, minute):
day = 1e7 * (day+1) day = 1e7 * (day+1)
hour = 1e4 * (hour+1) hour = 1e4 * (hour+1)
@ -291,7 +254,8 @@ class SchedulerDialog(QDialog, Ui_Dialog):
def __init__(self, db, *args): def __init__(self, db, *args):
QDialog.__init__(self, *args) QDialog.__init__(self, *args)
self.setupUi(self) self.setupUi(self)
self.search = Search(self) self.search = SearchBox2(self)
self.search.initialize('scheduler_search_history')
self.recipe_box.layout().insertWidget(0, self.search) self.recipe_box.layout().insertWidget(0, self.search)
self.detail_box.setVisible(False) self.detail_box.setVisible(False)
self._model = RecipeModel(db) self._model = RecipeModel(db)
@ -308,7 +272,9 @@ class SchedulerDialog(QDialog, Ui_Dialog):
self.connect(self.time, SIGNAL('timeChanged(QTime)'), self.do_schedule) self.connect(self.time, SIGNAL('timeChanged(QTime)'), self.do_schedule)
for button in (self.daily_button, self.interval_button): for button in (self.daily_button, self.interval_button):
self.connect(button, SIGNAL('toggled(bool)'), self.do_schedule) self.connect(button, SIGNAL('toggled(bool)'), self.do_schedule)
self.connect(self.search, SIGNAL('search(PyQt_PyObject)'), self._model.search) self.connect(self.search, SIGNAL('search(PyQt_PyObject,PyQt_PyObject)'), self._model.search)
self.connect(self._model, SIGNAL('searched(PyQt_PyObject)'),
self.search.search_done)
self.connect(self._model, SIGNAL('modelReset()'), lambda : self.detail_box.setVisible(False)) self.connect(self._model, SIGNAL('modelReset()'), lambda : self.detail_box.setVisible(False))
self.connect(self.download_all_button, SIGNAL('clicked()'), self.connect(self.download_all_button, SIGNAL('clicked()'),
self.download_all) self.download_all)

View File

@ -8,10 +8,10 @@ from operator import attrgetter
from math import cos, sin, pi from math import cos, sin, pi
from PyQt4.QtGui import QTableView, QAbstractItemView, QColor, \ from PyQt4.QtGui import QTableView, QAbstractItemView, QColor, \
QItemDelegate, QPainterPath, QLinearGradient, QBrush, \ QItemDelegate, QPainterPath, QLinearGradient, QBrush, \
QPen, QStyle, QPainter, QLineEdit, \ QPen, QStyle, QPainter, \
QPalette, QImage, QApplication, QMenu, \ QImage, QApplication, QMenu, \
QStyledItemDelegate, QCompleter QStyledItemDelegate, QCompleter
from PyQt4.QtCore import QAbstractTableModel, QVariant, Qt, QString, \ from PyQt4.QtCore import QAbstractTableModel, QVariant, Qt, \
SIGNAL, QObject, QSize, QModelIndex, QDate SIGNAL, QObject, QSize, QModelIndex, QDate
from calibre import strftime from calibre import strftime
@ -1100,94 +1100,4 @@ class DeviceBooksModel(BooksModel):
self.editable = editable self.editable = editable
class SearchBox(QLineEdit):
INTERVAL = 1000 #: Time to wait before emitting search signal
def __init__(self, parent, help_text=_('Search (For Advanced Search click the button to the left)')):
QLineEdit.__init__(self, parent)
self.help_text = help_text
self.initial_state = True
self.as_you_type = True
self.default_palette = QApplication.palette(self)
self.gray = QPalette(self.default_palette)
self.gray.setBrush(QPalette.Text, QBrush(QColor('gray')))
self.prev_search = ''
self.timer = None
self.clear_to_help()
QObject.connect(self, SIGNAL('textEdited(QString)'), self.text_edited_slot)
def normalize_state(self):
self.setText('')
self.setPalette(self.default_palette)
self.setStyleSheet('QLineEdit { background-color: white; }')
def clear_to_help(self):
self.setPalette(self.gray)
self.setText(self.help_text)
self.home(False)
self.initial_state = True
self.setStyleSheet('QLineEdit { background-color: white; }')
self.emit(SIGNAL('cleared()'))
def clear(self):
self.clear_to_help()
self.emit(SIGNAL('search(PyQt_PyObject, PyQt_PyObject)'), '', False)
def search_done(self, ok):
col = 'rgba(0,255,0,20%)' if ok else 'rgb(255,0,0,20%)'
self.setStyleSheet('QLineEdit { background-color: %s; }' % col)
def keyPressEvent(self, event):
if self.initial_state:
self.normalize_state()
self.initial_state = False
if not self.as_you_type:
if event.key() in (Qt.Key_Return, Qt.Key_Enter):
self.do_search()
QLineEdit.keyPressEvent(self, event)
def mouseReleaseEvent(self, event):
if self.initial_state:
self.normalize_state()
self.initial_state = False
QLineEdit.mouseReleaseEvent(self, event)
def text_edited_slot(self, text):
if self.as_you_type:
text = qstring_to_unicode(text) if isinstance(text, QString) else unicode(text)
self.prev_text = text
self.timer = self.startTimer(self.__class__.INTERVAL)
def timerEvent(self, event):
self.killTimer(event.timerId())
if event.timerId() == self.timer:
self.do_search()
def do_search(self):
text = qstring_to_unicode(self.text())
refinement = text.startswith(self.prev_search) and ':' not in text
self.prev_search = text
self.emit(SIGNAL('search(PyQt_PyObject, PyQt_PyObject)'), text, refinement)
def search_from_tokens(self, tokens, all):
ans = u' '.join([u'%s:%s'%x for x in tokens])
if not all:
ans = '[' + ans + ']'
self.set_search_string(ans)
def search_from_tags(self, tags, all):
joiner = ' and ' if all else ' or '
self.set_search_string(joiner.join(tags))
def set_search_string(self, txt):
self.normalize_state()
self.setText(txt)
self.emit(SIGNAL('search(PyQt_PyObject, PyQt_PyObject)'), txt, False)
self.end(False)
self.initial_state = False
def search_as_you_type(self, enabled):
self.as_you_type = enabled

View File

@ -15,10 +15,10 @@ from calibre.gui2.lrf_renderer.main_ui import Ui_MainWindow
from calibre.gui2.lrf_renderer.config_ui import Ui_ViewerConfig from calibre.gui2.lrf_renderer.config_ui import Ui_ViewerConfig
from calibre.gui2.main_window import MainWindow from calibre.gui2.main_window import MainWindow
from calibre.gui2.lrf_renderer.document import Document from calibre.gui2.lrf_renderer.document import Document
from calibre.gui2.library import SearchBox from calibre.gui2.search_box import SearchBox2
class RenderWorker(QThread): class RenderWorker(QThread):
def __init__(self, parent, lrf_stream, logger, opts): def __init__(self, parent, lrf_stream, logger, opts):
QThread.__init__(self, parent) QThread.__init__(self, parent)
self.stream, self.logger, self.opts = lrf_stream, logger, opts self.stream, self.logger, self.opts = lrf_stream, logger, opts
@ -26,7 +26,7 @@ class RenderWorker(QThread):
self.lrf = None self.lrf = None
self.document = None self.document = None
self.exception = None self.exception = None
def run(self): def run(self):
try: try:
self.lrf = LRFDocument(self.stream) self.lrf = LRFDocument(self.stream)
@ -34,35 +34,35 @@ class RenderWorker(QThread):
self.stream.close() self.stream.close()
self.stream = None self.stream = None
if self.aborted: if self.aborted:
self.lrf = None self.lrf = None
except Exception, err: except Exception, err:
self.lrf, self.stream = None, None self.lrf, self.stream = None, None
self.exception = err self.exception = err
self.formatted_traceback = traceback.format_exc() self.formatted_traceback = traceback.format_exc()
def abort(self): def abort(self):
if self.lrf is not None: if self.lrf is not None:
self.aborted = True self.aborted = True
self.lrf.keep_parsing = False self.lrf.keep_parsing = False
class Config(QDialog, Ui_ViewerConfig): class Config(QDialog, Ui_ViewerConfig):
def __init__(self, parent, opts): def __init__(self, parent, opts):
QDialog.__init__(self, parent) QDialog.__init__(self, parent)
Ui_ViewerConfig.__init__(self) Ui_ViewerConfig.__init__(self)
self.setupUi(self) self.setupUi(self)
self.white_background.setChecked(opts.white_background) self.white_background.setChecked(opts.white_background)
self.hyphenate.setChecked(opts.hyphenate) self.hyphenate.setChecked(opts.hyphenate)
class Main(MainWindow, Ui_MainWindow): class Main(MainWindow, Ui_MainWindow):
def __init__(self, logger, opts, parent=None): def __init__(self, logger, opts, parent=None):
MainWindow.__init__(self, opts, parent) MainWindow.__init__(self, opts, parent)
Ui_MainWindow.__init__(self) Ui_MainWindow.__init__(self)
self.setupUi(self) self.setupUi(self)
self.setAttribute(Qt.WA_DeleteOnClose) self.setAttribute(Qt.WA_DeleteOnClose)
self.setWindowTitle(__appname__ + _(' - LRF Viewer')) self.setWindowTitle(__appname__ + _(' - LRF Viewer'))
self.logger = logger self.logger = logger
self.opts = opts self.opts = opts
self.document = None self.document = None
@ -73,18 +73,19 @@ class Main(MainWindow, Ui_MainWindow):
self.slider_action = self.slider = QSlider(Qt.Horizontal) self.slider_action = self.slider = QSlider(Qt.Horizontal)
self.tool_bar.addWidget(self.slider) self.tool_bar.addWidget(self.slider)
self.tool_bar.addSeparator() self.tool_bar.addSeparator()
self.search = SearchBox(self) self.search = SearchBox2(self)
self.search.initialize('lrf_viewer_search_history')
self.search_action = self.tool_bar.addWidget(self.search) self.search_action = self.tool_bar.addWidget(self.search)
QObject.connect(self.document, SIGNAL('chapter_rendered(int)'), self.chapter_rendered) QObject.connect(self.document, SIGNAL('chapter_rendered(int)'), self.chapter_rendered)
QObject.connect(self.document, SIGNAL('page_changed(PyQt_PyObject)'), self.page_changed) QObject.connect(self.document, SIGNAL('page_changed(PyQt_PyObject)'), self.page_changed)
QObject.connect(self.search, SIGNAL('search(PyQt_PyObject, PyQt_PyObject)'), self.find) QObject.connect(self.search, SIGNAL('search(PyQt_PyObject, PyQt_PyObject)'), self.find)
self.action_next_page.setShortcuts([QKeySequence.MoveToNextPage, QKeySequence(Qt.Key_Space)]) self.action_next_page.setShortcuts([QKeySequence.MoveToNextPage, QKeySequence(Qt.Key_Space)])
self.action_previous_page.setShortcuts([QKeySequence.MoveToPreviousPage, QKeySequence(Qt.Key_Backspace)]) self.action_previous_page.setShortcuts([QKeySequence.MoveToPreviousPage, QKeySequence(Qt.Key_Backspace)])
self.action_next_match.setShortcuts(QKeySequence.FindNext) self.action_next_match.setShortcuts(QKeySequence.FindNext)
self.addAction(self.action_next_match) self.addAction(self.action_next_match)
QObject.connect(self.action_next_page, SIGNAL('triggered(bool)'), self.next) QObject.connect(self.action_next_page, SIGNAL('triggered(bool)'), self.next)
QObject.connect(self.action_previous_page, SIGNAL('triggered(bool)'), self.previous) QObject.connect(self.action_previous_page, SIGNAL('triggered(bool)'), self.previous)
QObject.connect(self.action_back, SIGNAL('triggered(bool)'), self.back) QObject.connect(self.action_back, SIGNAL('triggered(bool)'), self.back)
QObject.connect(self.action_forward, SIGNAL('triggered(bool)'), self.forward) QObject.connect(self.action_forward, SIGNAL('triggered(bool)'), self.forward)
@ -93,15 +94,15 @@ class Main(MainWindow, Ui_MainWindow):
QObject.connect(self.action_configure, SIGNAL('triggered(bool)'), self.configure) QObject.connect(self.action_configure, SIGNAL('triggered(bool)'), self.configure)
QObject.connect(self.spin_box, SIGNAL('valueChanged(int)'), self.go_to_page) QObject.connect(self.spin_box, SIGNAL('valueChanged(int)'), self.go_to_page)
QObject.connect(self.slider, SIGNAL('valueChanged(int)'), self.go_to_page) QObject.connect(self.slider, SIGNAL('valueChanged(int)'), self.go_to_page)
self.graphics_view.setRenderHint(QPainter.Antialiasing, True) self.graphics_view.setRenderHint(QPainter.Antialiasing, True)
self.graphics_view.setRenderHint(QPainter.TextAntialiasing, True) self.graphics_view.setRenderHint(QPainter.TextAntialiasing, True)
self.graphics_view.setRenderHint(QPainter.SmoothPixmapTransform, True) self.graphics_view.setRenderHint(QPainter.SmoothPixmapTransform, True)
self.closed = False self.closed = False
def configure(self, triggered): def configure(self, triggered):
opts = config['LRF_ebook_viewer_options'] opts = config['LRF_ebook_viewer_options']
if not opts: if not opts:
@ -112,65 +113,65 @@ class Main(MainWindow, Ui_MainWindow):
opts.white_background = bool(d.white_background.isChecked()) opts.white_background = bool(d.white_background.isChecked())
opts.hyphenate = bool(d.hyphenate.isChecked()) opts.hyphenate = bool(d.hyphenate.isChecked())
config['LRF_ebook_viewer_options'] = opts config['LRF_ebook_viewer_options'] = opts
def set_ebook(self, stream): def set_ebook(self, stream):
self.progress_bar.setMinimum(0) self.progress_bar.setMinimum(0)
self.progress_bar.setMaximum(0) self.progress_bar.setMaximum(0)
self.progress_bar.setValue(0) self.progress_bar.setValue(0)
if stream is not None: if stream is not None:
self.file_name = os.path.basename(stream.name) if hasattr(stream, 'name') else '' self.file_name = os.path.basename(stream.name) if hasattr(stream, 'name') else ''
self.progress_label.setText('Parsing '+ self.file_name) self.progress_label.setText('Parsing '+ self.file_name)
self.renderer = RenderWorker(self, stream, self.logger, self.opts) self.renderer = RenderWorker(self, stream, self.logger, self.opts)
QObject.connect(self.renderer, SIGNAL('finished()'), self.parsed, Qt.QueuedConnection) QObject.connect(self.renderer, SIGNAL('finished()'), self.parsed, Qt.QueuedConnection)
self.search.help_text = 'Search'
self.search.clear_to_help() self.search.clear_to_help()
self.last_search = None self.last_search = None
else: else:
self.stack.setCurrentIndex(0) self.stack.setCurrentIndex(0)
self.renderer = None self.renderer = None
def open_ebook(self, triggered): def open_ebook(self, triggered):
files = choose_files(self, 'open ebook dialog', 'Choose ebook', files = choose_files(self, 'open ebook dialog', 'Choose ebook',
[('Ebooks', ['lrf'])], all_files=False, [('Ebooks', ['lrf'])], all_files=False,
select_only_single_file=True) select_only_single_file=True)
if files: if files:
file = files[0] file = files[0]
self.set_ebook(open(file, 'rb')) self.set_ebook(open(file, 'rb'))
self.render() self.render()
def page_changed(self, num): def page_changed(self, num):
self.slider.setValue(num) self.slider.setValue(num)
self.spin_box.setValue(num) self.spin_box.setValue(num)
def render(self): def render(self):
if self.renderer is not None: if self.renderer is not None:
self.stack.setCurrentIndex(1) self.stack.setCurrentIndex(1)
self.renderer.start() self.renderer.start()
def find(self, search, refinement): def find(self, search, refinement):
self.last_search = search self.last_search = search
try: try:
self.document.search(search) self.document.search(search)
except StopIteration: except StopIteration:
error_dialog(self, _('No matches found'), _('<b>No matches</b> for the search phrase <i>%s</i> were found.')%(search,)).exec_() error_dialog(self, _('No matches found'), _('<b>No matches</b> for the search phrase <i>%s</i> were found.')%(search,)).exec_()
self.search.search_done(True)
def parsed(self): def parsed(self):
if not self.renderer.aborted and self.renderer.lrf is not None: if not self.renderer.aborted and self.renderer.lrf is not None:
width, height = self.renderer.lrf.device_info.width, \ width, height = self.renderer.lrf.device_info.width, \
self.renderer.lrf.device_info.height self.renderer.lrf.device_info.height
hdelta = self.tool_bar.height()+3 hdelta = self.tool_bar.height()+3
from PyQt4.QtGui import QScrollBar from PyQt4.QtGui import QScrollBar
s = QScrollBar(self) s = QScrollBar(self)
scrollbar_adjust = min(s.width(), s.height()) scrollbar_adjust = min(s.width(), s.height())
self.graphics_view.resize_for(width+scrollbar_adjust, height+scrollbar_adjust) self.graphics_view.resize_for(width+scrollbar_adjust, height+scrollbar_adjust)
desktop = QCoreApplication.instance().desktop() desktop = QCoreApplication.instance().desktop()
screen_height = desktop.availableGeometry(self).height() - 25 screen_height = desktop.availableGeometry(self).height() - 25
height = min(screen_height, height+hdelta+scrollbar_adjust) height = min(screen_height, height+hdelta+scrollbar_adjust)
self.resize(width+scrollbar_adjust, height) self.resize(width+scrollbar_adjust, height)
self.setWindowTitle(self.renderer.lrf.metadata.title + ' - ' + __appname__) self.setWindowTitle(self.renderer.lrf.metadata.title + ' - ' + __appname__)
self.document_title = self.renderer.lrf.metadata.title self.document_title = self.renderer.lrf.metadata.title
if self.opts.profile: if self.opts.profile:
@ -183,7 +184,7 @@ class Main(MainWindow, Ui_MainWindow):
self.document.render(self.renderer.lrf) self.document.render(self.renderer.lrf)
print 'Layout time:', time.time()-start, 'seconds' print 'Layout time:', time.time()-start, 'seconds'
self.renderer.lrf = None self.renderer.lrf = None
self.graphics_view.setScene(self.document) self.graphics_view.setScene(self.document)
self.graphics_view.show() self.graphics_view.show()
self.spin_box.setRange(1, self.document.num_of_pages) self.spin_box.setRange(1, self.document.num_of_pages)
@ -200,10 +201,10 @@ class Main(MainWindow, Ui_MainWindow):
msg = u'<p><b>%s</b>: '%(exception.__class__.__name__,) + unicode(str(exception), 'utf8', 'replace') + u'</p>' msg = u'<p><b>%s</b>: '%(exception.__class__.__name__,) + unicode(str(exception), 'utf8', 'replace') + u'</p>'
msg += u'<p>Failed to render document</p>' msg += u'<p>Failed to render document</p>'
msg += u'<p>Detailed <b>traceback</b>:<pre>' msg += u'<p>Detailed <b>traceback</b>:<pre>'
msg += self.renderer.formatted_traceback + '</pre>' msg += self.renderer.formatted_traceback + '</pre>'
d = ConversionErrorDialog(self, 'Error while rendering file', msg) d = ConversionErrorDialog(self, 'Error while rendering file', msg)
d.exec_() d.exec_()
def chapter_rendered(self, num): def chapter_rendered(self, num):
if num > 0: if num > 0:
self.progress_bar.setMinimum(0) self.progress_bar.setMinimum(0)
@ -213,7 +214,7 @@ class Main(MainWindow, Ui_MainWindow):
else: else:
self.progress_bar.setValue(self.progress_bar.value()+1) self.progress_bar.setValue(self.progress_bar.value()+1)
QCoreApplication.processEvents() QCoreApplication.processEvents()
def next(self, triggered): def next(self, triggered):
self.document.next() self.document.next()
@ -222,19 +223,19 @@ class Main(MainWindow, Ui_MainWindow):
self.document.next_match() self.document.next_match()
except StopIteration: except StopIteration:
pass pass
def previous(self, triggered): def previous(self, triggered):
self.document.previous() self.document.previous()
def go_to_page(self, num): def go_to_page(self, num):
self.document.show_page(num) self.document.show_page(num)
def forward(self, triggered): def forward(self, triggered):
self.document.forward() self.document.forward()
def back(self, triggered): def back(self, triggered):
self.document.back() self.document.back()
def wheelEvent(self, ev): def wheelEvent(self, ev):
if ev.delta() >= 0: if ev.delta() >= 0:
self.document.previous() self.document.previous()
@ -263,7 +264,7 @@ def file_renderer(stream, opts, parent=None, logger=None):
m = Main(logger, opts, parent=parent) m = Main(logger, opts, parent=parent)
m.set_ebook(stream) m.set_ebook(stream)
return m return m
def option_parser(): def option_parser():
from calibre.gui2.main_window import option_parser from calibre.gui2.main_window import option_parser
@ -295,7 +296,7 @@ def normalize_settings(parser, opts):
continue continue
setattr(saved_opts, opt.dest, getattr(opts, opt.dest)) setattr(saved_opts, opt.dest, getattr(opts, opt.dest))
return saved_opts return saved_opts
def main(args=sys.argv, logger=None): def main(args=sys.argv, logger=None):
parser = option_parser() parser = option_parser()
@ -310,17 +311,17 @@ def main(args=sys.argv, logger=None):
QCoreApplication.setOrganizationName(ORG_NAME) QCoreApplication.setOrganizationName(ORG_NAME)
QCoreApplication.setApplicationName(APP_UID) QCoreApplication.setApplicationName(APP_UID)
opts = normalize_settings(parser, opts) opts = normalize_settings(parser, opts)
stream = open(args[1], 'rb') if len(args) > 1 else None stream = open(args[1], 'rb') if len(args) > 1 else None
main = file_renderer(stream, opts, logger=logger) main = file_renderer(stream, opts, logger=logger)
sys.excepthook = main.unhandled_exception sys.excepthook = main.unhandled_exception
main.show() main.show()
main.render() main.render()
main.activateWindow() main.activateWindow()
main.raise_() main.raise_()
return app.exec_() return app.exec_()
return 0 return 0
if __name__ == '__main__': if __name__ == '__main__':
sys.exit(main()) sys.exit(main())

View File

@ -121,6 +121,9 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
Ui_MainWindow.__init__(self) Ui_MainWindow.__init__(self)
self.setupUi(self) self.setupUi(self)
self.setWindowTitle(__appname__) self.setWindowTitle(__appname__)
self.search.initialize('main_search_history', colorize=True,
help_text=_('Search (For Advanced Search click the button to the left)'))
self.connect(self.clear_button, SIGNAL('clicked()'), self.search.clear)
self.progress_indicator = ProgressIndicator(self) self.progress_indicator = ProgressIndicator(self)
self.verbose = opts.verbose self.verbose = opts.verbose
self.get_metadata = GetMetadata() self.get_metadata = GetMetadata()
@ -148,7 +151,6 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
self.system_tray_icon.hide() self.system_tray_icon.hide()
else: else:
self.system_tray_icon.show() self.system_tray_icon.show()
self.search.search_as_you_type(config['search_as_you_type'])
self.system_tray_menu = QMenu(self) self.system_tray_menu = QMenu(self)
self.restore_action = self.system_tray_menu.addAction( self.restore_action = self.system_tray_menu.addAction(
QIcon(':/images/page.svg'), _('&Restore')) QIcon(':/images/page.svg'), _('&Restore'))

View File

@ -185,33 +185,18 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="SearchBox" name="search"> <widget class="SearchBox2" name="search">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed"> <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>1</horstretch> <horstretch>0</horstretch>
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="acceptDrops">
<bool>false</bool>
</property>
<property name="toolTip"> <property name="toolTip">
<string>Search the list of books by title or author&lt;br&gt;&lt;br&gt;Words separated by spaces are ANDed</string> <string>&lt;p&gt;Search the list of books by title, author, publisher, tags, comments, etc.&lt;br&gt;&lt;br&gt;Words separated by spaces are ANDed</string>
</property> </property>
<property name="whatsThis"> <property name="whatsThis">
<string>Search the list of books by title, author, publisher, tags and comments&lt;br&gt;&lt;br&gt;Words separated by spaces are ANDed</string> <string>&lt;p&gt;Search the list of books by title, author, publisher, tags, comments, etc.&lt;br&gt;&lt;br&gt;Words separated by spaces are ANDed</string>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="text">
<string/>
</property>
<property name="frame">
<bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
@ -673,11 +658,6 @@
</action> </action>
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget>
<class>SearchBox</class>
<extends>QLineEdit</extends>
<header>library.h</header>
</customwidget>
<customwidget> <customwidget>
<class>BooksView</class> <class>BooksView</class>
<extends>QTableView</extends> <extends>QTableView</extends>
@ -698,26 +678,14 @@
<extends>QTreeView</extends> <extends>QTreeView</extends>
<header>calibre/gui2/tag_view.h</header> <header>calibre/gui2/tag_view.h</header>
</customwidget> </customwidget>
<customwidget>
<class>SearchBox2</class>
<extends>QComboBox</extends>
<header>calibre.gui2.search_box</header>
</customwidget>
</customwidgets> </customwidgets>
<resources> <resources>
<include location="images.qrc"/> <include location="images.qrc"/>
</resources> </resources>
<connections> <connections/>
<connection>
<sender>clear_button</sender>
<signal>clicked()</signal>
<receiver>search</receiver>
<slot>clear()</slot>
<hints>
<hint type="sourcelabel">
<x>787</x>
<y>215</y>
</hint>
<hint type="destinationlabel">
<x>755</x>
<y>213</y>
</hint>
</hints>
</connection>
</connections>
</ui> </ui>

View File

@ -0,0 +1,157 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
from __future__ import with_statement
__license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
from PyQt4.Qt import QComboBox, SIGNAL, Qt, QLineEdit, QStringList
from calibre.gui2 import config
class SearchLineEdit(QLineEdit):
def keyPressEvent(self, event):
self.emit(SIGNAL('key_pressed(PyQt_PyObject)'), event)
QLineEdit.keyPressEvent(self, event)
def mouseReleaseEvent(self, event):
self.emit(SIGNAL('mouse_released(PyQt_PyObject)'), event)
QLineEdit.mouseReleaseEvent(self, event)
class SearchBox2(QComboBox):
'''
To use this class:
* Call initialize()
* Connect to the search() and cleared() signals from this widget
* Call search_done() after evry search is complete
* Use clear() to clear back to the help message
'''
INTERVAL = 1500 #: Time to wait before emitting search signal
MAX_COUNT = 25
def __init__(self, parent=None):
QComboBox.__init__(self, parent)
self.line_edit = SearchLineEdit(self)
self.setLineEdit(self.line_edit)
self.connect(self.line_edit, SIGNAL('key_pressed(PyQt_PyObject)'),
self.key_pressed, Qt.DirectConnection)
self.connect(self.line_edit, SIGNAL('mouse_released(PyQt_PyObject)'),
self.key_pressed, Qt.DirectConnection)
self.setEditable(True)
self.help_state = True
self.as_you_type = True
self.prev_search = ''
self.timer = None
self.setInsertPolicy(self.NoInsert)
self.setMaxCount(self.MAX_COUNT)
def initialize(self, opt_name, colorize=False,
help_text=_('Search')):
self.as_you_type = config['search_as_you_type']
self.opt_name = opt_name
self.addItems(QStringList(list(set(config[opt_name]))))
self.help_text = help_text
self.colorize = colorize
self.clear_to_help()
self.connect(self, SIGNAL('editTextChanged(QString)'), self.text_edited_slot)
def normalize_state(self):
self.setEditText('')
self.line_edit.setStyleSheet('QLineEdit { color: black; background-color: white; }')
self.help_state = False
def clear_to_help(self):
self.setEditText(self.help_text)
self.line_edit.home(False)
self.help_state = True
self.line_edit.setStyleSheet('QLineEdit { color: gray; background-color: white; }')
self.emit(SIGNAL('cleared()'))
def text(self):
return self.currentText()
def clear(self):
self.clear_to_help()
self.emit(SIGNAL('search(PyQt_PyObject, PyQt_PyObject)'), '', False)
def search_done(self, ok):
if not unicode(self.currentText()).strip():
return self.clear_to_help()
col = 'rgba(0,255,0,20%)' if ok else 'rgb(255,0,0,20%)'
if not self.colorize:
col = 'white'
self.line_edit.setStyleSheet('QLineEdit { color: black; background-color: %s; }' % col)
def key_pressed(self, event):
if self.help_state:
self.normalize_state()
if not self.as_you_type:
if event.key() in (Qt.Key_Return, Qt.Key_Enter):
self.do_search()
def mouse_released(self, event):
if self.help_state:
self.normalize_state()
def text_edited_slot(self, text):
if self.as_you_type:
text = unicode(text)
self.prev_text = text
self.timer = self.startTimer(self.__class__.INTERVAL)
def timerEvent(self, event):
self.killTimer(event.timerId())
if event.timerId() == self.timer:
self.do_search()
def do_search(self):
text = unicode(self.currentText()).strip()
if not text or text == self.help_text:
return self.clear()
self.help_state = False
refinement = text.startswith(self.prev_search) and ':' not in text
self.prev_search = text
self.emit(SIGNAL('search(PyQt_PyObject, PyQt_PyObject)'), text, refinement)
idx = self.findText(text, Qt.MatchFixedString)
self.block_signals(True)
if idx < 0:
self.insertItem(0, text)
else:
t = self.itemText(idx)
self.removeItem(idx)
self.insertItem(0, t)
self.setCurrentIndex(0)
self.block_signals(False)
config[self.opt_name] = [unicode(self.itemText(i)) for i in
range(self.count())]
def block_signals(self, yes):
self.blockSignals(yes)
self.line_edit.blockSignals(yes)
def search_from_tokens(self, tokens, all):
ans = u' '.join([u'%s:%s'%x for x in tokens])
if not all:
ans = '[' + ans + ']'
self.set_search_string(ans)
def search_from_tags(self, tags, all):
joiner = ' and ' if all else ' or '
self.set_search_string(joiner.join(tags))
def set_search_string(self, txt):
self.normalize_state()
self.setEditText(txt)
self.emit(SIGNAL('search(PyQt_PyObject, PyQt_PyObject)'), txt, False)
self.line_edit.end(False)
self.initial_state = False
def search_as_you_type(self, enabled):
self.as_you_type = enabled

View File

@ -21,7 +21,7 @@ from calibre.ebooks.oeb.iterator import EbookIterator
from calibre.ebooks import DRMError from calibre.ebooks import DRMError
from calibre.constants import islinux from calibre.constants import islinux
from calibre.utils.config import Config, StringConfig, dynamic from calibre.utils.config import Config, StringConfig, dynamic
from calibre.gui2.library import SearchBox from calibre.gui2.search_box import SearchBox2
from calibre.ebooks.metadata import MetaInformation from calibre.ebooks.metadata import MetaInformation
from calibre.customize.ui import available_input_formats from calibre.customize.ui import available_input_formats
@ -218,7 +218,8 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
self.tool_bar2.insertWidget(self.action_find_next, self.reference) self.tool_bar2.insertWidget(self.action_find_next, self.reference)
self.tool_bar2.insertSeparator(self.action_find_next) self.tool_bar2.insertSeparator(self.action_find_next)
self.setFocusPolicy(Qt.StrongFocus) self.setFocusPolicy(Qt.StrongFocus)
self.search = SearchBox(self, _('Search')) self.search = SearchBox2(self)
self.search.initialize('viewer_search_history')
self.search.setToolTip(_('Search for text in book')) self.search.setToolTip(_('Search for text in book'))
self.tool_bar2.insertWidget(self.action_find_next, self.search) self.tool_bar2.insertWidget(self.action_find_next, self.search)
self.view.set_manager(self) self.view.set_manager(self)
@ -408,10 +409,10 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
def find(self, text, refinement, repeat=False): def find(self, text, refinement, repeat=False):
if not text: if not text:
return return self.search.search_done(False)
if self.view.search(text): if self.view.search(text):
self.scrolled(self.view.scroll_fraction) self.scrolled(self.view.scroll_fraction)
return return self.search.search_done(True)
index = self.iterator.search(text, self.current_index) index = self.iterator.search(text, self.current_index)
if index is None: if index is None:
if self.current_index > 0: if self.current_index > 0:
@ -419,8 +420,8 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
if index is None: if index is None:
info_dialog(self, _('No matches found'), info_dialog(self, _('No matches found'),
_('No matches found for: %s')%text).exec_() _('No matches found for: %s')%text).exec_()
return return self.search.search_done(True)
return return self.search.search_done(True)
self.pending_search = text self.pending_search = text
self.load_path(self.iterator.spine[index]) self.load_path(self.iterator.spine[index])

View File

@ -27,15 +27,12 @@ from calibre.library.sqlite import connect, IntegrityError
from calibre.utils.search_query_parser import SearchQueryParser from calibre.utils.search_query_parser import SearchQueryParser
from calibre.ebooks.metadata import string_to_authors, authors_to_string, \ from calibre.ebooks.metadata import string_to_authors, authors_to_string, \
MetaInformation, authors_to_sort_string MetaInformation, authors_to_sort_string
from calibre.ebooks.metadata.meta import get_metadata, set_metadata, \ from calibre.ebooks.metadata.meta import get_metadata, metadata_from_formats
metadata_from_formats
from calibre.ebooks.metadata.opf2 import metadata_to_opf
from calibre.constants import preferred_encoding, iswindows, isosx, filesystem_encoding from calibre.constants import preferred_encoding, iswindows, isosx, filesystem_encoding
from calibre.ptempfile import PersistentTemporaryFile from calibre.ptempfile import PersistentTemporaryFile
from calibre.customize.ui import run_plugins_on_import from calibre.customize.ui import run_plugins_on_import
from calibre.utils.filenames import ascii_filename, shorten_components_to, \ from calibre.utils.filenames import ascii_filename
supports_long_names
from calibre.ebooks import BOOK_EXTENSIONS from calibre.ebooks import BOOK_EXTENSIONS
if iswindows: if iswindows:
@ -1587,124 +1584,6 @@ books_series_link feeds
progress.reset() progress.reset()
return len(books) return len(books)
def export_to_dir(self, dir, indices, byauthor=False, single_dir=False,
index_is_id=False, callback=None):
if not os.path.exists(dir):
raise IOError('Target directory does not exist: '+dir)
by_author = {}
count = 0
path_len, au_len = (1000, 500) if supports_long_names(dir) else (240, 50)
for index in indices:
id = index if index_is_id else self.id(index)
au = self.conn.get('SELECT author_sort FROM books WHERE id=?',
(id,), all=False)
if not au:
au = self.authors(index, index_is_id=index_is_id)
if not au:
au = _('Unknown')
au = au.split(',')[0]
if not by_author.has_key(au):
by_author[au] = []
by_author[au].append(index)
for au in by_author.keys():
aname = ascii_filename(au)[:au_len]
apath = os.path.abspath(os.path.join(dir, aname))
if not single_dir and not os.path.exists(apath):
os.mkdir(apath)
for idx in by_author[au]:
title = re.sub(r'\s', ' ', self.title(idx, index_is_id=index_is_id))
name = au + ' - ' + title if byauthor else title + ' - ' + au
name = ascii_filename(name)
tname = ascii_filename(title)
tname, name = shorten_components_to(path_len-len(apath), (tname,
name))
name += '_'+str(id)
tpath = os.path.join(apath, tname)
id = idx if index_is_id else self.id(idx)
id = str(id)
if not single_dir and not os.path.exists(tpath):
os.makedirs(tpath)
base = dir if single_dir else tpath
mi = self.get_metadata(idx, index_is_id=index_is_id, get_cover=True)
if not mi.authors:
mi.authors = [_('Unknown')]
cdata = self.cover(int(id), index_is_id=True)
if cdata is not None:
cname = name+'.jpg'
open(os.path.join(base, cname), 'wb').write(cdata)
mi.cover = cname
with open(os.path.join(base, name+'.opf'),
'wb') as f:
f.write(metadata_to_opf(mi))
fmts = self.formats(idx, index_is_id=index_is_id)
if not fmts:
fmts = ''
for fmt in fmts.split(','):
data = self.format(idx, fmt, index_is_id=index_is_id)
if not data:
continue
fname = name +'.'+fmt.lower()
f = open(os.path.join(base, fname), 'w+b')
f.write(data)
f.flush()
f.seek(0)
try:
set_metadata(f, mi, fmt.lower())
except:
pass
f.close()
count += 1
if callable(callback):
if not callback(int(id), mi.title):
return
def export_single_format_to_dir(self, dir, indices, format,
index_is_id=False, callback=None):
dir = os.path.abspath(dir)
if not index_is_id:
indices = map(self.id, indices)
failures = []
plen = 1000 if supports_long_names(dir) else 245
for count, id in enumerate(indices):
try:
data = self.format(id, format, index_is_id=True)
if not data:
failures.append((id, self.title(id, index_is_id=True)))
continue
except:
failures.append((id, self.title(id, index_is_id=True)))
continue
title = self.title(id, index_is_id=True)
au = self.authors(id, index_is_id=True)
if not au:
au = _('Unknown')
fname = '%s - %s'%(title, au)
while fname.endswith('.'):
fname = fname[:-1]
fname = ascii_filename(fname)
fname = fname + '.' + format.lower()
dir = os.path.abspath(dir)
fname = shorten_components_to(plen - len(dir), (fname,))[0]
if not os.path.exists(dir):
os.makedirs(dir)
f = open(os.path.join(dir, fname), 'w+b')
f.write(data)
f.flush()
f.seek(0)
try:
set_metadata(f, self.get_metadata(id, index_is_id=True, get_cover=True),
stream_type=format.lower())
except:
pass
f.close()
if callable(callback):
if not callback(int(id), title):
break
return failures
def find_books_in_directory(self, dirpath, single_book_per_directory): def find_books_in_directory(self, dirpath, single_book_per_directory):
dirpath = os.path.abspath(dirpath) dirpath = os.path.abspath(dirpath)
if single_book_per_directory: if single_book_per_directory:

File diff suppressed because it is too large Load Diff