diff --git a/manual/custom.py b/manual/custom.py index 1e137d6dc2..9359e7d244 100644 --- a/manual/custom.py +++ b/manual/custom.py @@ -90,7 +90,32 @@ def generate_calibredb_help(preamble, app): preamble += textwrap.dedent(''' :command:`calibredb` is the command line interface to the calibre database. It has - several sub-commands, documented below: + several sub-commands, documented below. + + :command:`calibredb` can be used to manipulate either a calibre database + specified by path or a calibre :guilabel:`Content server` running either on + the local machine or over the internet. You can start a calibre + :guilabel:`Content server` using either the :command:`calibre-server` + program or in the main calibre program click :guilabel:`Connect/share -> + Start Content server`. Since :command:`calibredb` can make changes to your + calibre libraries, you must setup authentication on the server first. There + are two ways to do that: + + * If you plan to connect only to a server running on the same computer, + you can simply use the ``--enable-local-write`` option of the + content server, to allow any program, including calibredb, running on + the local computer to make changes to your calibre data. When running + the server from the main calibre program, this option is in + :guilabel:`Preferences->Sharing over the net->Advanced`. + + * If you want to enable access over the internet, then you should setup + user accounts on the server and use the :option:`--username` and :option:`--password` + options to :command:`calibredb` to give it access. You can setup + user authentication for :command:`calibre-server` by using the ``--enable-auth`` + option and using ``--manage-users`` to create the user accounts. + If you are running the server from the main calibre program, use + :guilabel:`Preferences->Sharing over the net->Require username/password`. + ''') diff --git a/src/calibre/db/cli/main.py b/src/calibre/db/cli/main.py index 6d2af6b403..e6004e23af 100644 --- a/src/calibre/db/cli/main.py +++ b/src/calibre/db/cli/main.py @@ -12,10 +12,12 @@ from urllib import urlencode from urlparse import urlparse, urlunparse from calibre import browser, prints -from calibre.constants import __appname__, __version__ +from calibre.constants import __appname__, __version__, iswindows from calibre.db.cli import module_for_cmd from calibre.db.legacy import LibraryDatabase from calibre.utils.config import OptionParser, prefs +from calibre.utils.localization import localize_user_manual_link +from calibre.utils.lock import singleinstance from calibre.utils.serialize import MSGPACK_MIME COMMANDS = ( @@ -51,8 +53,8 @@ def run_cmd(cmd, opts, args, dbctx): if dbctx.is_remote and getattr(m, 'no_remote', False): raise SystemExit(_('The {} command is not supported with remote (server based) libraries').format(cmd)) ret = m.main(opts, args, dbctx) - if not dbctx.is_remote and not opts.dont_notify_gui and not getattr(m, 'readonly', False): - send_message() + # if not dbctx.is_remote and not opts.dont_notify_gui and not getattr(m, 'readonly', False): + # send_message() return ret @@ -71,17 +73,11 @@ def get_parser(usage): ' for example, http://localhost:8080/#mylibrary. library_id is the library id' ' of the library you want to connect to on the Content server. You can use' ' the special library_id value of - to get a list of library ids available' - ' on the server.' - ) - ) - go.add_option( - '--dont-notify-gui', - default=False, - action='store_true', - help=_( - 'Do not notify the running calibre GUI (if any) that the database has' - ' changed. Use with care, as it can lead to database corruption!' - ) + ' on the server. For details on how to setup access via a Content server, see' + ' {}.' + ).format(localize_user_manual_link( + 'https://manual.calibre-ebook.com/generated/en/calibredb.html' + )) ) go.add_option( '-h', '--help', help=_('show this help message and exit'), action='help' @@ -107,7 +103,7 @@ def get_parser(usage): def option_parser(): - parser = OptionParser( + return get_parser( _( '''\ %%prog command [options] [arguments] @@ -121,7 +117,6 @@ For help on an individual command: %%prog command --help ''' ) % '\n '.join(COMMANDS) ) - return parser def read_credetials(opts): @@ -163,6 +158,16 @@ class DBCtx(object): raise SystemExit() else: self.library_path = os.path.expanduser(self.library_path) + if not singleinstance('db'): + ext = '.exe' if iswindows else '' + raise SystemExit(_( + 'Another calibre program such as {} or the main calibre program is running.' + ' Having multiple programs that can make changes to a calibre library' + ' running at the same time is a bad idea. calibredb can connect directly' + ' to a running calibre content server, to make changes through it, instead.' + ' See the documentation of the {} option for details.' + ).format('calibre-server' + ext, '--with-library') + ) self._db = None self.is_remote = False diff --git a/src/calibre/gui2/main.py b/src/calibre/gui2/main.py index 51c310c1f9..cf8ce58720 100644 --- a/src/calibre/gui2/main.py +++ b/src/calibre/gui2/main.py @@ -26,6 +26,7 @@ from calibre.gui2.main_window import option_parser as _option_parser from calibre.gui2.splash_screen import SplashScreen from calibre.utils.config import dynamic, prefs from calibre.utils.ipc import RC, gui_socket_address +from calibre.utils.lock import singleinstance if iswindows: winutil = plugins['winutil'][0] @@ -364,6 +365,16 @@ def shellquote(s): def run_gui(opts, args, listener, app, gui_debug=None): + si = singleinstance('db') + if not si: + ext = '.exe' if iswindows else '' + error_dialog(None, _('Cannot start calibre'), _( + 'Another calibre program that can modify calibre libraries, such as,' + ' {} or {} is already running. You must first shut it down, before' + ' starting the main calibre program. If you are sure no such' + ' program is running, try restarting your computer.').format( + 'calibre-server' + ext, 'calibredb' + ext), show=True) + return 1 initialize_file_icon_provider() app.load_builtin_fonts(scan_for_fonts=True) if not dynamic.get('welcome_wizard_was_run', False): @@ -462,7 +473,6 @@ def shutdown_other(rc=None): if rc.conn is None: prints(_('No running calibre found')) return # No running instance found - from calibre.utils.lock import singleinstance rc.conn.send('shutdown:') prints(_('Shutdown command sent, waiting for shutdown...')) for i in xrange(50): @@ -518,7 +528,6 @@ def main(args=sys.argv): except AbortInit: return 1 try: - from calibre.utils.lock import singleinstance si = singleinstance(singleinstance_name) except Exception: error_dialog(None, _('Cannot start calibre'), _( diff --git a/src/calibre/srv/TODO b/src/calibre/srv/TODO index 11e2e6e4fc..f437c008d2 100644 --- a/src/calibre/srv/TODO +++ b/src/calibre/srv/TODO @@ -1,6 +1,3 @@ -Prevent standalone and embedded servers from running simultaneously -Prevent more than a single instance of the standalone server from running - Fix search completion popups Read progress Bookmarks/annotations diff --git a/src/calibre/srv/standalone.py b/src/calibre/srv/standalone.py index 6e4f31ae9e..cd71015f88 100644 --- a/src/calibre/srv/standalone.py +++ b/src/calibre/srv/standalone.py @@ -18,6 +18,7 @@ from calibre.srv.http_response import create_http_handler from calibre.srv.handler import Handler from calibre.srv.utils import RotatingLog from calibre.utils.config import prefs +from calibre.utils.lock import singleinstance from calibre.db.legacy import LibraryDatabase @@ -297,6 +298,15 @@ def main(args=sys.argv): raise SystemExit(_('You must specify at least one calibre library')) libraries=[prefs['library_path']] + if not singleinstance('db'): + ext = '.exe' if iswindows else '' + raise SystemExit(_( + 'Another calibre program such as another instance of {} or the main' + ' calibre program is running. Having multiple programs that can make' + ' changes to a calibre library running at the same time is not supported.' + ).format('calibre-server' + ext) + ) + if opts.auto_reload: if opts.daemonize: raise SystemExit('Cannot specify --auto-reload and --daemonize at the same time')