From 0a1f3b45255ec832ec57017895d69ba9da0ad922 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 30 Nov 2008 20:37:53 -0800 Subject: [PATCH] Automatically send downloaded news to device. Can be controlled via the config dialog. --- src/calibre/gui2/__init__.py | 2 ++ src/calibre/gui2/dialogs/config.py | 4 +++ src/calibre/gui2/dialogs/config.ui | 18 +++++++++++-- src/calibre/gui2/library.py | 24 +++++++++-------- src/calibre/gui2/main.py | 41 ++++++++++++++++++++++++------ src/calibre/library/database2.py | 24 +++++++++++++++++ 6 files changed, 93 insertions(+), 20 deletions(-) diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index 14afa604df..a393843156 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -54,6 +54,8 @@ def _config(): c.add_opt('autolaunch_server', default=False, help=_('Automatically launch content server on application startup')) c.add_opt('oldest_news', default=60, help=_('Oldest news kept in database')) c.add_opt('systray_icon', default=True, help=_('Show system tray icon')) + c.add_opt('upload_news_to_device', default=True, help=_('Upload downloaded news to device')) + c.add_opt('delete_news_from_library_on_upload', default=False, help=_('Delete books from library after uploading to device')) return ConfigProxy(c) config = _config() diff --git a/src/calibre/gui2/dialogs/config.py b/src/calibre/gui2/dialogs/config.py index 38b4060084..e5a71a0f8b 100644 --- a/src/calibre/gui2/dialogs/config.py +++ b/src/calibre/gui2/dialogs/config.py @@ -118,6 +118,8 @@ class ConfigDialog(QDialog, Ui_Dialog): self.password.setText(opts.password if opts.password else '') self.auto_launch.setChecked(config['autolaunch_server']) self.systray_icon.setChecked(config['systray_icon']) + self.sync_news.setChecked(config['upload_news_to_device']) + self.delete_news.setChecked(config['delete_news_from_library_on_upload']) def up_column(self): idx = self.columns.currentRow() @@ -224,6 +226,8 @@ class ConfigDialog(QDialog, Ui_Dialog): sc.set('username', unicode(self.username.text()).strip()) sc.set('password', unicode(self.password.text()).strip()) sc.set('port', self.port.value()) + config['delete_news_from_library_on_upload'] = self.delete_news.isChecked() + config['upload_news_to_device'] = self.sync_news.isChecked() of = str(self.output_format.currentText()) fmts = [] for i in range(self.viewer.count()): diff --git a/src/calibre/gui2/dialogs/config.ui b/src/calibre/gui2/dialogs/config.ui index 637e9d9325..7faa38e715 100644 --- a/src/calibre/gui2/dialogs/config.ui +++ b/src/calibre/gui2/dialogs/config.ui @@ -374,7 +374,7 @@ - + Toolbar @@ -422,7 +422,7 @@ - + Select visible &columns in library view @@ -510,6 +510,20 @@ + + + + Automatically send downloaded &news to ebook reader + + + + + + + &Delete news from library when it is sent to reader + + + diff --git a/src/calibre/gui2/library.py b/src/calibre/gui2/library.py index fec106997b..2fbacc9e88 100644 --- a/src/calibre/gui2/library.py +++ b/src/calibre/gui2/library.py @@ -165,6 +165,9 @@ class BooksModel(QAbstractTableModel): def add_books(self, paths, formats, metadata, uris=[], add_duplicates=False): return self.db.add_books(paths, formats, metadata, uris, add_duplicates=add_duplicates) + + def add_news(self, path, recipe): + return self.db.add_news(path, recipe) def row_indices(self, index): ''' Return list indices of all cells in index.row()''' @@ -317,14 +320,15 @@ class BooksModel(QAbstractTableModel): return data - def get_metadata(self, rows): + def get_metadata(self, rows, rows_are_ids=False): metadata = [] - for row in rows: - row = row.row() - au = self.db.authors(row) - tags = self.db.tags(row) + if not rows_are_ids: + rows = [self.db.id(row.row()) for row in rows] + for id in rows: + au = self.db.authors(id, index_is_id=True) + tags = self.db.tags(id, index_is_id=True) if not au: - au = 'Unknown' + au = _('Unknown') au = au.split(',') if len(au) > 1: t = ', '.join(au[:-1]) @@ -336,15 +340,15 @@ class BooksModel(QAbstractTableModel): tags = [] else: tags = tags.split(',') - series = self.db.series(row) + series = self.db.series(id, index_is_id=True) if series is not None: tags.append(series) mi = { - 'title' : self.db.title(row), + 'title' : self.db.title(id, index_is_id=True), 'authors' : au, - 'cover' : self.db.cover(row), + 'cover' : self.db.cover(id, index_is_id=True), 'tags' : tags, - 'comments': self.db.comments(row), + 'comments': self.db.comments(id, index_is_id=True), } if series is not None: mi['tag order'] = {series:self.db.books_in_series_of(row)} diff --git a/src/calibre/gui2/main.py b/src/calibre/gui2/main.py index 770d2c35fe..cd8a74989b 100644 --- a/src/calibre/gui2/main.py +++ b/src/calibre/gui2/main.py @@ -451,6 +451,7 @@ class Main(MainWindow, Ui_MainWindow): view.resizeColumnsToContents() view.resizeRowsToContents() view.resize_on_select = not view.isVisible() + self.sync_news() ############################################################################ @@ -636,8 +637,7 @@ class Main(MainWindow, Ui_MainWindow): view.model().resort(reset=False) view.model().research() if memory and memory[1]: - rows = map(self.library_view.model().db.index, memory[1]) - self.library_view.model().delete_books(rows) + self.library_view.model().delete_books_by_id(memory[1]) ############################################################################ @@ -742,6 +742,30 @@ class Main(MainWindow, Ui_MainWindow): p = p.scaledToHeight(ht, Qt.SmoothTransformation) return (p.width(), p.height(), pixmap_to_data(p)) + def sync_news(self): + if self.device_connected: + ids = list(dynamic.get('news_to_be_synced', set([]))) + files = [self.library_view.model().db.format(id, prefs['output_format'], index_is_id=True, as_file=True) for id in ids] + files = [f for f in files if f is not None] + metadata = self.library_view.model().get_metadata(ids, rows_are_ids=True) + names = [] + for mi in metadata: + prefix = sanitize_file_name(mi['title']) + if not isinstance(prefix, unicode): + prefix = prefix.decode(preferred_encoding, 'replace') + prefix = ascii_filename(prefix) + names.append('%s_%d%s'%(prefix, id, os.path.splitext(f.name)[1])) + cdata = mi['cover'] + if cdata: + mi['cover'] = self.cover_to_thumbnail(cdata) + dynamic.set('news_to_be_synced', set([])) + if config['upload_news_to_device'] and files: + remove = ids if config['delete_news_from_library_on_upload'] else [] + on_card = self.location_view.model().free[0] < self.location_view.model().free[1] + self.upload_books(files, names, metadata, on_card=on_card, memory=[[f.name for f in files], remove]) + self.status_bar.showMessage(_('Sending news to device.'), 5000) + + def sync_to_device(self, on_card, delete_from_library): rows = self.library_view.selectionModel().selectedRows() if not self.device_manager or not rows or len(rows) == 0: @@ -780,10 +804,10 @@ class Main(MainWindow, Ui_MainWindow): gf.append(f) t = mi['title'] if not t: - t = 'Unknown' + t = _('Unknown') a = mi['authors'] if not a: - a = 'Unknown' + a = _('Unknown') prefix = sanitize_file_name(t+' - '+a) if not isinstance(prefix, unicode): prefix = prefix.decode(preferred_encoding, 'replace') @@ -854,12 +878,13 @@ class Main(MainWindow, Ui_MainWindow): if job.exception is not None: self.job_exception(job) return - mi = get_metadata(open(pt.name, 'rb'), fmt, use_libprs_metadata=False) - mi.tags = [_('News'), recipe.title] - paths, formats, metadata = [pt.name], [fmt], [mi] - self.library_view.model().add_books(paths, formats, metadata, add_duplicates=True) + id = self.library_view.model().add_news(pt.name, recipe) + sync = dynamic.get('news_to_be_synced', set([])) + sync.add(id) + dynamic.set('news_to_be_synced', sync) callback(recipe) self.status_bar.showMessage(recipe.title + _(' fetched.'), 3000) + self.sync_news() ############################################################################ diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 3daf38233d..faf0b560eb 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -20,6 +20,7 @@ from calibre.library.database import LibraryDatabase from calibre.library.sqlite import connect, IntegrityError from calibre.utils.search_query_parser import SearchQueryParser from calibre.ebooks.metadata import string_to_authors, authors_to_string +from calibre.ebooks.metadata.meta import get_metadata from calibre.constants import preferred_encoding, iswindows, isosx @@ -1030,6 +1031,29 @@ class LibraryDatabase2(LibraryDatabase): if notify: self.notify('metadata', [id]) + def add_news(self, path, recipe): + format = os.path.splitext(path)[1][1:].lower() + stream = path if hasattr(path, 'read') else open(path, 'rb') + stream.seek(0) + mi = get_metadata(stream, format, use_libprs_metadata=False) + stream.seek(0) + mi.series_index = 1 + mi.tags = [_('News'), recipe.title] + obj = self.conn.execute('INSERT INTO books(title, author_sort) VALUES (?, ?)', + (mi.title, mi.authors[0])) + id = obj.lastrowid + self.data.books_added([id], self.conn) + self.set_path(id, True) + self.conn.commit() + self.set_metadata(id, mi) + + self.add_format(id, format, stream, index_is_id=True) + if not hasattr(path, 'read'): + stream.close() + self.conn.commit() + self.data.refresh_ids(self.conn, [id]) # Needed to update format list and size + return id + def add_books(self, paths, formats, metadata, uris=[], add_duplicates=True): ''' Add a book to the database. The result cache is not updated.