mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Untested store download job.
This commit is contained in:
parent
7ed397ca4d
commit
deea9f48bc
@ -588,7 +588,7 @@ class StorePlugin(Plugin): # {{{
|
|||||||
author = 'John Schember'
|
author = 'John Schember'
|
||||||
type = _('Stores')
|
type = _('Stores')
|
||||||
|
|
||||||
def open(self, parent=None, start_item=None):
|
def open(self, gui, parent=None, start_item=None):
|
||||||
'''
|
'''
|
||||||
Open a dialog for displaying the store.
|
Open a dialog for displaying the store.
|
||||||
start_item is a refernce unique to the store
|
start_item is a refernce unique to the store
|
||||||
|
@ -27,8 +27,8 @@ class StoreAction(InterfaceAction):
|
|||||||
|
|
||||||
def search(self):
|
def search(self):
|
||||||
from calibre.gui2.store.search import SearchDialog
|
from calibre.gui2.store.search import SearchDialog
|
||||||
sd = SearchDialog(self.gui)
|
sd = SearchDialog(self.gui, self.gui)
|
||||||
sd.exec_()
|
sd.exec_()
|
||||||
|
|
||||||
def open_store(self, store_plugin):
|
def open_store(self, store_plugin):
|
||||||
store_plugin.open(self.gui)
|
store_plugin.open(self.gui, self.gui)
|
||||||
|
@ -14,10 +14,12 @@ class AmazonKindleDialog(QDialog, Ui_Dialog):
|
|||||||
|
|
||||||
ASTORE_URL = 'http://astore.amazon.com/josbl0e-20/'
|
ASTORE_URL = 'http://astore.amazon.com/josbl0e-20/'
|
||||||
|
|
||||||
def __init__(self, parent=None, start_item=None):
|
def __init__(self, gui, parent=None, start_item=None):
|
||||||
QDialog.__init__(self, parent=parent)
|
QDialog.__init__(self, parent=parent)
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
|
|
||||||
|
self.gui = gui
|
||||||
|
|
||||||
self.view.loadStarted.connect(self.load_started)
|
self.view.loadStarted.connect(self.load_started)
|
||||||
self.view.loadProgress.connect(self.load_progress)
|
self.view.loadProgress.connect(self.load_progress)
|
||||||
self.view.loadFinished.connect(self.load_finished)
|
self.view.loadFinished.connect(self.load_finished)
|
||||||
|
@ -18,9 +18,9 @@ class AmazonKindleStore(StorePlugin):
|
|||||||
name = 'Amazon Kindle'
|
name = 'Amazon Kindle'
|
||||||
description = _('Buy Kindle books from Amazon')
|
description = _('Buy Kindle books from Amazon')
|
||||||
|
|
||||||
def open(self, parent=None, start_item=None):
|
def open(self, gui, parent=None, start_item=None):
|
||||||
from calibre.gui2.store.amazon.amazon_kindle_dialog import AmazonKindleDialog
|
from calibre.gui2.store.amazon.amazon_kindle_dialog import AmazonKindleDialog
|
||||||
d = AmazonKindleDialog(parent, start_item)
|
d = AmazonKindleDialog(gui, parent, start_item)
|
||||||
d = d.exec_()
|
d = d.exec_()
|
||||||
|
|
||||||
def search(self, query, max_results=10, timeout=60):
|
def search(self, query, max_results=10, timeout=60):
|
||||||
|
@ -21,10 +21,12 @@ class SearchDialog(QDialog, Ui_Dialog):
|
|||||||
HANG_TIME = 75000 # milliseconds seconds
|
HANG_TIME = 75000 # milliseconds seconds
|
||||||
TIMEOUT = 75 # seconds
|
TIMEOUT = 75 # seconds
|
||||||
|
|
||||||
def __init__(self, *args):
|
def __init__(self, gui, *args):
|
||||||
QDialog.__init__(self, *args)
|
QDialog.__init__(self, *args)
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
|
|
||||||
|
self.gui = gui
|
||||||
|
|
||||||
self.store_plugins = {}
|
self.store_plugins = {}
|
||||||
self.running_threads = []
|
self.running_threads = []
|
||||||
self.results = Queue()
|
self.results = Queue()
|
||||||
@ -96,7 +98,7 @@ class SearchDialog(QDialog, Ui_Dialog):
|
|||||||
|
|
||||||
def open_store(self, index):
|
def open_store(self, index):
|
||||||
result = self.results_view.model().get_result(index)
|
result = self.results_view.model().get_result(index)
|
||||||
self.store_plugins[result.store].open(self, result.item_data)
|
self.store_plugins[result.store].open(self.gui, self, result.item_data)
|
||||||
|
|
||||||
|
|
||||||
class SearchThread(Thread):
|
class SearchThread(Thread):
|
||||||
|
183
src/calibre/gui2/store_download.py
Normal file
183
src/calibre/gui2/store_download.py
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
__license__ = 'GPL 3'
|
||||||
|
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
import cStringIO
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import time
|
||||||
|
from contextlib import closing
|
||||||
|
from threading import Thread
|
||||||
|
from Queue import Queue
|
||||||
|
|
||||||
|
from calibre import browser
|
||||||
|
from calibre.ebooks import BOOK_EXTENSIONS
|
||||||
|
from calibre.gui2 import Dispatcher
|
||||||
|
from calibre.ptempfile import PersistentTemporaryFile
|
||||||
|
from calibre.utils.ipc.job import BaseJob
|
||||||
|
|
||||||
|
class StoreDownloadJob(BaseJob):
|
||||||
|
|
||||||
|
def __init__(self, callback, description, job_manager, db, url='', save_as_loc='', add_to_lib=True):
|
||||||
|
BaseJob.__init__(self, description)
|
||||||
|
self.exception = None
|
||||||
|
self.job_manager = job_manager
|
||||||
|
self.db = db
|
||||||
|
self.args = (url, save_as_loc, add_to_lib)
|
||||||
|
self.tmp_file_name = ''
|
||||||
|
self.callback = callback
|
||||||
|
self.log_path = None
|
||||||
|
self._log_file = cStringIO.StringIO()
|
||||||
|
self._log_file.write(self.description.encode('utf-8') + '\n')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def log_file(self):
|
||||||
|
if self.log_path is not None:
|
||||||
|
return open(self.log_path, 'rb')
|
||||||
|
return cStringIO.StringIO(self._log_file.getvalue())
|
||||||
|
|
||||||
|
def start_work(self):
|
||||||
|
self.start_time = time.time()
|
||||||
|
self.job_manager.changed_queue.put(self)
|
||||||
|
|
||||||
|
def job_done(self):
|
||||||
|
self.duration = time.time() - self.start_time
|
||||||
|
self.percent = 1
|
||||||
|
# Dump log onto disk
|
||||||
|
lf = PersistentTemporaryFile('store_log')
|
||||||
|
lf.write(self._log_file.getvalue())
|
||||||
|
lf.close()
|
||||||
|
self.log_path = lf.name
|
||||||
|
self._log_file.close()
|
||||||
|
self._log_file = None
|
||||||
|
|
||||||
|
self.job_manager.changed_queue.put(self)
|
||||||
|
|
||||||
|
def log_write(self, what):
|
||||||
|
self._log_file.write(what)
|
||||||
|
|
||||||
|
class StoreDownloader(Thread):
|
||||||
|
|
||||||
|
def __init__(self, job_manager):
|
||||||
|
Thread.__init__(self)
|
||||||
|
self.daemon = True
|
||||||
|
self.jobs = Queue()
|
||||||
|
self.job_manager = job_manager
|
||||||
|
self._run = True
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
self._run = False
|
||||||
|
self.jobs.put(None)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
while self._run:
|
||||||
|
try:
|
||||||
|
job = self.jobs.get()
|
||||||
|
except:
|
||||||
|
break
|
||||||
|
if job is None or not self._run:
|
||||||
|
break
|
||||||
|
|
||||||
|
failed, exc = False, None
|
||||||
|
job.start_work()
|
||||||
|
if job.kill_on_start:
|
||||||
|
job.log_write('Aborted\n')
|
||||||
|
job.failed = failed
|
||||||
|
job.killed = True
|
||||||
|
job.job_done()
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
self._download(job)
|
||||||
|
self._add(job)
|
||||||
|
self._save_as(job)
|
||||||
|
break
|
||||||
|
except Exception, e:
|
||||||
|
if not self._run:
|
||||||
|
return
|
||||||
|
import traceback
|
||||||
|
failed = True
|
||||||
|
exc = e
|
||||||
|
job.log_write('\nSending failed...\n')
|
||||||
|
job.log_write(traceback.format_exc())
|
||||||
|
|
||||||
|
if not self._run:
|
||||||
|
break
|
||||||
|
|
||||||
|
job.failed = failed
|
||||||
|
job.exception = exc
|
||||||
|
job.job_done()
|
||||||
|
try:
|
||||||
|
job.callback(job)
|
||||||
|
except:
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
|
def _download(self, job):
|
||||||
|
url, save_loc, add_to_lib = job.args
|
||||||
|
if not url:
|
||||||
|
raise Exception(_('No file specified to download.'))
|
||||||
|
if not save_loc and not add_to_lib:
|
||||||
|
# Nothing to do.
|
||||||
|
return
|
||||||
|
|
||||||
|
br = browser()
|
||||||
|
|
||||||
|
basename = br.geturl(url).split('/')[-1]
|
||||||
|
ext = os.path.splitext(basename)[1][1:].lower()
|
||||||
|
if ext not in BOOK_EXTENSIONS:
|
||||||
|
raise Exception(_('Not a valid ebook format.'))
|
||||||
|
|
||||||
|
tf = PersistentTemporaryFile(suffix=basename)
|
||||||
|
with closing(br.urlopen(url)) as f:
|
||||||
|
tf.write(f.read())
|
||||||
|
tf.close()
|
||||||
|
job.tmp_file_name = tf.name
|
||||||
|
|
||||||
|
def _add(self, job):
|
||||||
|
url, save_loc, add_to_lib = job.args
|
||||||
|
if not add_to_lib and job.tmp_file_name:
|
||||||
|
return
|
||||||
|
|
||||||
|
ext = os.path.splitext(job.tmp_file_name)[1:]
|
||||||
|
|
||||||
|
from calibre.ebooks.metadata.meta import get_metadata
|
||||||
|
with open(job.tmp_file_name) as f:
|
||||||
|
mi = get_metadata(f, ext)
|
||||||
|
|
||||||
|
job.db.add_books([job.tmp_file_name], [ext], [mi])
|
||||||
|
|
||||||
|
def _save_as(self, job):
|
||||||
|
url, save_loc, add_to_lib = job.args
|
||||||
|
if not save_loc and job.tmp_fie_name:
|
||||||
|
return
|
||||||
|
|
||||||
|
shutil.copy(job.tmp_fie_name, save_loc)
|
||||||
|
|
||||||
|
def download_from_store(self, callback, db, url='', save_as_loc='', add_to_lib=True):
|
||||||
|
description = _('Downloading %s') % url
|
||||||
|
job = StoreDownloadJob(callback, description, job_manager, db, url, save_as_loc, add_to_lib)
|
||||||
|
self.job_manager.add_job(job)
|
||||||
|
self.jobs.put(job)
|
||||||
|
|
||||||
|
|
||||||
|
class StoreDownloadMixin(object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.store_downloader = StoreDownloader(self.job_manager)
|
||||||
|
self.store_downloader.start()
|
||||||
|
|
||||||
|
def download_from_store(self, url='', save_as_loc='', add_to_lib=True):
|
||||||
|
self.store_downloader.download_from_store(Dispatcher(self.downloaded_from_store), self.library_view.model().db, url, save_as_loc, add_to_lib)
|
||||||
|
self.status_bar.show_message(_('Downloading') + ' ' + url, 3000)
|
||||||
|
|
||||||
|
def downloaded_from_store(self, job):
|
||||||
|
if job.failed:
|
||||||
|
self.job_exception(job, dialog_title=_('Failed to download book'))
|
||||||
|
return
|
||||||
|
|
||||||
|
self.status_bar.show_message(job.description + ' ' + _('finished'), 5000)
|
||||||
|
|
||||||
|
|
@ -33,6 +33,7 @@ from calibre.gui2.main_window import MainWindow
|
|||||||
from calibre.gui2.layout import MainWindowMixin
|
from calibre.gui2.layout import MainWindowMixin
|
||||||
from calibre.gui2.device import DeviceMixin
|
from calibre.gui2.device import DeviceMixin
|
||||||
from calibre.gui2.email import EmailMixin
|
from calibre.gui2.email import EmailMixin
|
||||||
|
from calibre.gui2.store_download import StoreDownloadMixin
|
||||||
from calibre.gui2.jobs import JobManager, JobsDialog, JobsButton
|
from calibre.gui2.jobs import JobManager, JobsDialog, JobsButton
|
||||||
from calibre.gui2.init import LibraryViewMixin, LayoutMixin
|
from calibre.gui2.init import LibraryViewMixin, LayoutMixin
|
||||||
from calibre.gui2.search_box import SearchBoxMixin, SavedSearchBoxMixin
|
from calibre.gui2.search_box import SearchBoxMixin, SavedSearchBoxMixin
|
||||||
@ -89,7 +90,8 @@ class SystemTrayIcon(QSystemTrayIcon): # {{{
|
|||||||
|
|
||||||
class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
||||||
TagBrowserMixin, CoverFlowMixin, LibraryViewMixin, SearchBoxMixin,
|
TagBrowserMixin, CoverFlowMixin, LibraryViewMixin, SearchBoxMixin,
|
||||||
SavedSearchBoxMixin, SearchRestrictionMixin, LayoutMixin, UpdateMixin
|
SavedSearchBoxMixin, SearchRestrictionMixin, LayoutMixin, UpdateMixin,
|
||||||
|
StoreDownloadMixin
|
||||||
):
|
):
|
||||||
'The main GUI'
|
'The main GUI'
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user