2
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
3
import os, traceback, Queue, time
4
from threading import Thread
6
from calibre.devices import devices
7
from calibre.parallel import Job
8
from calibre.devices.scanner import DeviceScanner
13
def __init__(self, func, *args, **kwargs):
14
Job.__init__(self, *args, **kwargs)
20
self.result = self.func(*self.args, **self.kwargs)
21
except (Exception, SystemExit), err:
23
self.traceback = traceback.format_exc()
28
class DeviceManager(Thread):
30
Worker thread that polls the USB ports for devices. Emits the
31
signal connected(PyQt_PyObject, PyQt_PyObject) on connection and
34
def __init__(self, connected_slot, job_manager, sleep_time=2):
36
@param sleep_time: Time to sleep between device probes in millisecs
37
@type sleep_time: integer
41
self.devices = [[d, False] for d in devices()]
43
self.device_class = None
44
self.sleep_time = sleep_time
45
self.connected_slot = connected_slot
46
self.jobs = Queue.Queue(0)
47
self.keep_going = True
48
self.job_manager = job_manager
49
self.current_job = None
50
self.scanner = DeviceScanner()
52
def detect_device(self):
54
for device in self.devices:
55
connected = self.scanner.is_device_connected(device[0])
56
if connected and not device[1]:
61
self.device_class = dev.__class__
62
self.connected_slot(True)
64
print 'Unable to open device'
68
elif not connected and device[1]:
71
job = self.jobs.get_nowait()
72
job.abort(Exception(_('Device no longer connected.')))
76
self.connected_slot(False)
80
if not self.jobs.empty():
82
return self.jobs.get_nowait()
87
while self.keep_going:
92
self.current_job = job
93
self.device.set_progress_reporter(job.update_status)
94
self.current_job.run()
95
self.current_job = None
98
time.sleep(self.sleep_time)
100
def create_job(self, func, done, description, args=[], kwargs={}):
101
job = DeviceJob(func, done, self.job_manager,
102
args=args, kwargs=kwargs, description=description)
103
self.job_manager.add_job(job)
107
def _get_device_information(self):
108
info = self.device.get_device_information(end_session=False)
109
info = [i.replace('\x00', '').replace('\x01', '') for i in info]
110
cp = self.device.card_prefix(end_session=False)
111
fs = self.device.free_space()
114
def get_device_information(self, done):
115
'''Get device information and free space on device'''
116
return self.create_job(self._get_device_information, done,
117
description=_('Get device information'))
121
'''Get metadata from device'''
122
mainlist = self.device.books(oncard=False, end_session=False)
123
cardlist = self.device.books(oncard=True)
124
return (mainlist, cardlist)
126
def books(self, done):
127
'''Return callable that returns the list of books on device as two booklists'''
128
return self.create_job(self._books, done, description=_('Get list of books on device'))
130
def _sync_booklists(self, booklists):
131
'''Sync metadata to device'''
132
self.device.sync_booklists(booklists, end_session=False)
133
return self.device.card_prefix(end_session=False), self.device.free_space()
135
def sync_booklists(self, done, booklists):
136
return self.create_job(self._sync_booklists, done, args=[booklists],
137
description=_('Send metadata to device'))
139
def _upload_books(self, files, names, on_card=False):
140
'''Upload books to device: '''
141
return self.device.upload_books(files, names, on_card, end_session=False)
143
def upload_books(self, done, files, names, on_card=False, titles=None):
144
desc = _('Upload %d books to device')%len(names)
146
desc += u':' + u', '.join(titles)
147
return self.create_job(self._upload_books, done, args=[files, names],
148
kwargs={'on_card':on_card}, description=desc)
150
def add_books_to_metadata(self, locations, metadata, booklists):
151
self.device.add_books_to_metadata(locations, metadata, booklists)
153
def _delete_books(self, paths):
154
'''Remove books from device'''
155
self.device.delete_books(paths, end_session=True)
157
def delete_books(self, done, paths):
158
return self.create_job(self._delete_books, done, args=[paths],
159
description=_('Delete books from device'))
161
def remove_books_from_metadata(self, paths, booklists):
162
self.device.remove_books_from_metadata(paths, booklists)
164
def _save_books(self, paths, target):
165
'''Copy books from device to disk'''
167
name = path.rpartition('/')[2]
168
f = open(os.path.join(target, name), 'wb')
169
self.device.get_file(path, f)
172
def save_books(self, done, paths, target):
173
return self.create_job(self._save_books, done, args=[paths, target],
174
description=_('Download books from device'))
176
def _view_book(self, path, target):
177
f = open(target, 'wb')
178
self.device.get_file(path, f)
182
def view_book(self, done, path, target):
183
return self.create_job(self._view_book, done, args=[path, target],
184
description=_('View book on device'))
b'\\ No newline at end of file'