diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py
index 9150172fc1..92a68fa840 100644
--- a/src/calibre/gui2/__init__.py
+++ b/src/calibre/gui2/__init__.py
@@ -50,6 +50,7 @@ gprefs.defaults['action-layout-context-menu-device'] = (
gprefs.defaults['show_splash_screen'] = True
gprefs.defaults['toolbar_icon_size'] = 'medium'
+gprefs.defaults['automerge'] = 'ignore'
gprefs.defaults['toolbar_text'] = 'auto'
gprefs.defaults['show_child_bar'] = False
gprefs.defaults['font'] = None
diff --git a/src/calibre/gui2/actions/add.py b/src/calibre/gui2/actions/add.py
index 4236a63340..25127d3635 100644
--- a/src/calibre/gui2/actions/add.py
+++ b/src/calibre/gui2/actions/add.py
@@ -244,8 +244,8 @@ class AddAction(InterfaceAction):
x.decode(preferred_encoding, 'replace') for x in
self._adder.merged_books])
info_dialog(self.gui, _('Merged some books'),
- _('Some duplicates were found and merged into the '
- 'following existing books:'), det_msg=books, show=True)
+ _('The following duplicate books were found and incoming book formats were '
+ 'processed and merged into your Calibre database according to your automerge settings:'), det_msg=books, show=True)
if getattr(self._adder, 'critical', None):
det_msg = []
for name, log in self._adder.critical.items():
diff --git a/src/calibre/gui2/add.py b/src/calibre/gui2/add.py
index 026fabea07..086f40feee 100644
--- a/src/calibre/gui2/add.py
+++ b/src/calibre/gui2/add.py
@@ -8,7 +8,7 @@ from functools import partial
from PyQt4.Qt import QThread, QObject, Qt, QProgressDialog, pyqtSignal, QTimer
from calibre.gui2.dialogs.progress import ProgressDialog
-from calibre.gui2 import question_dialog, error_dialog, info_dialog
+from calibre.gui2 import question_dialog, error_dialog, info_dialog, gprefs
from calibre.ebooks.metadata.opf2 import OPF
from calibre.ebooks.metadata import MetaInformation
from calibre.constants import preferred_encoding, filesystem_encoding, DEBUG
@@ -179,23 +179,46 @@ class DBAdder(QObject): # {{{
cover = f.read()
orig_formats = formats
formats = [f for f in formats if not f.lower().endswith('.opf')]
- if prefs['add_formats_to_existing']:
+ if prefs['add_formats_to_existing']: #automerge is on
identical_book_list = self.db.find_identical_books(mi)
-
- if identical_book_list: # books with same author and nearly same title exist in db
+ if identical_book_list: # books with same author and nearly same title exist in db
self.merged_books.add(mi.title)
+ a_new_record_has_been_created = False
for identical_book in identical_book_list:
- self.add_formats(identical_book, formats, replace=False)
- else:
- id = self.db.create_book_entry(mi, cover=cover, add_duplicates=True)
+ if gprefs['automerge'] == 'ignore':
+ self.add_formats(identical_book, formats, replace=False)
+ if gprefs['automerge'] == 'overwrite':
+ self.add_formats(identical_book, formats, replace=True)
+ if gprefs['automerge'] == 'new record' and not a_new_record_has_been_created:
+ '''
+ We are here because we have at least one book record in the db that matches the one file/format being processed
+ We need to check if the file/format being processed matches a format in the matching book record.
+ If so, create new record (as below), else, add to existing record, as above.
+ Test if format exists in matching record. identical_book is an id, formats is a FQPN path in a list
+ '''
+ for path in formats:
+ fmt = os.path.splitext(path)[-1].replace('.', '').upper()
+ ib_fmts = self.db.formats(identical_book, index_is_id=True)
+ if ib_fmts and fmt in ib_fmts: # Create a new record
+ if not a_new_record_has_been_created:
+ id_ = self.db.create_book_entry(mi, cover=cover, add_duplicates=True)
+ self.number_of_books_added += 1
+ self.add_formats(id_, formats)
+ a_new_record_has_been_created = True
+ else: #new record not required
+ self.add_formats(identical_book, formats, replace=False)
+
+ else: # books with same author and nearly same title do not exist in db
+ id_ = self.db.create_book_entry(mi, cover=cover, add_duplicates=True)
self.number_of_books_added += 1
- self.add_formats(id, formats)
- else:
- id = self.db.create_book_entry(mi, cover=cover, add_duplicates=False)
- if id is None:
+ self.add_formats(id_, formats)
+
+ else: #automerge is off
+ id_ = self.db.create_book_entry(mi, cover=cover, add_duplicates=False)
+ if id_ is None:
self.duplicates.append((mi, cover, orig_formats))
else:
- self.add_formats(id, formats)
+ self.add_formats(id_, formats)
self.number_of_books_added += 1
else:
self.names.append(name)
diff --git a/src/calibre/gui2/preferences/adding.py b/src/calibre/gui2/preferences/adding.py
index e919d53b64..b4c4ce846a 100644
--- a/src/calibre/gui2/preferences/adding.py
+++ b/src/calibre/gui2/preferences/adding.py
@@ -12,6 +12,7 @@ from calibre.gui2.preferences import ConfigWidgetBase, test_widget, \
from calibre.gui2.preferences.adding_ui import Ui_Form
from calibre.utils.config import prefs
from calibre.gui2.widgets import FilenamePattern
+from calibre.gui2 import gprefs
class ConfigWidget(ConfigWidgetBase, Ui_Form):
@@ -23,18 +24,23 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
r('read_file_metadata', prefs)
r('swap_author_names', prefs)
r('add_formats_to_existing', prefs)
+ choices = [
+ (_('Ignore duplicate incoming formats'), 'ignore'),
+ (_('Overwrite existing duplicate formats'), 'overwrite'),
+ (_('Create new record for each duplicate format'), 'new record')]
+ r('automerge', gprefs, choices=choices)
r('new_book_tags', prefs, setting=CommaSeparatedList)
self.filename_pattern = FilenamePattern(self)
self.metadata_box.layout().insertWidget(0, self.filename_pattern)
self.filename_pattern.changed_signal.connect(self.changed_signal.emit)
-
def initialize(self):
ConfigWidgetBase.initialize(self)
self.filename_pattern.blockSignals(True)
self.filename_pattern.initialize()
self.filename_pattern.blockSignals(False)
+ self.opt_automerge.setEnabled(self.opt_add_formats_to_existing.isChecked())
def restore_defaults(self):
ConfigWidgetBase.restore_defaults(self)
diff --git a/src/calibre/gui2/preferences/adding.ui b/src/calibre/gui2/preferences/adding.ui
index 75e6c466f0..f9a2c74444 100644
--- a/src/calibre/gui2/preferences/adding.ui
+++ b/src/calibre/gui2/preferences/adding.ui
@@ -6,7 +6,7 @@
0
0
- 750
+ 753
339
@@ -58,16 +58,33 @@
- -
+
-
- If an existing book with a similar title and author is found that does not have the format being added, the format is added
-to the existing book, instead of creating a new entry. If the existing book already has the format, then it is silently ignored.
+ Automerge: If books with similar titles and authors found, merge the incoming formats automatically into
+existing book records. The box to the right controls what happens when an existing record already has
+the incoming format. Note that this option also affects the Copy to library action.
Title match ignores leading indefinite articles ("the", "a", "an"), punctuation, case, etc. Author match is exact.
- If books with similar titles and authors found, &merge the new files automatically
+ &Automerge added books if they already exist in the calibre library:
+
+
+
+ -
+
+
+ Automerge: If books with similar titles and authors found, merge the incoming formats automatically into
+existing book records. This box controls what happens when an existing record already has
+the incoming format:
+
+Ignore duplicate incoming files - means that existing files in your calibre library will not be replaced
+Overwrite existing duplicate files - means that existing files in your calibre library will be replaced
+Create new record for each duplicate file - means that a new book entry will be created for each duplicate file
+
+Title matching ignores leading indefinite articles ("the", "a", "an"), punctuation, case, etc.
+Author matching is exact.
@@ -113,5 +130,22 @@ Title match ignores leading indefinite articles ("the", "a",
-
+
+
+ opt_add_formats_to_existing
+ toggled(bool)
+ opt_automerge
+ setEnabled(bool)
+
+
+ 406
+ 83
+
+
+ 457
+ 83
+
+
+
+
diff --git a/src/calibre/library/server/content.py b/src/calibre/library/server/content.py
index 62d08aa2c3..11ea2b951e 100644
--- a/src/calibre/library/server/content.py
+++ b/src/calibre/library/server/content.py
@@ -124,7 +124,6 @@ class ContentServer(object):
cherrypy.request.headers.get('Want-OPDS-Catalog', 919) != 919 or \
ua.startswith('Stanza')
- # A better search would be great
want_mobile = self.is_mobile_browser(ua)
if self.opts.develop and not want_mobile:
cherrypy.log('User agent: '+ua)