From c8cc5a05986f33b402693c32ac58d1873c86e991 Mon Sep 17 00:00:00 2001 From: John Schember Date: Sun, 28 Jun 2009 11:33:13 -0400 Subject: [PATCH 01/10] Add isbn to search. --- src/calibre/library/database2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 730ca364d5..3550253ffa 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -198,7 +198,7 @@ class ResultCache(SearchQueryParser): query = query.decode('utf-8') if location in ('tag', 'author', 'format'): location += 's' - all = ('title', 'authors', 'publisher', 'tags', 'comments', 'series', 'formats') + all = ('title', 'authors', 'publisher', 'tags', 'comments', 'series', 'formats', 'isbn') MAP = {} for x in all: MAP[x] = FIELD_MAP[x] From d015f179acb4a1da7b57298619b5a297e8f50514 Mon Sep 17 00:00:00 2001 From: John Schember Date: Sun, 28 Jun 2009 11:39:13 -0400 Subject: [PATCH 02/10] Add isbn: search tag. --- src/calibre/utils/search_query_parser.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/calibre/utils/search_query_parser.py b/src/calibre/utils/search_query_parser.py index 05e0ed3e8f..35241c89c4 100644 --- a/src/calibre/utils/search_query_parser.py +++ b/src/calibre/utils/search_query_parser.py @@ -52,6 +52,7 @@ class SearchQueryParser(object): 'series', 'comments', 'format', + 'isbn', 'all', ] From c2f74ca22af51a3e378520ce561bcce88442fbfd Mon Sep 17 00:00:00 2001 From: John Schember Date: Sun, 28 Jun 2009 12:25:00 -0400 Subject: [PATCH 03/10] Don't throw exception when searching with token: --- src/calibre/gui2/library.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/calibre/gui2/library.py b/src/calibre/gui2/library.py index e27ccba82f..728c835e11 100644 --- a/src/calibre/gui2/library.py +++ b/src/calibre/gui2/library.py @@ -239,7 +239,18 @@ class BooksModel(QAbstractTableModel): self.endInsertRows() self.count_changed() + def clean_search_text(self, text): + tokens = text.split(' ') + for i, token in enumerate(tokens): + if token.strip().endswith(':') or token.strip() == '': + del tokens[i] + text = ' '.join(tokens) + if text.strip() == '': + text = None + return text + def search(self, text, refinement, reset=True): + text = self.clean_search_text(text) self.db.search(text) self.last_search = text if reset: @@ -851,9 +862,9 @@ class DeviceBooksModel(BooksModel): flags |= Qt.ItemIsEditable return flags - def search(self, text, refinement, reset=True): - if not text or not text.strip(): + text = self.clean_search_text(text) + if not text: self.map = list(range(len(self.db))) else: matches = self.search_engine.parse(text) From 1a10ff20ad09f34c57ef512e9aac221b40cc63df Mon Sep 17 00:00:00 2001 From: John Schember Date: Sun, 28 Jun 2009 15:29:21 -0400 Subject: [PATCH 04/10] Remove illegal characters from filepath when uploading to device. --- src/calibre/devices/cybookg3/driver.py | 3 ++- src/calibre/devices/jetbook/driver.py | 3 ++- src/calibre/devices/prs505/driver.py | 12 ++++++++++-- src/calibre/devices/usbms/driver.py | 13 +++++++++++-- 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/calibre/devices/cybookg3/driver.py b/src/calibre/devices/cybookg3/driver.py index 5dde9ab51d..547029630c 100644 --- a/src/calibre/devices/cybookg3/driver.py +++ b/src/calibre/devices/cybookg3/driver.py @@ -68,10 +68,11 @@ class CYBOOKG3(USBMS): newpath = os.path.join(newpath, mdata.get('authors', _('Unknown'))) newpath = os.path.join(newpath, mdata.get('title', _('Unknown'))) + newpath = self._sanitize_path(newpath) if not os.path.exists(newpath): os.makedirs(newpath) - filepath = os.path.join(newpath, names.next()) + filepath = self._sanitize_path(os.path.join(newpath, names.next())) paths.append(filepath) if hasattr(infile, 'read'): diff --git a/src/calibre/devices/jetbook/driver.py b/src/calibre/devices/jetbook/driver.py index 2af1b03bf7..d66fcf03f7 100644 --- a/src/calibre/devices/jetbook/driver.py +++ b/src/calibre/devices/jetbook/driver.py @@ -78,10 +78,11 @@ class JETBOOK(USBMS): if newpath == path: newpath = os.path.join(newpath, author, title) + newpath = self._sanitize_path(newpath) if not os.path.exists(newpath): os.makedirs(newpath) - filepath = os.path.join(newpath, fname) + filepath = self._sanitize_path(os.path.join(newpath, fname)) paths.append(filepath) if hasattr(infile, 'read'): diff --git a/src/calibre/devices/prs505/driver.py b/src/calibre/devices/prs505/driver.py index 6b1f33818a..05da6f0cdf 100644 --- a/src/calibre/devices/prs505/driver.py +++ b/src/calibre/devices/prs505/driver.py @@ -4,7 +4,9 @@ __copyright__ = '2008, Kovid Goyal ' \ ''' Device driver for the SONY PRS-505 ''' -import os, time +import os +import re +import time from itertools import cycle from calibre.devices.usbms.cli import CLI @@ -99,6 +101,11 @@ class PRS505(CLI, Device): self.report_progress(1.0, _('Getting list of books on device...')) return bl + def _sanitize_path(self, path): + path = re.sub('[?<>:*|\^]+', '', path) + path = re.sub('[\.\s]+$', '', path) + return path + def upload_books(self, files, names, on_card=None, end_session=True, metadata=None): if on_card == 'carda' and not self._card_a_prefix: @@ -162,10 +169,11 @@ class PRS505(CLI, Device): newpath = os.path.join(newpath, mdata.get('authors', _('Unknown'))) newpath = os.path.join(newpath, mdata.get('title', _('Unknown'))) + newpath = self._sanitize_path(newpath) if not os.path.exists(newpath): os.makedirs(newpath) - filepath = os.path.join(newpath, names.next()) + filepath = self._sanitize_path(os.path.join(newpath, names.next())) paths.append(filepath) self.put_file(infile, paths[-1], replace_file=True) diff --git a/src/calibre/devices/usbms/driver.py b/src/calibre/devices/usbms/driver.py index fe39e12077..317d699262 100644 --- a/src/calibre/devices/usbms/driver.py +++ b/src/calibre/devices/usbms/driver.py @@ -7,7 +7,10 @@ driver. It is intended to be subclassed with the relevant parts implemented for a particular device. ''' -import os, fnmatch, shutil +import os +import fnmatch +import shutil +import re from itertools import cycle from calibre.ebooks.metadata import authors_to_string @@ -113,6 +116,11 @@ class USBMS(CLI, Device): raise FreeSpaceError(_("There is insufficient free space on the storage card")) return path + def _sanitize_path(self, path): + path = re.sub('[?<>:*|\^]+', '', path) + path = re.sub('[\.\s]+$', '', path) + return path + def upload_books(self, files, names, on_card=None, end_session=True, metadata=None): @@ -145,10 +153,11 @@ class USBMS(CLI, Device): mdata.get('authors', _('Unknown')), mdata.get('title', _('Unknown'))) + newpath = self._sanitize_path(newpath) if not os.path.exists(newpath): os.makedirs(newpath) - filepath = os.path.join(newpath, names.next()) + filepath = self._sanitize_path(os.path.join(newpath, names.next())) paths.append(filepath) if hasattr(infile, 'read'): From 0a9ddcda0c29d94929469d1797f6659471f787ef Mon Sep 17 00:00:00 2001 From: John Schember Date: Sun, 28 Jun 2009 17:59:33 -0400 Subject: [PATCH 05/10] Prevent exception with search text. --- src/calibre/gui2/library.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/calibre/gui2/library.py b/src/calibre/gui2/library.py index 728c835e11..c3134de917 100644 --- a/src/calibre/gui2/library.py +++ b/src/calibre/gui2/library.py @@ -240,6 +240,8 @@ class BooksModel(QAbstractTableModel): self.count_changed() def clean_search_text(self, text): + if not text: + return text tokens = text.split(' ') for i, token in enumerate(tokens): if token.strip().endswith(':') or token.strip() == '': From b975eb135c219493823a217563b240916f80f435 Mon Sep 17 00:00:00 2001 From: John Schember Date: Sun, 28 Jun 2009 18:13:56 -0400 Subject: [PATCH 06/10] sanitize file names with build in santitize function. --- src/calibre/devices/cybookg3/driver.py | 15 ++++++++------- src/calibre/devices/jetbook/driver.py | 3 +-- src/calibre/devices/prs505/driver.py | 18 ++++++------------ src/calibre/devices/usbms/driver.py | 18 ++++++------------ 4 files changed, 21 insertions(+), 33 deletions(-) diff --git a/src/calibre/devices/cybookg3/driver.py b/src/calibre/devices/cybookg3/driver.py index 547029630c..eebd28ebee 100644 --- a/src/calibre/devices/cybookg3/driver.py +++ b/src/calibre/devices/cybookg3/driver.py @@ -4,9 +4,11 @@ __copyright__ = '2009, John Schember ' Device driver for Bookeen's Cybook Gen 3 ''' -import os, shutil +import os +import shutil from itertools import cycle +from calibre import sanitize_file_name as sanitize from calibre.devices.errors import DeviceError, FreeSpaceError from calibre.devices.usbms.driver import USBMS import calibre.devices.cybookg3.t2b as t2b @@ -57,22 +59,21 @@ class CYBOOKG3(USBMS): for tag in mdata['tags']: if tag.startswith(_('News')): newpath = os.path.join(newpath, 'news') - newpath = os.path.join(newpath, mdata.get('title', '')) - newpath = os.path.join(newpath, mdata.get('timestamp', '')) + newpath = os.path.join(newpath, sanitize(mdata.get('title', ''))) + newpath = os.path.join(newpath, sanitize(mdata.get('timestamp', ''))) elif tag.startswith('/'): newpath += tag newpath = os.path.normpath(newpath) break if newpath == path: - newpath = os.path.join(newpath, mdata.get('authors', _('Unknown'))) - newpath = os.path.join(newpath, mdata.get('title', _('Unknown'))) + newpath = os.path.join(newpath, sanitize(mdata.get('authors', _('Unknown')))) + newpath = os.path.join(newpath, sanitize(mdata.get('title', _('Unknown')))) - newpath = self._sanitize_path(newpath) if not os.path.exists(newpath): os.makedirs(newpath) - filepath = self._sanitize_path(os.path.join(newpath, names.next())) + filepath = os.path.join(newpath, sanitize(names.next())) paths.append(filepath) if hasattr(infile, 'read'): diff --git a/src/calibre/devices/jetbook/driver.py b/src/calibre/devices/jetbook/driver.py index d66fcf03f7..2af1b03bf7 100644 --- a/src/calibre/devices/jetbook/driver.py +++ b/src/calibre/devices/jetbook/driver.py @@ -78,11 +78,10 @@ class JETBOOK(USBMS): if newpath == path: newpath = os.path.join(newpath, author, title) - newpath = self._sanitize_path(newpath) if not os.path.exists(newpath): os.makedirs(newpath) - filepath = self._sanitize_path(os.path.join(newpath, fname)) + filepath = os.path.join(newpath, fname) paths.append(filepath) if hasattr(infile, 'read'): diff --git a/src/calibre/devices/prs505/driver.py b/src/calibre/devices/prs505/driver.py index 05da6f0cdf..165dd324ad 100644 --- a/src/calibre/devices/prs505/driver.py +++ b/src/calibre/devices/prs505/driver.py @@ -5,10 +5,10 @@ __copyright__ = '2008, Kovid Goyal ' \ Device driver for the SONY PRS-505 ''' import os -import re import time from itertools import cycle +from calibre import sanitize_file_name as sanitize from calibre.devices.usbms.cli import CLI from calibre.devices.usbms.device import Device from calibre.devices.errors import DeviceError, FreeSpaceError @@ -101,11 +101,6 @@ class PRS505(CLI, Device): self.report_progress(1.0, _('Getting list of books on device...')) return bl - def _sanitize_path(self, path): - path = re.sub('[?<>:*|\^]+', '', path) - path = re.sub('[\.\s]+$', '', path) - return path - def upload_books(self, files, names, on_card=None, end_session=True, metadata=None): if on_card == 'carda' and not self._card_a_prefix: @@ -157,8 +152,8 @@ class PRS505(CLI, Device): for tag in mdata['tags']: if tag.startswith(_('News')): newpath = os.path.join(newpath, 'news') - newpath = os.path.join(newpath, mdata.get('title', '')) - newpath = os.path.join(newpath, mdata.get('timestamp', '')) + newpath = os.path.join(newpath, sanitize(mdata.get('title', ''))) + newpath = os.path.join(newpath, sanitize(mdata.get('timestamp', ''))) elif tag.startswith('/'): newpath = path newpath += tag @@ -166,14 +161,13 @@ class PRS505(CLI, Device): break if newpath == path: - newpath = os.path.join(newpath, mdata.get('authors', _('Unknown'))) - newpath = os.path.join(newpath, mdata.get('title', _('Unknown'))) + newpath = os.path.join(newpath, sanitize(mdata.get('authors', _('Unknown')))) + newpath = os.path.join(newpath, sanitize(mdata.get('title', _('Unknown')))) - newpath = self._sanitize_path(newpath) if not os.path.exists(newpath): os.makedirs(newpath) - filepath = self._sanitize_path(os.path.join(newpath, names.next())) + filepath = os.path.join(newpath, sanitize(names.next())) paths.append(filepath) self.put_file(infile, paths[-1], replace_file=True) diff --git a/src/calibre/devices/usbms/driver.py b/src/calibre/devices/usbms/driver.py index 317d699262..ae5504fc71 100644 --- a/src/calibre/devices/usbms/driver.py +++ b/src/calibre/devices/usbms/driver.py @@ -10,9 +10,9 @@ for a particular device. import os import fnmatch import shutil -import re from itertools import cycle +from calibre import sanitize_file_name as sanitize from calibre.ebooks.metadata import authors_to_string from calibre.devices.usbms.cli import CLI from calibre.devices.usbms.device import Device @@ -116,11 +116,6 @@ class USBMS(CLI, Device): raise FreeSpaceError(_("There is insufficient free space on the storage card")) return path - def _sanitize_path(self, path): - path = re.sub('[?<>:*|\^]+', '', path) - path = re.sub('[\.\s]+$', '', path) - return path - def upload_books(self, files, names, on_card=None, end_session=True, metadata=None): @@ -140,8 +135,8 @@ class USBMS(CLI, Device): for tag in mdata['tags']: if tag.startswith(_('News')): newpath = os.path.join(newpath, 'news') - newpath = os.path.join(newpath, mdata.get('title', '')) - newpath = os.path.join(newpath, mdata.get('timestamp', '')) + newpath = os.path.join(newpath, sanitize(mdata.get('title', ''))) + newpath = os.path.join(newpath, sanitize(mdata.get('timestamp', ''))) break elif tag.startswith('/'): newpath += tag @@ -150,14 +145,13 @@ class USBMS(CLI, Device): if newpath == path: newpath = os.path.join(newpath, - mdata.get('authors', _('Unknown')), - mdata.get('title', _('Unknown'))) + sanitize(mdata.get('authors', _('Unknown'))), + sanitize(mdata.get('title', _('Unknown')))) - newpath = self._sanitize_path(newpath) if not os.path.exists(newpath): os.makedirs(newpath) - filepath = self._sanitize_path(os.path.join(newpath, names.next())) + filepath = os.path.join(newpath, sanitize(names.next())) paths.append(filepath) if hasattr(infile, 'read'): From 6a88e685de494e6bf0d9fbada985e3940636a9c8 Mon Sep 17 00:00:00 2001 From: John Schember Date: Sun, 28 Jun 2009 18:50:16 -0400 Subject: [PATCH 07/10] PDB title: include space and ensure \x00 is the last character. --- src/calibre/ebooks/pdb/header.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/calibre/ebooks/pdb/header.py b/src/calibre/ebooks/pdb/header.py index c012110a62..75455354c2 100644 --- a/src/calibre/ebooks/pdb/header.py +++ b/src/calibre/ebooks/pdb/header.py @@ -30,7 +30,7 @@ class PdbHeaderReader(object): def name(self): self.stream.seek(0) - return re.sub('[^-A-Za-z0-9]+', '', self.stream.read(32).replace('\x00', '')) + return re.sub('[^-A-Za-z0-9 ]+', '', self.stream.read(32).replace('\x00', '')) def full_section_info(self, number): if number not in range(0, self.num_sections): @@ -66,7 +66,7 @@ class PdbHeaderBuilder(object): def __init__(self, identity, title): self.identity = identity.ljust(3, '\x00')[:8] - self.title = re.sub('[^-A-Za-z0-9]+', '_', title).ljust(32, '\x00')[:32].encode('ascii', 'replace') + self.title = '%s\x00' % re.sub('[^-A-Za-z0-9 ]+', '_', title).ljust(31, '\x00')[:31].encode('ascii', 'replace') def build_header(self, section_lengths, out_stream): ''' From f9b51db519ba0a735087dbdec8683561481f2a54 Mon Sep 17 00:00:00 2001 From: John Schember Date: Sun, 28 Jun 2009 18:54:38 -0400 Subject: [PATCH 08/10] Fix bug #2742: set the series value to 0 if mi.series_index is an invalid type. --- src/calibre/gui2/convert/metadata.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/calibre/gui2/convert/metadata.py b/src/calibre/gui2/convert/metadata.py index d961fef473..5c82a1d19c 100644 --- a/src/calibre/gui2/convert/metadata.py +++ b/src/calibre/gui2/convert/metadata.py @@ -54,7 +54,10 @@ class MetadataWidget(Widget, Ui_Form): if mi.series: self.series.setCurrentIndex(self.series.findText(mi.series)) if mi.series_index is not None: - self.series_index.setValue(mi.series_index) + try: + self.series_index.setValue(mi.series_index) + except: + self.series_index.setValue(0) cover = self.db.cover(self.book_id, index_is_id=True) if cover: From be1b29d35b4eef2c141c8a1822e42c36979a980a Mon Sep 17 00:00:00 2001 From: John Schember Date: Sun, 28 Jun 2009 19:23:06 -0400 Subject: [PATCH 09/10] Possible fix for bug #2738. --- src/calibre/devices/prs505/driver.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/calibre/devices/prs505/driver.py b/src/calibre/devices/prs505/driver.py index 165dd324ad..6c7049d8e8 100644 --- a/src/calibre/devices/prs505/driver.py +++ b/src/calibre/devices/prs505/driver.py @@ -183,6 +183,9 @@ class PRS505(CLI, Device): return zip(paths, sizes, ctimes, cycle([on_card])) def add_books_to_metadata(self, locations, metadata, booklists): + if not locations or not metadata: + return + metadata = iter(metadata) for location in locations: info = metadata.next() From b0ec329dc35ebd40c2f4cefa78616c15bebfe882 Mon Sep 17 00:00:00 2001 From: John Schember Date: Sun, 28 Jun 2009 20:56:27 -0400 Subject: [PATCH 10/10] Option to add book with no formats. Useful for organizing a collection even when they aren't acquired yet. --- src/calibre/gui2/main.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/calibre/gui2/main.py b/src/calibre/gui2/main.py index 353f4d9c37..e3aa0e6b8c 100644 --- a/src/calibre/gui2/main.py +++ b/src/calibre/gui2/main.py @@ -234,6 +234,8 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI): self.add_menu.addAction(_('Add books from directories, including ' 'sub directories (Multiple books per directory, assumes every ' 'ebook file is a different book)')) + self.add_menu.addAction(_('Add Empty book. (Book entry with no ' + 'formats)')) self.action_add.setMenu(self.add_menu) QObject.connect(self.action_add, SIGNAL("triggered(bool)"), self.add_books) @@ -243,6 +245,8 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI): self.add_recursive_single) QObject.connect(self.add_menu.actions()[2], SIGNAL("triggered(bool)"), self.add_recursive_multiple) + QObject.connect(self.add_menu.actions()[3], SIGNAL('triggered(bool)'), + self.add_empty) QObject.connect(self.action_del, SIGNAL("triggered(bool)"), self.delete_books) QObject.connect(self.action_edit, SIGNAL("triggered(bool)"), @@ -779,6 +783,15 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI): ''' self.add_recursive(False) + def add_empty(self, checked): + ''' + Add an empty book item to the library. This does not import any formats + from a book file. + ''' + from calibre.ebooks.metadata import MetaInformation + self.library_view.model().db.import_book(MetaInformation(None), []) + self.library_view.model().books_added(1) + def files_dropped(self, paths): to_device = self.stack.currentIndex() != 0 self._add_books(paths, to_device)