mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Automatically send downloaded news to device. Can be controlled via the config dialog.
This commit is contained in:
parent
f96e9cce0c
commit
0a1f3b4525
@ -54,6 +54,8 @@ def _config():
|
|||||||
c.add_opt('autolaunch_server', default=False, help=_('Automatically launch content server on application startup'))
|
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('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('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)
|
return ConfigProxy(c)
|
||||||
|
|
||||||
config = _config()
|
config = _config()
|
||||||
|
@ -118,6 +118,8 @@ class ConfigDialog(QDialog, Ui_Dialog):
|
|||||||
self.password.setText(opts.password if opts.password else '')
|
self.password.setText(opts.password if opts.password else '')
|
||||||
self.auto_launch.setChecked(config['autolaunch_server'])
|
self.auto_launch.setChecked(config['autolaunch_server'])
|
||||||
self.systray_icon.setChecked(config['systray_icon'])
|
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):
|
def up_column(self):
|
||||||
idx = self.columns.currentRow()
|
idx = self.columns.currentRow()
|
||||||
@ -224,6 +226,8 @@ class ConfigDialog(QDialog, Ui_Dialog):
|
|||||||
sc.set('username', unicode(self.username.text()).strip())
|
sc.set('username', unicode(self.username.text()).strip())
|
||||||
sc.set('password', unicode(self.password.text()).strip())
|
sc.set('password', unicode(self.password.text()).strip())
|
||||||
sc.set('port', self.port.value())
|
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())
|
of = str(self.output_format.currentText())
|
||||||
fmts = []
|
fmts = []
|
||||||
for i in range(self.viewer.count()):
|
for i in range(self.viewer.count()):
|
||||||
|
@ -374,7 +374,7 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0" >
|
<item row="5" column="0" >
|
||||||
<widget class="QGroupBox" name="groupBox_2" >
|
<widget class="QGroupBox" name="groupBox_2" >
|
||||||
<property name="title" >
|
<property name="title" >
|
||||||
<string>Toolbar</string>
|
<string>Toolbar</string>
|
||||||
@ -422,7 +422,7 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="0" >
|
<item row="6" column="0" >
|
||||||
<widget class="QGroupBox" name="groupBox" >
|
<widget class="QGroupBox" name="groupBox" >
|
||||||
<property name="title" >
|
<property name="title" >
|
||||||
<string>Select visible &columns in library view</string>
|
<string>Select visible &columns in library view</string>
|
||||||
@ -510,6 +510,20 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="3" column="0" >
|
||||||
|
<widget class="QCheckBox" name="sync_news" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>Automatically send downloaded &news to ebook reader</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0" >
|
||||||
|
<widget class="QCheckBox" name="delete_news" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>&Delete news from library when it is sent to reader</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="page_2" >
|
<widget class="QWidget" name="page_2" >
|
||||||
|
@ -165,6 +165,9 @@ class BooksModel(QAbstractTableModel):
|
|||||||
def add_books(self, paths, formats, metadata, uris=[], add_duplicates=False):
|
def add_books(self, paths, formats, metadata, uris=[], add_duplicates=False):
|
||||||
return self.db.add_books(paths, formats, metadata, uris,
|
return self.db.add_books(paths, formats, metadata, uris,
|
||||||
add_duplicates=add_duplicates)
|
add_duplicates=add_duplicates)
|
||||||
|
|
||||||
|
def add_news(self, path, recipe):
|
||||||
|
return self.db.add_news(path, recipe)
|
||||||
|
|
||||||
def row_indices(self, index):
|
def row_indices(self, index):
|
||||||
''' Return list indices of all cells in index.row()'''
|
''' Return list indices of all cells in index.row()'''
|
||||||
@ -317,14 +320,15 @@ class BooksModel(QAbstractTableModel):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
def get_metadata(self, rows):
|
def get_metadata(self, rows, rows_are_ids=False):
|
||||||
metadata = []
|
metadata = []
|
||||||
for row in rows:
|
if not rows_are_ids:
|
||||||
row = row.row()
|
rows = [self.db.id(row.row()) for row in rows]
|
||||||
au = self.db.authors(row)
|
for id in rows:
|
||||||
tags = self.db.tags(row)
|
au = self.db.authors(id, index_is_id=True)
|
||||||
|
tags = self.db.tags(id, index_is_id=True)
|
||||||
if not au:
|
if not au:
|
||||||
au = 'Unknown'
|
au = _('Unknown')
|
||||||
au = au.split(',')
|
au = au.split(',')
|
||||||
if len(au) > 1:
|
if len(au) > 1:
|
||||||
t = ', '.join(au[:-1])
|
t = ', '.join(au[:-1])
|
||||||
@ -336,15 +340,15 @@ class BooksModel(QAbstractTableModel):
|
|||||||
tags = []
|
tags = []
|
||||||
else:
|
else:
|
||||||
tags = tags.split(',')
|
tags = tags.split(',')
|
||||||
series = self.db.series(row)
|
series = self.db.series(id, index_is_id=True)
|
||||||
if series is not None:
|
if series is not None:
|
||||||
tags.append(series)
|
tags.append(series)
|
||||||
mi = {
|
mi = {
|
||||||
'title' : self.db.title(row),
|
'title' : self.db.title(id, index_is_id=True),
|
||||||
'authors' : au,
|
'authors' : au,
|
||||||
'cover' : self.db.cover(row),
|
'cover' : self.db.cover(id, index_is_id=True),
|
||||||
'tags' : tags,
|
'tags' : tags,
|
||||||
'comments': self.db.comments(row),
|
'comments': self.db.comments(id, index_is_id=True),
|
||||||
}
|
}
|
||||||
if series is not None:
|
if series is not None:
|
||||||
mi['tag order'] = {series:self.db.books_in_series_of(row)}
|
mi['tag order'] = {series:self.db.books_in_series_of(row)}
|
||||||
|
@ -451,6 +451,7 @@ class Main(MainWindow, Ui_MainWindow):
|
|||||||
view.resizeColumnsToContents()
|
view.resizeColumnsToContents()
|
||||||
view.resizeRowsToContents()
|
view.resizeRowsToContents()
|
||||||
view.resize_on_select = not view.isVisible()
|
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().resort(reset=False)
|
||||||
view.model().research()
|
view.model().research()
|
||||||
if memory and memory[1]:
|
if memory and memory[1]:
|
||||||
rows = map(self.library_view.model().db.index, memory[1])
|
self.library_view.model().delete_books_by_id(memory[1])
|
||||||
self.library_view.model().delete_books(rows)
|
|
||||||
|
|
||||||
|
|
||||||
############################################################################
|
############################################################################
|
||||||
@ -742,6 +742,30 @@ class Main(MainWindow, Ui_MainWindow):
|
|||||||
p = p.scaledToHeight(ht, Qt.SmoothTransformation)
|
p = p.scaledToHeight(ht, Qt.SmoothTransformation)
|
||||||
return (p.width(), p.height(), pixmap_to_data(p))
|
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):
|
def sync_to_device(self, on_card, delete_from_library):
|
||||||
rows = self.library_view.selectionModel().selectedRows()
|
rows = self.library_view.selectionModel().selectedRows()
|
||||||
if not self.device_manager or not rows or len(rows) == 0:
|
if not self.device_manager or not rows or len(rows) == 0:
|
||||||
@ -780,10 +804,10 @@ class Main(MainWindow, Ui_MainWindow):
|
|||||||
gf.append(f)
|
gf.append(f)
|
||||||
t = mi['title']
|
t = mi['title']
|
||||||
if not t:
|
if not t:
|
||||||
t = 'Unknown'
|
t = _('Unknown')
|
||||||
a = mi['authors']
|
a = mi['authors']
|
||||||
if not a:
|
if not a:
|
||||||
a = 'Unknown'
|
a = _('Unknown')
|
||||||
prefix = sanitize_file_name(t+' - '+a)
|
prefix = sanitize_file_name(t+' - '+a)
|
||||||
if not isinstance(prefix, unicode):
|
if not isinstance(prefix, unicode):
|
||||||
prefix = prefix.decode(preferred_encoding, 'replace')
|
prefix = prefix.decode(preferred_encoding, 'replace')
|
||||||
@ -854,12 +878,13 @@ class Main(MainWindow, Ui_MainWindow):
|
|||||||
if job.exception is not None:
|
if job.exception is not None:
|
||||||
self.job_exception(job)
|
self.job_exception(job)
|
||||||
return
|
return
|
||||||
mi = get_metadata(open(pt.name, 'rb'), fmt, use_libprs_metadata=False)
|
id = self.library_view.model().add_news(pt.name, recipe)
|
||||||
mi.tags = [_('News'), recipe.title]
|
sync = dynamic.get('news_to_be_synced', set([]))
|
||||||
paths, formats, metadata = [pt.name], [fmt], [mi]
|
sync.add(id)
|
||||||
self.library_view.model().add_books(paths, formats, metadata, add_duplicates=True)
|
dynamic.set('news_to_be_synced', sync)
|
||||||
callback(recipe)
|
callback(recipe)
|
||||||
self.status_bar.showMessage(recipe.title + _(' fetched.'), 3000)
|
self.status_bar.showMessage(recipe.title + _(' fetched.'), 3000)
|
||||||
|
self.sync_news()
|
||||||
|
|
||||||
############################################################################
|
############################################################################
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ from calibre.library.database import LibraryDatabase
|
|||||||
from calibre.library.sqlite import connect, IntegrityError
|
from calibre.library.sqlite import connect, IntegrityError
|
||||||
from calibre.utils.search_query_parser import SearchQueryParser
|
from calibre.utils.search_query_parser import SearchQueryParser
|
||||||
from calibre.ebooks.metadata import string_to_authors, authors_to_string
|
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
|
from calibre.constants import preferred_encoding, iswindows, isosx
|
||||||
|
|
||||||
|
|
||||||
@ -1030,6 +1031,29 @@ class LibraryDatabase2(LibraryDatabase):
|
|||||||
if notify:
|
if notify:
|
||||||
self.notify('metadata', [id])
|
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):
|
def add_books(self, paths, formats, metadata, uris=[], add_duplicates=True):
|
||||||
'''
|
'''
|
||||||
Add a book to the database. The result cache is not updated.
|
Add a book to the database. The result cache is not updated.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user