def _process_sidecar_upload(device, book_ids, book_nodes): # book_ids is NOT a complete list of books on device for asin in book_ids: book = calibre.book(asin) if book: book.mark_on_device(device) for x_book in book_nodes: asin = x_book.getAttribute('key') book = calibre.book(asin) if not book: logging.warn("sidecar upload for unknown book %s", asin) continue for kind in ('last_read', 'bookmark', 'highlight', 'note'): for x_item in qxml.iter_children(x_book, kind): timestamp = x_item.getAttribute('timestamp') or None begin = x_item.getAttribute('begin') or None end = x_item.getAttribute('end') or begin pos = x_item.getAttribute('pos') or None state = x_item.getAttribute('state') or None text = qxml.get_text(x_item) if kind == 'note' else None if kind == 'last_read': annotations.set_last_read(device, asin, timestamp, begin, pos, state) else: action = x_item.getAttribute('action') if action == 'create': annotations.create(device, asin, kind, timestamp, begin, end, pos, state, text) elif action == 'delete': annotations.delete(device, asin, kind, timestamp, begin, end) elif action == 'modify': annotations.modify(device, asin, kind, timestamp, begin, end, text) else: logging.error("unknown sidecar action %s: %s", action, x_item)
def _process_books_on_device(device, book_ids): # need to better express this, without exposing device.books for asin, last_downloaded in list(device.books.items()): if asin not in book_ids and last_downloaded > 0: # we know snapshot lists ALL books on the device logging.warn("%s no longer has book %s %s", device, asin, calibre.book(asin)) device.books[asin] = 0 for asin in book_ids: book = calibre.book(asin) if book: book.mark_on_device(device) else: logging.debug("%s has book unknown book %s", device, asin)
def call(self, request, device): if device.is_provisional(): return None if request.command == 'GET': q = request.get_query_params() asin = q.get('key') if is_uuid(asin, q.get('type')): book = calibre.book(asin) if not book: logging.warn("tried to download sidecar for unknown book %s", asin) return None sidecar_data = formats.sidecar(book) if sidecar_data: content_type, data = sidecar_data return DummyResponse(headers = { 'Content-Type': content_type }, data = data) return None elif request.command == 'POST': q = request.get_query_params() lto = q.get('device_lto', -1) if lto != -1: try: device.lto = int(lto) except: pass with minidom.parseString(request.body_text()) as doc: if _process_xml(request, doc, device): if not request.is_signed(): # drats xml = doc.toxml('UTF-8') request.update_body(xml) return self.call_upstream(request, device)
def _collections(asin_mappings): collections = {} for name, asin_list in asin_mappings.items(): books_list = [calibre.book(asin) for asin in asin_list] books_list = [b for b in books_list if b] books_with_files = [b for b in books_list if b.file_path] if books_with_files: # if at least _some_ books in the collection have files collections[name] = books_list return collections
def _collections(asin_mappings): collections = {} for name, asin_list in asin_mappings.items(): books_list = [ calibre.book(asin) for asin in asin_list ] books_list = [ b for b in books_list if b ] books_with_files = [ b for b in books_list if b.file_path ] if books_with_files: # if at least _some_ books in the collection have files collections[name] = books_list return collections
def _process_item(device, action=None, cde_type=None, key=None, complete_status=None, **extra): if key.startswith('KSP.') or cde_type.startswith('KSP.'): # KSP internal stuff, not relevant upstream return True if not features.allow_logs_upload and action == 'SND' and cde_type == 'CMND' and key.endswith( ':SYSLOG:UPLOAD'): return True if action in ('GET', 'DOWNLOAD') and cde_type in ('EBOK', 'PDOC', 'APNX') and is_uuid( key, cde_type): book = calibre.book(key) if complete_status == 'COMPLETED': if book: book.mark_downloaded_by(device) else: logging.warn("%s successfully updated unknown book %s", device, key) elif complete_status == 'FAILED': logging.warn("%s failed to update book %s", device, book or key) else: logging.warn("%s: unknown downloaded status %s for %s", device, complete_status, book or key) return True if action == 'UPD_LPRD' and is_uuid(key, cde_type): if complete_status == 'COMPLETED': annotations.last_read_updated(device, key) elif complete_status == 'FAILED': logging.warn("%s failed to update last_read for book %s", device, key) else: logging.warn("%s: unknown UPD_LPRD status %s for book %s", device, complete_status, key) return True if action == 'UPD_ANOT' and is_uuid(key, cde_type): if complete_status == 'COMPLETED': annotations.annotations_updated(device, key) elif complete_status == 'FAILED': logging.warn("%s failed to update last_read for book %s", device, key) else: logging.warn("%s: unknown UPD_LPRD status %s for book %s", device, complete_status, key) return True return False
def book_response(self, asin, device, range_header): """ builds a BookResponse object for downloading the book contents """ book = calibre.book(asin, True) if not book: logging.warn("device %s tried to download book %s, but it is not in the library (anymore?)", device, asin) return None if not book.file_path: logging.warn("device %s tried to download book %s, but it has no file available", device, asin) return None bytes_range = _range(range_header, book.file_size) return _BookResponse(book, bytes_range)
def call(self, request, device): q = request.get_query_params() asin = q.get('key') if q.get('type') == 'EBOK' and is_uuid(asin, 'EBOK'): book = calibre.book(asin) apnx_path = annotations.apnx_path(book) # logging.debug("looking for apnx of %s, found %s", book, apnx_path) if apnx_path: # APNX files are usually small (a few Ks at most), # so there's no need to do stream copying apnx_data = None with open(apnx_path, 'rb') as apnx: apnx_data = apnx.read() if apnx_data: return DummyResponse(headers=_HEADERS, data=apnx_data)
def call(self, request, device): q = request.get_query_params() asin = q.get('key') if is_uuid(asin, q.get('type')): kind = q.get('filter') book = calibre.book(asin) if not book: logging.warn("book not found %s", asin) return None if kind == 'last_read': return _last_read(book, device.serial) logging.warn("don't know how to filter annotations of kind %s", kind) return None return self.call_upstream(request, device)
def call(self, request, device): if request.path.startswith('/images/P/'): asin = asin[10:asin.find('.', 11)] if is_uuid(asin): book = calibre.book(asin) # logging.debug("looking for cover of %s %s", asin, book) if book and book.file_path: image_path = os.path.join(os.path.dirname(book.file_path), "cover.jpg") image_data = None if os.path.isfile(image_path): try: with open(image_path, 'rb') as fi: image_data = fi.read() except: logging.exception("failed to read cover for %s", book) if image_data: return DummyResponse(headers = _HEADERS, data = image_data) return 404 # forces Kindle 4 PC to use the cover from the .mobi file, if any return DummyResponse(303, headers = { 'Location: http://' + _ECX + request.path })
def _process_item(device, action = None, cde_type = None, key = None, complete_status = None, **extra): if key.startswith('KSP.') or cde_type.startswith('KSP.'): # KSP internal stuff, not relevant upstream return True if not features.allow_logs_upload and action == 'SND' and cde_type == 'CMND' and key.endswith(':SYSLOG:UPLOAD'): return True if action in ('GET', 'DOWNLOAD') and cde_type in ('EBOK', 'PDOC', 'APNX') and is_uuid(key, cde_type): book = calibre.book(key) if complete_status == 'COMPLETED': if book: book.mark_downloaded_by(device) else: logging.warn("%s successfully updated unknown book %s", device, key) elif complete_status == 'FAILED': logging.warn("%s failed to update book %s", device, book or key) else: logging.warn("%s: unknown downloaded status %s for %s", device, complete_status, book or key) return True if action == 'UPD_LPRD' and is_uuid(key, cde_type): if complete_status == 'COMPLETED': annotations.last_read_updated(device, key) elif complete_status == 'FAILED': logging.warn("%s failed to update last_read for book %s", device, key) else: logging.warn("%s: unknown UPD_LPRD status %s for book %s", device, complete_status, key) return True if action == 'UPD_ANOT' and is_uuid(key, cde_type): if complete_status == 'COMPLETED': annotations.annotations_updated(device, key) elif complete_status == 'FAILED': logging.warn("%s failed to update last_read for book %s", device, key) else: logging.warn("%s: unknown UPD_LPRD status %s for book %s", device, complete_status, key) return True return False
def call(self, request, device): if request.path.startswith('/images/P/'): asin = request.path[10:] asin = asin[:asin.find('.', 11)] if is_uuid(asin): book = calibre.book(asin) # logging.debug("looking for cover of %s %s", asin, book) if book and book.file_path: image_path = os.path.join(os.path.dirname(book.file_path), "cover.jpg") image_data = None if os.path.isfile(image_path): try: with open(image_path, 'rb') as fi: image_data = fi.read() except: logging.exception("failed to read cover for %s", book) if image_data: return DummyResponse(headers=_HEADERS, data=image_data) return 404 # forces Kindle 4 PC to use the cover from the .mobi file, if any return DummyResponse( 303, headers={'Location: http://' + _ECX + request.path})