mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Allow using a tweak to switch db backends
This commit is contained in:
parent
8d4d404b5d
commit
d0b826c39a
@ -118,65 +118,26 @@ def get_data_as_dict(self, prefix=None, authors_as_string=False, ids=None):
|
|||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
def get_db_loader():
|
||||||
|
from calibre.utils.config_base import tweaks
|
||||||
|
if tweaks.get('use_new_db', False):
|
||||||
|
from calibre.db.legacy import LibraryDatabase as cls
|
||||||
|
import apsw
|
||||||
|
errs = (apsw.Error,)
|
||||||
|
else:
|
||||||
|
from calibre.library.database2 import LibraryDatabase2 as cls
|
||||||
|
from calibre.library.sqlite import sqlite, DatabaseException
|
||||||
|
errs = (sqlite.Error, DatabaseException)
|
||||||
|
return cls, errs
|
||||||
|
|
||||||
'''
|
'''
|
||||||
Rewrite of the calibre database backend.
|
|
||||||
|
|
||||||
Broad Objectives:
|
|
||||||
|
|
||||||
* Use the sqlite db only as a datastore. i.e. do not do
|
|
||||||
sorting/searching/concatenation or anything else in sqlite. Instead
|
|
||||||
mirror the sqlite tables in memory, create caches and lookup maps from
|
|
||||||
them and create a set_* API that updates the memory caches and the sqlite
|
|
||||||
correctly.
|
|
||||||
|
|
||||||
* Move from keeping a list of books in memory as a cache to a per table
|
|
||||||
cache. This allows much faster search and sort operations at the expense
|
|
||||||
of slightly slower lookup operations. That slowdown can be mitigated by
|
|
||||||
keeping lots of maps and updating them in the set_* API. Also
|
|
||||||
get_categories becomes blazingly fast.
|
|
||||||
|
|
||||||
* Separate the database layer from the cache layer more cleanly. Rather
|
|
||||||
than having the db layer refer to the cache layer and vice versa, the
|
|
||||||
cache layer will refer to the db layer only and the new API will be
|
|
||||||
defined on the cache layer.
|
|
||||||
|
|
||||||
* Get rid of index_is_id and other poor design decisions
|
|
||||||
|
|
||||||
* Minimize the API as much as possible and define it cleanly
|
|
||||||
|
|
||||||
* Do not change the on disk format of metadata.db at all (this is for
|
|
||||||
backwards compatibility)
|
|
||||||
|
|
||||||
* Get rid of the need for a separate db access thread by switching to apsw
|
|
||||||
to access sqlite, which is thread safe
|
|
||||||
|
|
||||||
* The new API will have methods to efficiently do bulk operations and will
|
|
||||||
use shared/exclusive/pending locks to serialize access to the in-mem data
|
|
||||||
structures. Use the same locking scheme as sqlite itself does.
|
|
||||||
|
|
||||||
How this will proceed:
|
|
||||||
|
|
||||||
1. Create the new API
|
|
||||||
2. Create a test suite for it
|
|
||||||
3. Write a replacement for LibraryDatabase2 that uses the new API
|
|
||||||
internally
|
|
||||||
4. Lots of testing of calibre with the new LibraryDatabase2
|
|
||||||
5. Gradually migrate code to use the (much faster) new api wherever possible (the new api
|
|
||||||
will be exposed via db.new_api)
|
|
||||||
|
|
||||||
I plan to work on this slowly, in parallel to normal calibre development
|
|
||||||
work.
|
|
||||||
|
|
||||||
Various things that require other things before they can be migrated:
|
Various things that require other things before they can be migrated:
|
||||||
1. From initialize_dynamic(): Also add custom
|
1. From initialize_dynamic(): Also add custom
|
||||||
columns/categories/searches info into
|
columns/categories/searches info into
|
||||||
self.field_metadata.
|
self.field_metadata.
|
||||||
2. Catching DatabaseException and sqlite.Error when creating new
|
2. Port library/restore.py
|
||||||
libraries/switching/on calibre startup.
|
3. Replace the metadatabackup thread with the new implementation when using the new backend.
|
||||||
3. Port library/restore.py
|
4. Check that content server reloading on metadata,db change, metadata
|
||||||
4. Replace the metadatabackup thread with the new implementation when using the new backend.
|
|
||||||
5. grep the sources for TODO
|
|
||||||
6. Check that content server reloading on metadata,db change, metadata
|
|
||||||
backup, refresh gui on calibredb add and moving libraries all work (check
|
backup, refresh gui on calibredb add and moving libraries all work (check
|
||||||
them on windows as well for file locking issues)
|
them on windows as well for file locking issues)
|
||||||
'''
|
'''
|
||||||
|
@ -19,9 +19,12 @@ from calibre.utils.config import prefs, tweaks
|
|||||||
from calibre.utils.icu import sort_key
|
from calibre.utils.icu import sort_key
|
||||||
from calibre.gui2 import (gprefs, warning_dialog, Dispatcher, error_dialog,
|
from calibre.gui2 import (gprefs, warning_dialog, Dispatcher, error_dialog,
|
||||||
question_dialog, info_dialog, open_local_file, choose_dir)
|
question_dialog, info_dialog, open_local_file, choose_dir)
|
||||||
from calibre.library.database2 import LibraryDatabase2
|
|
||||||
from calibre.gui2.actions import InterfaceAction
|
from calibre.gui2.actions import InterfaceAction
|
||||||
|
|
||||||
|
def db_class():
|
||||||
|
from calibre.db import get_db_loader
|
||||||
|
return get_db_loader()[0]
|
||||||
|
|
||||||
class LibraryUsageStats(object): # {{{
|
class LibraryUsageStats(object): # {{{
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@ -139,7 +142,7 @@ class MovedDialog(QDialog): # {{{
|
|||||||
|
|
||||||
def accept(self):
|
def accept(self):
|
||||||
newloc = unicode(self.loc.text())
|
newloc = unicode(self.loc.text())
|
||||||
if not LibraryDatabase2.exists_at(newloc):
|
if not db_class.exists_at(newloc):
|
||||||
error_dialog(self, _('No library found'),
|
error_dialog(self, _('No library found'),
|
||||||
_('No existing calibre library found at %s')%newloc,
|
_('No existing calibre library found at %s')%newloc,
|
||||||
show=True)
|
show=True)
|
||||||
@ -313,6 +316,7 @@ class ChooseLibraryAction(InterfaceAction):
|
|||||||
self.qaction.setEnabled(enabled)
|
self.qaction.setEnabled(enabled)
|
||||||
|
|
||||||
def rename_requested(self, name, location):
|
def rename_requested(self, name, location):
|
||||||
|
LibraryDatabase = db_class()
|
||||||
loc = location.replace('/', os.sep)
|
loc = location.replace('/', os.sep)
|
||||||
base = os.path.dirname(loc)
|
base = os.path.dirname(loc)
|
||||||
newname, ok = QInputDialog.getText(self.gui, _('Rename') + ' ' + name,
|
newname, ok = QInputDialog.getText(self.gui, _('Rename') + ' ' + name,
|
||||||
@ -328,10 +332,10 @@ class ChooseLibraryAction(InterfaceAction):
|
|||||||
_('The folder %s already exists. Delete it first.') %
|
_('The folder %s already exists. Delete it first.') %
|
||||||
newloc, show=True)
|
newloc, show=True)
|
||||||
if (iswindows and len(newloc) >
|
if (iswindows and len(newloc) >
|
||||||
LibraryDatabase2.WINDOWS_LIBRARY_PATH_LIMIT):
|
LibraryDatabase.WINDOWS_LIBRARY_PATH_LIMIT):
|
||||||
return error_dialog(self.gui, _('Too long'),
|
return error_dialog(self.gui, _('Too long'),
|
||||||
_('Path to library too long. Must be less than'
|
_('Path to library too long. Must be less than'
|
||||||
' %d characters.')%LibraryDatabase2.WINDOWS_LIBRARY_PATH_LIMIT,
|
' %d characters.')%LibraryDatabase.WINDOWS_LIBRARY_PATH_LIMIT,
|
||||||
show=True)
|
show=True)
|
||||||
if not os.path.exists(loc):
|
if not os.path.exists(loc):
|
||||||
error_dialog(self.gui, _('Not found'),
|
error_dialog(self.gui, _('Not found'),
|
||||||
@ -387,16 +391,17 @@ class ChooseLibraryAction(InterfaceAction):
|
|||||||
'rate of approximately 1 book every three seconds.'), show=True)
|
'rate of approximately 1 book every three seconds.'), show=True)
|
||||||
|
|
||||||
def restore_database(self):
|
def restore_database(self):
|
||||||
|
LibraryDatabase = db_class()
|
||||||
m = self.gui.library_view.model()
|
m = self.gui.library_view.model()
|
||||||
db = m.db
|
db = m.db
|
||||||
if (iswindows and len(db.library_path) >
|
if (iswindows and len(db.library_path) >
|
||||||
LibraryDatabase2.WINDOWS_LIBRARY_PATH_LIMIT):
|
LibraryDatabase.WINDOWS_LIBRARY_PATH_LIMIT):
|
||||||
return error_dialog(self.gui, _('Too long'),
|
return error_dialog(self.gui, _('Too long'),
|
||||||
_('Path to library too long. Must be less than'
|
_('Path to library too long. Must be less than'
|
||||||
' %d characters. Move your library to a location with'
|
' %d characters. Move your library to a location with'
|
||||||
' a shorter path using Windows Explorer, then point'
|
' a shorter path using Windows Explorer, then point'
|
||||||
' calibre to the new location and try again.')%
|
' calibre to the new location and try again.')%
|
||||||
LibraryDatabase2.WINDOWS_LIBRARY_PATH_LIMIT,
|
LibraryDatabase.WINDOWS_LIBRARY_PATH_LIMIT,
|
||||||
show=True)
|
show=True)
|
||||||
|
|
||||||
from calibre.gui2.dialogs.restore_library import restore_database
|
from calibre.gui2.dialogs.restore_library import restore_database
|
||||||
|
@ -55,8 +55,8 @@ class Worker(Thread): # {{{
|
|||||||
notify=False, replace=replace)
|
notify=False, replace=replace)
|
||||||
|
|
||||||
def doit(self):
|
def doit(self):
|
||||||
from calibre.library.database2 import LibraryDatabase2
|
from calibre.db import get_db_loader
|
||||||
newdb = LibraryDatabase2(self.loc, is_second_db=True)
|
newdb = get_db_loader()[0](self.loc, is_second_db=True)
|
||||||
with closing(newdb):
|
with closing(newdb):
|
||||||
self._doit(newdb)
|
self._doit(newdb)
|
||||||
newdb.break_cycles()
|
newdb.break_cycles()
|
||||||
|
@ -15,7 +15,6 @@ from calibre.constants import (filesystem_encoding, iswindows,
|
|||||||
get_portable_base)
|
get_portable_base)
|
||||||
from calibre import isbytestring, patheq, force_unicode
|
from calibre import isbytestring, patheq, force_unicode
|
||||||
from calibre.gui2.wizard import move_library
|
from calibre.gui2.wizard import move_library
|
||||||
from calibre.library.database2 import LibraryDatabase2
|
|
||||||
|
|
||||||
class ChooseLibrary(QDialog, Ui_Dialog):
|
class ChooseLibrary(QDialog, Ui_Dialog):
|
||||||
|
|
||||||
@ -86,6 +85,8 @@ class ChooseLibrary(QDialog, Ui_Dialog):
|
|||||||
show=True)
|
show=True)
|
||||||
return False
|
return False
|
||||||
if ac in ('new', 'move'):
|
if ac in ('new', 'move'):
|
||||||
|
from calibre.db import get_db_loader
|
||||||
|
LibraryDatabase = get_db_loader()[0]
|
||||||
if not empty:
|
if not empty:
|
||||||
error_dialog(self, _('Not empty'),
|
error_dialog(self, _('Not empty'),
|
||||||
_('The folder %s is not empty. Please choose an empty'
|
_('The folder %s is not empty. Please choose an empty'
|
||||||
@ -93,10 +94,10 @@ class ChooseLibrary(QDialog, Ui_Dialog):
|
|||||||
show=True)
|
show=True)
|
||||||
return False
|
return False
|
||||||
if (iswindows and len(loc) >
|
if (iswindows and len(loc) >
|
||||||
LibraryDatabase2.WINDOWS_LIBRARY_PATH_LIMIT):
|
LibraryDatabase.WINDOWS_LIBRARY_PATH_LIMIT):
|
||||||
error_dialog(self, _('Too long'),
|
error_dialog(self, _('Too long'),
|
||||||
_('Path to library too long. Must be less than'
|
_('Path to library too long. Must be less than'
|
||||||
' %d characters.')%LibraryDatabase2.WINDOWS_LIBRARY_PATH_LIMIT,
|
' %d characters.')%LibraryDatabase.WINDOWS_LIBRARY_PATH_LIMIT,
|
||||||
show=True)
|
show=True)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -15,8 +15,6 @@ from calibre.gui2 import (ORG_NAME, APP_UID, initialize_file_icon_provider,
|
|||||||
Application, choose_dir, error_dialog, question_dialog, gprefs)
|
Application, choose_dir, error_dialog, question_dialog, gprefs)
|
||||||
from calibre.gui2.main_window import option_parser as _option_parser
|
from calibre.gui2.main_window import option_parser as _option_parser
|
||||||
from calibre.utils.config import prefs, dynamic
|
from calibre.utils.config import prefs, dynamic
|
||||||
from calibre.library.database2 import LibraryDatabase2
|
|
||||||
from calibre.library.sqlite import sqlite, DatabaseException
|
|
||||||
|
|
||||||
if iswindows:
|
if iswindows:
|
||||||
winutil = plugins['winutil'][0]
|
winutil = plugins['winutil'][0]
|
||||||
@ -51,7 +49,8 @@ path_to_ebook to the database.
|
|||||||
|
|
||||||
def find_portable_library():
|
def find_portable_library():
|
||||||
base = get_portable_base()
|
base = get_portable_base()
|
||||||
if base is None: return
|
if base is None:
|
||||||
|
return
|
||||||
import glob
|
import glob
|
||||||
candidates = [os.path.basename(os.path.dirname(x)) for x in glob.glob(
|
candidates = [os.path.basename(os.path.dirname(x)) for x in glob.glob(
|
||||||
os.path.join(base, u'*%smetadata.db'%os.sep))]
|
os.path.join(base, u'*%smetadata.db'%os.sep))]
|
||||||
@ -224,7 +223,7 @@ class GuiRunner(QObject):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
self.library_path = candidate
|
self.library_path = candidate
|
||||||
db = LibraryDatabase2(candidate)
|
db = self.db_class(candidate)
|
||||||
except:
|
except:
|
||||||
error_dialog(self.splash_screen, _('Bad database location'),
|
error_dialog(self.splash_screen, _('Bad database location'),
|
||||||
_('Bad database location %r. calibre will now quit.'
|
_('Bad database location %r. calibre will now quit.'
|
||||||
@ -235,10 +234,12 @@ class GuiRunner(QObject):
|
|||||||
self.start_gui(db)
|
self.start_gui(db)
|
||||||
|
|
||||||
def initialize_db(self):
|
def initialize_db(self):
|
||||||
|
from calibre.db import get_db_loader
|
||||||
db = None
|
db = None
|
||||||
|
self.db_class, errs = get_db_loader()
|
||||||
try:
|
try:
|
||||||
db = LibraryDatabase2(self.library_path)
|
db = self.db_class(self.library_path)
|
||||||
except (sqlite.Error, DatabaseException):
|
except errs:
|
||||||
repair = question_dialog(self.splash_screen, _('Corrupted database'),
|
repair = question_dialog(self.splash_screen, _('Corrupted database'),
|
||||||
_('The library database at %s appears to be corrupted. Do '
|
_('The library database at %s appears to be corrupted. Do '
|
||||||
'you want calibre to try and rebuild it automatically? '
|
'you want calibre to try and rebuild it automatically? '
|
||||||
@ -249,7 +250,7 @@ class GuiRunner(QObject):
|
|||||||
)
|
)
|
||||||
if repair:
|
if repair:
|
||||||
if repair_library(self.library_path):
|
if repair_library(self.library_path):
|
||||||
db = LibraryDatabase2(self.library_path)
|
db = self.db_class(self.library_path)
|
||||||
except:
|
except:
|
||||||
error_dialog(self.splash_screen, _('Bad database location'),
|
error_dialog(self.splash_screen, _('Bad database location'),
|
||||||
_('Bad database location %r. Will start with '
|
_('Bad database location %r. Will start with '
|
||||||
@ -458,7 +459,8 @@ if __name__ == '__main__':
|
|||||||
try:
|
try:
|
||||||
sys.exit(main())
|
sys.exit(main())
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
if not iswindows: raise
|
if not iswindows:
|
||||||
|
raise
|
||||||
tb = traceback.format_exc()
|
tb = traceback.format_exc()
|
||||||
from PyQt4.QtGui import QErrorMessage
|
from PyQt4.QtGui import QErrorMessage
|
||||||
logfile = os.path.join(os.path.expanduser('~'), 'calibre.log')
|
logfile = os.path.join(os.path.expanduser('~'), 'calibre.log')
|
||||||
@ -470,3 +472,4 @@ if __name__ == '__main__':
|
|||||||
unicode(tb).replace('\n', '<br>'),
|
unicode(tb).replace('\n', '<br>'),
|
||||||
log.replace('\n', '<br>')))
|
log.replace('\n', '<br>')))
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ from calibre import prints, force_unicode
|
|||||||
from calibre.constants import __appname__, isosx, filesystem_encoding
|
from calibre.constants import __appname__, isosx, filesystem_encoding
|
||||||
from calibre.utils.config import prefs, dynamic
|
from calibre.utils.config import prefs, dynamic
|
||||||
from calibre.utils.ipc.server import Server
|
from calibre.utils.ipc.server import Server
|
||||||
from calibre.library.database2 import LibraryDatabase2
|
from calibre.db import get_db_loader
|
||||||
from calibre.customize.ui import interface_actions, available_store_plugins
|
from calibre.customize.ui import interface_actions, available_store_plugins
|
||||||
from calibre.gui2 import (error_dialog, GetMetadata, open_url,
|
from calibre.gui2 import (error_dialog, GetMetadata, open_url,
|
||||||
gprefs, max_available_height, config, info_dialog, Dispatcher,
|
gprefs, max_available_height, config, info_dialog, Dispatcher,
|
||||||
@ -42,7 +42,6 @@ from calibre.gui2.search_restriction_mixin import SearchRestrictionMixin
|
|||||||
from calibre.gui2.tag_browser.ui import TagBrowserMixin
|
from calibre.gui2.tag_browser.ui import TagBrowserMixin
|
||||||
from calibre.gui2.keyboard import Manager
|
from calibre.gui2.keyboard import Manager
|
||||||
from calibre.gui2.auto_add import AutoAdder
|
from calibre.gui2.auto_add import AutoAdder
|
||||||
from calibre.library.sqlite import sqlite, DatabaseException
|
|
||||||
from calibre.gui2.proceed import ProceedQuestion
|
from calibre.gui2.proceed import ProceedQuestion
|
||||||
from calibre.gui2.dialogs.message_box import JobError
|
from calibre.gui2.dialogs.message_box import JobError
|
||||||
from calibre.gui2.job_indicator import Pointer
|
from calibre.gui2.job_indicator import Pointer
|
||||||
@ -575,9 +574,10 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
|||||||
unload_user_template_functions(olddb.library_id)
|
unload_user_template_functions(olddb.library_id)
|
||||||
except:
|
except:
|
||||||
olddb = None
|
olddb = None
|
||||||
|
db_class, errs = get_db_loader()
|
||||||
try:
|
try:
|
||||||
db = LibraryDatabase2(newloc, default_prefs=default_prefs)
|
db = db_class(newloc, default_prefs=default_prefs)
|
||||||
except (DatabaseException, sqlite.Error):
|
except errs:
|
||||||
if not allow_rebuild:
|
if not allow_rebuild:
|
||||||
raise
|
raise
|
||||||
import traceback
|
import traceback
|
||||||
@ -591,7 +591,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
|||||||
if repair:
|
if repair:
|
||||||
from calibre.gui2.dialogs.restore_library import repair_library_at
|
from calibre.gui2.dialogs.restore_library import repair_library_at
|
||||||
if repair_library_at(newloc, parent=self):
|
if repair_library_at(newloc, parent=self):
|
||||||
db = LibraryDatabase2(newloc, default_prefs=default_prefs)
|
db = db_class(newloc, default_prefs=default_prefs)
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
|
@ -14,7 +14,6 @@ from contextlib import closing
|
|||||||
from PyQt4.Qt import (QWizard, QWizardPage, QPixmap, Qt, QAbstractListModel,
|
from PyQt4.Qt import (QWizard, QWizardPage, QPixmap, Qt, QAbstractListModel,
|
||||||
QVariant, QItemSelectionModel, SIGNAL, QObject, QTimer, pyqtSignal)
|
QVariant, QItemSelectionModel, SIGNAL, QObject, QTimer, pyqtSignal)
|
||||||
from calibre import __appname__, patheq
|
from calibre import __appname__, patheq
|
||||||
from calibre.library.database2 import LibraryDatabase2
|
|
||||||
from calibre.library.move import MoveLibrary
|
from calibre.library.move import MoveLibrary
|
||||||
from calibre.constants import (filesystem_encoding, iswindows, plugins,
|
from calibre.constants import (filesystem_encoding, iswindows, plugins,
|
||||||
isportable)
|
isportable)
|
||||||
@ -34,6 +33,10 @@ from calibre.customize.ui import device_plugins
|
|||||||
if iswindows:
|
if iswindows:
|
||||||
winutil = plugins['winutil'][0]
|
winutil = plugins['winutil'][0]
|
||||||
|
|
||||||
|
def db_class():
|
||||||
|
from calibre.db import get_db_loader
|
||||||
|
return get_db_loader()[0]
|
||||||
|
|
||||||
# Devices {{{
|
# Devices {{{
|
||||||
|
|
||||||
class Device(object):
|
class Device(object):
|
||||||
@ -623,7 +626,7 @@ def move_library(oldloc, newloc, parent, callback_on_complete):
|
|||||||
if oldloc and os.access(os.path.join(oldloc, 'metadata.db'), os.R_OK):
|
if oldloc and os.access(os.path.join(oldloc, 'metadata.db'), os.R_OK):
|
||||||
# Move old library to new location
|
# Move old library to new location
|
||||||
try:
|
try:
|
||||||
db = LibraryDatabase2(oldloc)
|
db = db_class()(oldloc)
|
||||||
except:
|
except:
|
||||||
return move_library(None, newloc, parent,
|
return move_library(None, newloc, parent,
|
||||||
callback)
|
callback)
|
||||||
@ -636,13 +639,13 @@ def move_library(oldloc, newloc, parent, callback_on_complete):
|
|||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
# Create new library at new location
|
# Create new library at new location
|
||||||
db = LibraryDatabase2(newloc)
|
db = db_class()(newloc)
|
||||||
callback(newloc)
|
callback(newloc)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Try to load existing library at new location
|
# Try to load existing library at new location
|
||||||
try:
|
try:
|
||||||
LibraryDatabase2(newloc)
|
db_class()(newloc)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
det = traceback.format_exc()
|
det = traceback.format_exc()
|
||||||
error_dialog(parent, _('Invalid database'),
|
error_dialog(parent, _('Invalid database'),
|
||||||
@ -729,7 +732,7 @@ class LibraryPage(QWizardPage, LibraryUI):
|
|||||||
|
|
||||||
def is_library_dir_suitable(self, x):
|
def is_library_dir_suitable(self, x):
|
||||||
try:
|
try:
|
||||||
return LibraryDatabase2.exists_at(x) or not os.listdir(x)
|
return db_class().exists_at(x) or not os.listdir(x)
|
||||||
except:
|
except:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -745,10 +748,10 @@ class LibraryPage(QWizardPage, LibraryUI):
|
|||||||
_('Select location for books'))
|
_('Select location for books'))
|
||||||
if x:
|
if x:
|
||||||
if (iswindows and len(x) >
|
if (iswindows and len(x) >
|
||||||
LibraryDatabase2.WINDOWS_LIBRARY_PATH_LIMIT):
|
db_class().WINDOWS_LIBRARY_PATH_LIMIT):
|
||||||
return error_dialog(self, _('Too long'),
|
return error_dialog(self, _('Too long'),
|
||||||
_('Path to library too long. Must be less than'
|
_('Path to library too long. Must be less than'
|
||||||
' %d characters.')%LibraryDatabase2.WINDOWS_LIBRARY_PATH_LIMIT,
|
' %d characters.')%(db_class().WINDOWS_LIBRARY_PATH_LIMIT),
|
||||||
show=True)
|
show=True)
|
||||||
if not os.path.exists(x):
|
if not os.path.exists(x):
|
||||||
try:
|
try:
|
||||||
|
@ -3,9 +3,9 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
|||||||
''' Code to manage ebook library'''
|
''' Code to manage ebook library'''
|
||||||
|
|
||||||
def db(path=None, read_only=False):
|
def db(path=None, read_only=False):
|
||||||
from calibre.library.database2 import LibraryDatabase2
|
from calibre.db import get_db_loader
|
||||||
from calibre.utils.config import prefs
|
from calibre.utils.config import prefs
|
||||||
return LibraryDatabase2(path if path else prefs['library_path'],
|
return get_db_loader()[0](path if path else prefs['library_path'],
|
||||||
read_only=read_only)
|
read_only=read_only)
|
||||||
|
|
||||||
|
|
||||||
@ -76,3 +76,4 @@ def current_library_name():
|
|||||||
if path:
|
if path:
|
||||||
return posixpath.basename(path)
|
return posixpath.basename(path)
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,15 +15,18 @@ from calibre import preferred_encoding, prints, isbytestring
|
|||||||
from calibre.utils.config import OptionParser, prefs, tweaks
|
from calibre.utils.config import OptionParser, prefs, tweaks
|
||||||
from calibre.ebooks.metadata.meta import get_metadata
|
from calibre.ebooks.metadata.meta import get_metadata
|
||||||
from calibre.ebooks.metadata.book.base import field_from_string
|
from calibre.ebooks.metadata.book.base import field_from_string
|
||||||
from calibre.library.database2 import LibraryDatabase2
|
|
||||||
from calibre.ebooks.metadata.opf2 import OPFCreator, OPF
|
from calibre.ebooks.metadata.opf2 import OPFCreator, OPF
|
||||||
from calibre.utils.date import isoformat
|
from calibre.utils.date import isoformat
|
||||||
|
from calibre.db import get_db_loader
|
||||||
|
|
||||||
FIELDS = set(['title', 'authors', 'author_sort', 'publisher', 'rating',
|
FIELDS = set(['title', 'authors', 'author_sort', 'publisher', 'rating',
|
||||||
'timestamp', 'size', 'tags', 'comments', 'series', 'series_index',
|
'timestamp', 'size', 'tags', 'comments', 'series', 'series_index',
|
||||||
'formats', 'isbn', 'uuid', 'pubdate', 'cover', 'last_modified',
|
'formats', 'isbn', 'uuid', 'pubdate', 'cover', 'last_modified',
|
||||||
'identifiers'])
|
'identifiers'])
|
||||||
|
|
||||||
|
def db_class():
|
||||||
|
return get_db_loader()[0]
|
||||||
|
|
||||||
do_notify = True
|
do_notify = True
|
||||||
def send_message(msg=''):
|
def send_message(msg=''):
|
||||||
global do_notify
|
global do_notify
|
||||||
@ -62,7 +65,7 @@ def get_db(dbpath, options):
|
|||||||
dbpath = os.path.abspath(dbpath)
|
dbpath = os.path.abspath(dbpath)
|
||||||
if options.dont_notify_gui:
|
if options.dont_notify_gui:
|
||||||
do_notify = False
|
do_notify = False
|
||||||
return LibraryDatabase2(dbpath)
|
return db_class()(dbpath)
|
||||||
|
|
||||||
def do_list(db, fields, afields, sort_by, ascending, search_text, line_width, separator,
|
def do_list(db, fields, afields, sort_by, ascending, search_text, line_width, separator,
|
||||||
prefix, limit, subtitle='Books in the calibre database'):
|
prefix, limit, subtitle='Books in the calibre database'):
|
||||||
@ -1101,7 +1104,7 @@ def command_backup_metadata(args, dbpath):
|
|||||||
dbpath = opts.library_path
|
dbpath = opts.library_path
|
||||||
if isbytestring(dbpath):
|
if isbytestring(dbpath):
|
||||||
dbpath = dbpath.decode(preferred_encoding)
|
dbpath = dbpath.decode(preferred_encoding)
|
||||||
db = LibraryDatabase2(dbpath)
|
db = db_class()(dbpath)
|
||||||
book_ids = None
|
book_ids = None
|
||||||
if opts.all:
|
if opts.all:
|
||||||
book_ids = db.all_ids()
|
book_ids = db.all_ids()
|
||||||
@ -1183,7 +1186,7 @@ def command_check_library(args, dbpath):
|
|||||||
for i in list:
|
for i in list:
|
||||||
print ' %-40.40s - %-40.40s'%(i[0], i[1])
|
print ' %-40.40s - %-40.40s'%(i[0], i[1])
|
||||||
|
|
||||||
db = LibraryDatabase2(dbpath)
|
db = db_class()(dbpath)
|
||||||
checker = CheckLibrary(dbpath, db)
|
checker = CheckLibrary(dbpath, db)
|
||||||
checker.scan_library(names, exts)
|
checker.scan_library(names, exts)
|
||||||
for check in checks:
|
for check in checks:
|
||||||
@ -1296,7 +1299,7 @@ def command_list_categories(args, dbpath):
|
|||||||
if isbytestring(dbpath):
|
if isbytestring(dbpath):
|
||||||
dbpath = dbpath.decode(preferred_encoding)
|
dbpath = dbpath.decode(preferred_encoding)
|
||||||
|
|
||||||
db = LibraryDatabase2(dbpath)
|
db = db_class()(dbpath)
|
||||||
category_data = db.get_categories()
|
category_data = db.get_categories()
|
||||||
data = []
|
data = []
|
||||||
report_on = [c.strip() for c in opts.report.split(',') if c.strip()]
|
report_on = [c.strip() for c in opts.report.split(',') if c.strip()]
|
||||||
|
@ -10,14 +10,14 @@ import time, os
|
|||||||
from threading import Thread
|
from threading import Thread
|
||||||
from Queue import Empty
|
from Queue import Empty
|
||||||
|
|
||||||
from calibre.library.database2 import LibraryDatabase2
|
|
||||||
from calibre.utils.ipc.server import Server
|
from calibre.utils.ipc.server import Server
|
||||||
from calibre.utils.ipc.job import ParallelJob
|
from calibre.utils.ipc.job import ParallelJob
|
||||||
|
|
||||||
|
|
||||||
def move_library(from_, to, notification=lambda x:x):
|
def move_library(from_, to, notification=lambda x:x):
|
||||||
|
from calibre.db import get_db_loader
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
old = LibraryDatabase2(from_)
|
old = get_db_loader()[0](from_)
|
||||||
old.move_library_to(to, notification)
|
old.move_library_to(to, notification)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -100,7 +100,7 @@ def daemonize(stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):
|
|||||||
|
|
||||||
|
|
||||||
def main(args=sys.argv):
|
def main(args=sys.argv):
|
||||||
from calibre.library.database2 import LibraryDatabase2
|
from calibre.db import get_db_loader
|
||||||
parser = option_parser()
|
parser = option_parser()
|
||||||
opts, args = parser.parse_args(args)
|
opts, args = parser.parse_args(args)
|
||||||
if opts.daemonize and not iswindows:
|
if opts.daemonize and not iswindows:
|
||||||
@ -116,7 +116,7 @@ def main(args=sys.argv):
|
|||||||
print('No saved library path. Use the --with-library option'
|
print('No saved library path. Use the --with-library option'
|
||||||
' to specify the path to the library you want to use.')
|
' to specify the path to the library you want to use.')
|
||||||
return 1
|
return 1
|
||||||
db = LibraryDatabase2(opts.with_library)
|
db = get_db_loader()[0](opts.with_library)
|
||||||
server = LibraryServer(db, opts, show_tracebacks=opts.develop)
|
server = LibraryServer(db, opts, show_tracebacks=opts.develop)
|
||||||
server.start()
|
server.start()
|
||||||
return 0
|
return 0
|
||||||
|
Loading…
x
Reference in New Issue
Block a user