Remove Bambook driver and profile

The driver has not worked for years (it depended on a third party
library that is not shipped with calibre anymore)
This commit is contained in:
Kovid Goyal 2015-12-07 12:32:23 +05:30
parent ae03e0265f
commit 0b1e77dd41
8 changed files with 2 additions and 1120 deletions

View File

@ -687,7 +687,6 @@ from calibre.devices.misc import (
COBY, EX124G, WAYTEQ, WOXTER, POCKETBOOK626, SONYDPTS1)
from calibre.devices.folder_device.driver import FOLDER_DEVICE_FOR_CONFIG
from calibre.devices.kobo.driver import KOBO, KOBOTOUCH
from calibre.devices.bambook.driver import BAMBOOK
from calibre.devices.boeye.driver import BOEYE_BEX, BOEYE_BDX
from calibre.devices.smart_device_app.driver import SMART_DEVICE_APP
from calibre.devices.mtp.driver import MTP_DEVICE
@ -752,7 +751,6 @@ plugins += [
PDNOVEL_KOBO,
LUMIREAD,
ALURATEK_COLOR,
BAMBOOK,
TREKSTOR,
EEEREADER,
NEXTBOOK,

View File

@ -776,21 +776,6 @@ class NookColorOutput(NookOutput):
comic_screen_size = (594, 900)
dpi = 169
class BambookOutput(OutputProfile):
author = 'Li Fanxi'
name = 'Sanda Bambook'
short_name = 'bambook'
description = _('This profile is intended for the Sanda Bambook.')
# Screen size is for full screen display
screen_size = (580, 780)
# Comic size is for normal display
comic_screen_size = (540, 700)
dpi = 168.451
fbase = 12
fsizes = [10, 12, 14, 16]
class PocketBook900Output(OutputProfile):
author = 'Chris Lockfort'
@ -821,7 +806,7 @@ output_profiles = [
iPad3Output, KoboReaderOutput, TabletOutput, SamsungGalaxy,
SonyReaderLandscapeOutput, KindleDXOutput, IlliadOutput, NookHD,
IRexDR1000Output, IRexDR800Output, JetBook5Output, NookOutput,
BambookOutput, NookColorOutput, PocketBook900Output,
NookColorOutput, PocketBook900Output,
PocketBookPro912Output, GenericEink, GenericEinkLarge, GenericEinkHD,
KindleFireOutput, KindlePaperWhiteOutput, KindleVoyageOutput,
KindlePaperWhite3Output

View File

@ -1,544 +0,0 @@
# -*- coding: utf-8 -*-
__license__ = 'GPL v3'
__copyright__ = '2010, Li Fanxi <lifanxi at freemindworld.com>'
__docformat__ = 'restructuredtext en'
'''
Device driver for Sanda's Bambook
'''
import time, os, hashlib, shutil
from itertools import cycle
from calibre.devices.interface import DevicePlugin
from calibre.devices.usbms.deviceconfig import DeviceConfig
from calibre.devices.bambook.libbambookcore import Bambook, text_encoding, CONN_CONNECTED, is_bambook_lib_ready
from calibre.devices.usbms.books import Book, BookList
from calibre.ebooks.metadata.book.json_codec import JsonCodec
from calibre.ptempfile import TemporaryDirectory, TemporaryFile
from calibre.constants import __appname__, __version__
from calibre.devices.errors import OpenFeedback
class BAMBOOK(DeviceConfig, DevicePlugin):
name = 'Bambook Device Interface'
description = _('Communicate with the Sanda Bambook eBook reader.')
author = _('Li Fanxi')
supported_platforms = ['windows', 'linux', 'osx']
log_packets = False
booklist_class = BookList
book_class = Book
ip = None
FORMATS = [ "snb", "pdf" ]
USER_CAN_ADD_NEW_FORMATS = False
VENDOR_ID = 0x230b
PRODUCT_ID = 0x0001
BCD = None
CAN_SET_METADATA = False
THUMBNAIL_HEIGHT = 155
EXTRA_CUSTOMIZATION_MESSAGE = \
_("Device IP Address (restart calibre after changing)")
icon = I("devices/bambook.png")
# OPEN_FEEDBACK_MESSAGE = _(
# 'Connecting to Bambook device, please wait ...')
BACKLOADING_ERROR_MESSAGE = _(
'Unable to add book to library directly from Bambook. '
'Please save the book to disk and add the file to library from disk.')
METADATA_CACHE = '.calibre.bambook'
METADATA_FILE_GUID = 'calibremetadata.snb'
bambook = None
is_connected = False
def __init__(self, ip):
self.ip = ip
def reset(self, key='-1', log_packets=False, report_progress=None,
detected_device=None) :
self.open(None, None)
def open(self, connected_device, library_uuid):
# Make sure the Bambook library is ready
if not is_bambook_lib_ready():
raise OpenFeedback(_("Unable to connect to Bambook, you need to install Bambook library first."))
# Disconnect first if connected
self.eject()
# Connect
self.bambook = Bambook()
self.bambook.Connect(ip = self.ip, timeout = 10000)
if self.bambook.GetState() != CONN_CONNECTED:
self.bambook = None
raise OpenFeedback(_("Unable to connect to Bambook. \n"
"If you are trying to connect via Wi-Fi, "
"please make sure the IP address of Bambook has been correctly configured."))
self.is_connected = True
return True
def unmount_device(self):
self.eject()
def eject(self):
if self.bambook:
self.bambook.Disconnect()
self.bambook = None
self.is_connected = False
def post_yank_cleanup(self):
self.eject()
def set_progress_reporter(self, report_progress):
'''
:param report_progress: Function that is called with a % progress
(number between 0 and 100) for various tasks
If it is called with -1 that means that the
task does not have any progress information
'''
self.report_progress = report_progress
def get_device_information(self, end_session=True):
"""
Ask device for device information. See L{DeviceInfoQuery}.
:return: (device name, device version, software version on device, mime type)
"""
if self.bambook:
deviceInfo = self.bambook.GetDeviceInfo()
return (_("Bambook"), "SD928", deviceInfo.firmwareVersion, "MimeType")
def card_prefix(self, end_session=True):
'''
Return a 2 element list of the prefix to paths on the cards.
If no card is present None is set for the card's prefix.
E.G.
('/place', '/place2')
(None, 'place2')
('place', None)
(None, None)
'''
return (None, None)
def total_space(self, end_session=True):
"""
Get total space available on the mountpoints:
1. Main memory
2. Memory Card A
3. Memory Card B
:return: A 3 element list with total space in bytes of (1, 2, 3). If a
particular device doesn't have any of these locations it should return 0.
"""
deviceInfo = self.bambook.GetDeviceInfo()
return (deviceInfo.deviceVolume * 1024, 0, 0)
def free_space(self, end_session=True):
"""
Get free space available on the mountpoints:
1. Main memory
2. Card A
3. Card B
:return: A 3 element list with free space in bytes of (1, 2, 3). If a
particular device doesn't have any of these locations it should return -1.
"""
deviceInfo = self.bambook.GetDeviceInfo()
return (deviceInfo.spareVolume * 1024, -1, -1)
def books(self, oncard=None, end_session=True):
"""
Return a list of ebooks on the device.
:param oncard: If 'carda' or 'cardb' return a list of ebooks on the
specific storage card, otherwise return list of ebooks
in main memory of device. If a card is specified and no
books are on the card return empty list.
:return: A BookList.
"""
# Bambook has no memroy card
if oncard:
return self.booklist_class(None, None, None)
# Get metadata cache
prefix = ''
booklist = self.booklist_class(oncard, prefix, self.settings)
need_sync = self.parse_metadata_cache(booklist)
# Get book list from device
devicebooks = self.bambook.GetBookList()
books = []
for book in devicebooks:
if book.bookGuid == self.METADATA_FILE_GUID:
continue
b = self.book_class('', book.bookGuid)
b.title = book.bookName.decode(text_encoding)
b.authors = [ book.bookAuthor.decode(text_encoding) ]
b.size = 0
b.datatime = time.gmtime()
b.lpath = book.bookGuid
b.thumbnail = None
b.tags = None
b.comments = book.bookAbstract.decode(text_encoding)
books.append(b)
# make a dict cache of paths so the lookup in the loop below is faster.
bl_cache = {}
for idx, b in enumerate(booklist):
bl_cache[b.lpath] = idx
def update_booklist(book, prefix):
changed = False
try:
idx = bl_cache.get(book.lpath, None)
if idx is not None:
bl_cache[book.lpath] = None
if self.update_metadata_item(book, booklist[idx]):
changed = True
else:
if booklist.add_book(book,
replace_metadata=False):
changed = True
except: # Probably a filename encoding error
import traceback
traceback.print_exc()
return changed
# Check each book on device whether it has a correspondig item
# in metadata cache. If not, add it to cache.
for i, book in enumerate(books):
self.report_progress(i/float(len(books)), _('Getting list of books on device...'))
changed = update_booklist(book, prefix)
if changed:
need_sync = True
# Remove books that are no longer in the Bambook. Cache contains
# indices into the booklist if book not in filesystem, None otherwise
# Do the operation in reverse order so indices remain valid
for idx in sorted(bl_cache.itervalues(), reverse=True):
if idx is not None:
need_sync = True
del booklist[idx]
if need_sync:
self.sync_booklists((booklist, None, None))
self.report_progress(1.0, _('Getting list of books on device...'))
return booklist
def upload_books(self, files, names, on_card=None, end_session=True,
metadata=None):
'''
Upload a list of books to the device. If a file already
exists on the device, it should be replaced.
This method should raise a :class:`FreeSpaceError` if there is not enough
free space on the device. The text of the FreeSpaceError must contain the
word "card" if ``on_card`` is not None otherwise it must contain the word "memory".
:param files: A list of paths and/or file-like objects. If they are paths and
the paths point to temporary files, they may have an additional
attribute, original_file_path pointing to the originals. They may have
another optional attribute, deleted_after_upload which if True means
that the file pointed to by original_file_path will be deleted after
being uploaded to the device.
:param names: A list of file names that the books should have
once uploaded to the device. len(names) == len(files)
:param metadata: If not None, it is a list of :class:`Metadata` objects.
The idea is to use the metadata to determine where on the device to
put the book. len(metadata) == len(files). Apart from the regular
cover (path to cover), there may also be a thumbnail attribute, which should
be used in preference. The thumbnail attribute is of the form
(width, height, cover_data as jpeg).
:return: A list of 3-element tuples. The list is meant to be passed
to :meth:`add_books_to_metadata`.
'''
self.report_progress(0, _('Transferring books to device...'))
paths = []
if self.bambook:
for (i, f) in enumerate(files):
self.report_progress((i+1) / float(len(files)), _('Transferring books to device...'))
if not hasattr(f, 'read'):
# Handle PDF File
if f[-3:].upper() == "PDF":
# Package the PDF file
with TemporaryDirectory() as tdir:
snbcdir = os.path.join(tdir, 'snbc')
snbfdir = os.path.join(tdir, 'snbf')
os.mkdir(snbcdir)
os.mkdir(snbfdir)
tmpfile = open(os.path.join(snbfdir, 'book.snbf'), 'wb')
tmpfile.write('''<book-snbf version="1.0">
<head>
<name><![CDATA[''' + metadata[i].title + ''']]></name>
<author><![CDATA[''' + ' '.join(metadata[i].authors) + ''']]></author>
<language>ZH-CN</language>
<rights/>
<publisher>calibre</publisher>
<generator>''' + __appname__ + ' ' + __version__ + '''</generator>
<created/>
<abstract></abstract>
<cover/>
</head>
</book-snbf>
''')
tmpfile.close()
tmpfile = open(os.path.join(snbfdir, 'toc.snbf'), 'wb')
tmpfile.write('''<toc-snbf>
<head>
<chapters>1</chapters>
</head>
<body>
<chapter src="pdf1.pdf"><![CDATA[''' + metadata[i].title + ''']]></chapter>
</body>
</toc-snbf>
''');
tmpfile.close()
pdf_name = os.path.join(snbcdir, "pdf1.pdf")
shutil.copyfile(f, pdf_name)
with TemporaryFile('.snb') as snbfile:
if self.bambook.PackageSNB(snbfile, tdir) and self.bambook.VerifySNB(snbfile):
guid = self.bambook.SendFile(snbfile, self.get_guid(metadata[i].uuid))
elif f[-3:].upper() == 'SNB':
if self.bambook.VerifySNB(f):
guid = self.bambook.SendFile(f, self.get_guid(metadata[i].uuid))
else:
print "book invalid"
if guid:
paths.append(guid)
else:
print "Send fail"
ret = zip(paths, cycle([on_card]))
self.report_progress(1.0, _('Transferring books to device...'))
return ret
def add_books_to_metadata(self, locations, metadata, booklists):
metadata = iter(metadata)
for i, location in enumerate(locations):
self.report_progress((i+1) / float(len(locations)), _('Adding books to device metadata listing...'))
info = metadata.next()
# Extract the correct prefix from the pathname. To do this correctly,
# we must ensure that both the prefix and the path are normalized
# so that the comparison will work. Book's __init__ will fix up
# lpath, so we don't need to worry about that here.
book = self.book_class('', location[0], other=info)
if book.size is None:
book.size = 0
b = booklists[0].add_book(book, replace_metadata=True)
if b:
b._new_book = True
self.report_progress(1.0, _('Adding books to device metadata listing...'))
def delete_books(self, paths, end_session=True):
'''
Delete books at paths on device.
'''
if self.bambook:
for i, path in enumerate(paths):
self.report_progress((i+1) / float(len(paths)), _('Removing books from device...'))
self.bambook.DeleteFile(path)
self.report_progress(1.0, _('Removing books from device...'))
def remove_books_from_metadata(self, paths, booklists):
'''
Remove books from the metadata list. This function must not communicate
with the device.
:param paths: paths to books on the device.
:param booklists: A tuple containing the result of calls to
(:meth:`books(oncard=None)`,
:meth:`books(oncard='carda')`,
:meth`books(oncard='cardb')`).
'''
for i, path in enumerate(paths):
self.report_progress((i+1) / float(len(paths)), _('Removing books from device metadata listing...'))
for bl in booklists:
for book in bl:
if book.lpath == path:
bl.remove_book(book)
self.report_progress(1.0, _('Removing books from device metadata listing...'))
def sync_booklists(self, booklists, end_session=True):
'''
Update metadata on device.
:param booklists: A tuple containing the result of calls to
(:meth:`books(oncard=None)`,
:meth:`books(oncard='carda')`,
:meth`books(oncard='cardb')`).
'''
if not self.bambook:
return
json_codec = JsonCodec()
# Create stub virtual book for sync info
with TemporaryDirectory() as tdir:
snbcdir = os.path.join(tdir, 'snbc')
snbfdir = os.path.join(tdir, 'snbf')
os.mkdir(snbcdir)
os.mkdir(snbfdir)
f = open(os.path.join(snbfdir, 'book.snbf'), 'wb')
f.write('''<book-snbf version="1.0">
<head>
<name>calibre同步信息</name>
<author>calibre</author>
<language>ZH-CN</language>
<rights/>
<publisher>calibre</publisher>
<generator>''' + __appname__ + ' ' + __version__ + '''</generator>
<created/>
<abstract></abstract>
<cover/>
</head>
</book-snbf>
''')
f.close()
f = open(os.path.join(snbfdir, 'toc.snbf'), 'wb')
f.write('''<toc-snbf>
<head>
<chapters>0</chapters>
</head>
<body>
</body>
</toc-snbf>
''');
f.close()
cache_name = os.path.join(snbcdir, self.METADATA_CACHE)
with open(cache_name, 'wb') as f:
json_codec.encode_to_file(f, booklists[0])
with TemporaryFile('.snb') as f:
if self.bambook.PackageSNB(f, tdir):
if not self.bambook.SendFile(f, self.METADATA_FILE_GUID):
print "Upload failed"
else:
print "Package failed"
# Clear the _new_book indication, as we are supposed to be done with
# adding books at this point
for blist in booklists:
if blist is not None:
for book in blist:
book._new_book = False
self.report_progress(1.0, _('Sending metadata to device...'))
def get_file(self, path, outfile, end_session=True):
'''
Read the file at ``path`` on the device and write it to outfile.
:param outfile: file object like ``sys.stdout`` or the result of an
:func:`open` call.
'''
if self.bambook:
with TemporaryDirectory() as tdir:
if self.bambook.GetFile(path, tdir):
filepath = os.path.join(tdir, path)
f = file(filepath, 'rb')
outfile.write(f.read())
f.close()
else:
print "Unable to get file from Bambook:", path
@classmethod
def config_widget(cls):
'''
Should return a QWidget. The QWidget contains the settings for the device interface
'''
from calibre.gui2.device_drivers.configwidget import ConfigWidget
cw = ConfigWidget(cls.settings(), cls.FORMATS, cls.SUPPORTS_SUB_DIRS,
cls.MUST_READ_METADATA, cls.SUPPORTS_USE_AUTHOR_SORT,
cls.EXTRA_CUSTOMIZATION_MESSAGE, cls)
# Turn off the Save template
cw.opt_save_template.setVisible(False)
cw.label.setVisible(False)
# Repurpose the metadata checkbox
cw.opt_read_metadata.setVisible(False)
# Repurpose the use_subdirs checkbox
cw.opt_use_subdirs.setVisible(False)
return cw
# @classmethod
# def save_settings(cls, settings_widget):
# '''
# Should save settings to disk. Takes the widget created in
# :meth:`config_widget` and saves all settings to disk.
# '''
# raise NotImplementedError()
# @classmethod
# def settings(cls):
# '''
# Should return an opts object. The opts object should have at least one attribute
# `format_map` which is an ordered list of formats for the device.
# '''
# raise NotImplementedError()
def parse_metadata_cache(self, bl):
need_sync = True
if not self.bambook:
return need_sync
# Get the metadata virtual book from Bambook
with TemporaryDirectory() as tdir:
if self.bambook.GetFile(self.METADATA_FILE_GUID, tdir):
cache_name = os.path.join(tdir, self.METADATA_CACHE)
if self.bambook.ExtractSNBContent(os.path.join(tdir, self.METADATA_FILE_GUID),
'snbc/' + self.METADATA_CACHE,
cache_name):
json_codec = JsonCodec()
if os.access(cache_name, os.R_OK):
try:
with open(cache_name, 'rb') as f:
json_codec.decode_from_file(f, bl, self.book_class, '')
need_sync = False
except:
import traceback
traceback.print_exc()
bl = []
return need_sync
@classmethod
def update_metadata_item(cls, book, blb):
# Currently, we do not have enough information
# from Bambook SDK to judge whether a book has
# been changed, we assume all books has been
# changed.
changed = True
# if book.bookName.decode(text_encoding) != blb.title:
# changed = True
# if book.bookAuthor.decode(text_encoding) != blb.authors[0]:
# changed = True
# if book.bookAbstract.decode(text_encoding) != blb.comments:
# changed = True
return changed
@staticmethod
def get_guid(uuid):
guid = hashlib.md5(uuid).hexdigest()[0:15] + ".snb"
return guid
class BAMBOOKWifi(BAMBOOK):
def is_usb_connected(self, devices_on_system, debug=False,
only_presence=False):
return self.is_connected, self

View File

@ -1,530 +0,0 @@
# -*- coding: utf-8 -*-
__license__ = 'GPL v3'
__copyright__ = '2010, Li Fanxi <lifanxi at freemindworld.com>'
__docformat__ = 'restructuredtext en'
'''
Sanda library wrapper
'''
import ctypes, hashlib, os, sys
from threading import Event, Lock
from calibre.constants import iswindows
from calibre import load_library
try:
_lib_name = 'libBambookCore'
cdll = ctypes.cdll
if iswindows:
_lib_name = 'BambookCore'
if hasattr(sys, 'frozen') and iswindows:
lp = os.path.join(os.path.dirname(sys.executable), 'DLLs', 'BambookCore.dll')
lib_handle = cdll.LoadLibrary(lp)
elif hasattr(sys, 'frozen_path'):
lp = os.path.join(sys.frozen_path, 'lib', 'libBambookCore.so')
lib_handle = cdll.LoadLibrary(lp)
else:
lib_handle = load_library(_lib_name, cdll)
except:
lib_handle = None
text_encoding = 'utf-8'
if iswindows:
text_encoding = 'mbcs'
def is_bambook_lib_ready():
return lib_handle != None
# Constant
DEFAULT_BAMBOOK_IP = '192.168.250.2'
BAMBOOK_SDK_VERSION = 0x00090000
BR_SUCC = 0 # 操作成功
BR_FAIL = 1001 # 操作失败
BR_NOT_IMPL = 1002 # 该功能还未实现
BR_DISCONNECTED = 1003 # 与设备的连接已断开
BR_PARAM_ERROR = 1004 # 调用函数传入的参数错误
BR_TIMEOUT = 1005 # 操作或通讯超时
BR_INVALID_HANDLE = 1006 # 传入的句柄无效
BR_INVALID_FILE = 1007 # 传入的文件不存在或格式无效
BR_INVALID_DIR = 1008 # 传入的目录不存在
BR_BUSY = 1010 # 设备忙,另一个操作还未完成
BR_EOF = 1011 # 文件或操作已结束
BR_IO_ERROR = 1012 # 文件读写失败
BR_FILE_NOT_INSIDE = 1013 # 指定的文件不在包里
# 当前连接状态
CONN_CONNECTED = 0 # 已连接
CONN_DISCONNECTED = 1 # 未连接或连接已断开
CONN_CONNECTING = 2 # 正在连接
CONN_WAIT_FOR_AUTH = 3 # 已连接,正在等待身份验证(暂未实现)
#传输状态
TRANS_STATUS_TRANS = 0 #正在传输
TRANS_STATUS_DONE = 1 #传输完成
TRANS_STATUS_ERR = 2 #传输出错
# Key Enums
BBKeyNum0 = 0
BBKeyNum1 = 1
BBKeyNum2 = 2
BBKeyNum3 = 3
BBKeyNum4 = 4
BBKeyNum5 = 5
BBKeyNum6 = 6
BBKeyNum7 = 7
BBKeyNum8 = 8
BBKeyNum9 = 9
BBKeyStar = 10
BBKeyCross = 11
BBKeyUp = 12
BBKeyDown = 13
BBKeyLeft = 14
BBKeyRight = 15
BBKeyPageUp = 16
BBKeyPageDown = 17
BBKeyOK = 18
BBKeyESC = 19
BBKeyBookshelf = 20
BBKeyStore = 21
BBKeyTTS = 22
BBKeyMenu = 23
BBKeyInteract =24
class DeviceInfo(ctypes.Structure):
_fields_ = [ ("cbSize", ctypes.c_int),
("sn", ctypes.c_char * 20),
("firmwareVersion", ctypes.c_char * 20),
("deviceVolume", ctypes.c_int),
("spareVolume", ctypes.c_int),
]
def __init__(self):
self.cbSize = ctypes.sizeof(self)
class PrivBookInfo(ctypes.Structure):
_fields_ = [ ("cbSize", ctypes.c_int),
("bookGuid", ctypes.c_char * 20),
("bookName", ctypes.c_char * 80),
("bookAuthor", ctypes.c_char * 40),
("bookAbstract", ctypes.c_char * 256),
]
def Clone(self):
bookInfo = PrivBookInfo()
bookInfo.cbSize = self.cbSize
bookInfo.bookGuid = self.bookGuid
bookInfo.bookName = self.bookName
bookInfo.bookAuthor = self.bookAuthor
bookInfo.bookAbstract = self.bookAbstract
return bookInfo
def __init__(self):
self.cbSize = ctypes.sizeof(self)
# extern "C"_declspec(dllexport) BB_RESULT BambookConnect(const char* lpszIP, int timeOut, BB_HANDLE* hConn);
def BambookConnect(ip = DEFAULT_BAMBOOK_IP, timeout = 0):
if isinstance(ip, unicode):
ip = ip.encode('ascii')
handle = ctypes.c_void_p(0)
if lib_handle == None:
raise Exception(_('Bambook SDK has not been installed.'))
ret = lib_handle.BambookConnect(ip, timeout, ctypes.byref(handle))
if ret == BR_SUCC:
return handle
else:
return None
# extern "C" _declspec(dllexport) BB_RESULT BambookGetConnectStatus(BB_HANDLE hConn, int* status);
def BambookGetConnectStatus(handle):
status = ctypes.c_int(0)
ret = lib_handle.BambookGetConnectStatus(handle, ctypes.byref(status))
if ret == BR_SUCC:
return status.value
else:
return None
# extern "C" _declspec(dllexport) BB_RESULT BambookDisconnect(BB_HANDLE hConn);
def BambookDisconnect(handle):
ret = lib_handle.BambookDisconnect(handle)
if ret == BR_SUCC:
return True
else:
return False
# extern "C" const char * BambookGetErrorString(BB_RESULT nCode)
def BambookGetErrorString(code):
func = lib_handle.BambookGetErrorString
func.restype = ctypes.c_char_p
return func(code)
# extern "C" BB_RESULT BambookGetSDKVersion(uint32_t * version);
def BambookGetSDKVersion():
version = ctypes.c_int(0)
lib_handle.BambookGetSDKVersion(ctypes.byref(version))
return version.value
# extern "C" BB_RESULT BambookGetDeviceInfo(BB_HANDLE hConn, DeviceInfo* pInfo);
def BambookGetDeviceInfo(handle):
deviceInfo = DeviceInfo()
ret = lib_handle.BambookGetDeviceInfo(handle, ctypes.byref(deviceInfo))
if ret == BR_SUCC:
return deviceInfo
else:
return None
# extern "C" BB_RESULT BambookKeyPress(BB_HANDLE hConn, BambookKey key);
def BambookKeyPress(handle, key):
ret = lib_handle.BambookKeyPress(handle, key)
if ret == BR_SUCC:
return True
else:
return False
# extern "C" BB_RESULT BambookGetFirstPrivBookInfo(BB_HANDLE hConn, PrivBookInfo * pInfo);
def BambookGetFirstPrivBookInfo(handle, bookInfo):
bookInfo.contents.cbSize = ctypes.sizeof(bookInfo.contents)
ret = lib_handle.BambookGetFirstPrivBookInfo(handle, bookInfo)
if ret == BR_SUCC:
return True
else:
return False
# extern "C" BB_RESULT BambookGetNextPrivBookInfo(BB_HANDLE hConn, PrivBookInfo * pInfo);
def BambookGetNextPrivBookInfo(handle, bookInfo):
bookInfo.contents.cbSize = ctypes.sizeof(bookInfo.contents)
ret = lib_handle.BambookGetNextPrivBookInfo(handle, bookInfo)
if ret == BR_SUCC:
return True
elif ret == BR_EOF:
return False
else:
return False
# extern "C" BB_RESULT BambookDeletePrivBook(BB_HANDLE hConn, const char * lpszBookID);
def BambookDeletePrivBook(handle, guid):
if isinstance(guid, unicode):
guid = guid.encode('ascii')
ret = lib_handle.BambookDeletePrivBook(handle, guid)
if ret == BR_SUCC:
return True
else:
return False
class JobQueue:
jobs = {}
maxID = 0
lock = Lock()
def __init__(self):
self.maxID = 0
def NewJob(self):
self.lock.acquire()
self.maxID = self.maxID + 1
maxid = self.maxID
self.lock.release()
event = Event()
self.jobs[maxid] = (event, TRANS_STATUS_TRANS)
return maxid
def FinishJob(self, jobID, status):
self.jobs[jobID] = (self.jobs[jobID][0], status)
self.jobs[jobID][0].set()
def WaitJob(self, jobID):
self.jobs[jobID][0].wait()
return (self.jobs[jobID][1] == TRANS_STATUS_DONE)
def DeleteJob(self, jobID):
del self.jobs[jobID]
job = JobQueue()
def BambookTransferCallback(status, progress, userData):
if status == TRANS_STATUS_DONE and progress == 100:
job.FinishJob(userData, status)
elif status == TRANS_STATUS_ERR:
job.FinishJob(userData, status)
TransCallback = ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_int, ctypes.c_int)
bambookTransferCallback = TransCallback(BambookTransferCallback)
# extern "C" BB_RESULT BambookAddPrivBook(BB_HANDLE hConn, const char * pszSnbFile,
# TransCallback pCallbackFunc, intptr_t userData);
def BambookAddPrivBook(handle, filename, callback, userData):
if isinstance(filename, unicode):
filename = filename.encode('ascii')
ret = lib_handle.BambookAddPrivBook(handle, filename, callback, userData)
if ret == BR_SUCC:
return True
else:
return False
# extern "C" BB_RESULT BambookReplacePrivBook(BB_HANDLE hConn, const char *
# pszSnbFile, const char * lpszBookID, TransCallback pCallbackFunc, intptr_t userData);
def BambookReplacePrivBook(handle, filename, bookID, callback, userData):
if isinstance(filename, unicode):
filename = filename.encode('ascii')
if isinstance(bookID, unicode):
bookID = bookID.encode('ascii')
ret = lib_handle.BambookReplacePrivBook(handle, filename, bookID, callback, userData)
if ret == BR_SUCC:
return True
else:
return False
# extern "C" BB_RESULT BambookFetchPrivBook(BB_HANDLE hConn, const char *
# lpszBookID, const char * lpszFilePath, TransCallback pCallbackFunc, intptr_t userData);
def BambookFetchPrivBook(handle, bookID, filename, callback, userData):
if isinstance(filename, unicode):
filename = filename.encode('ascii')
if isinstance(bookID, unicode):
bookID = bookID.encode('ascii')
ret = lib_handle.BambookFetchPrivBook(handle, bookID, filename, bambookTransferCallback, userData)
if ret == BR_SUCC:
return True
else:
return False
# extern "C" BB_RESULT BambookVerifySnbFile(const char * snbName)
def BambookVerifySnbFile(filename):
if isinstance(filename, unicode):
filename = filename.encode('ascii')
if lib_handle.BambookVerifySnbFile(filename) == BR_SUCC:
return True
else:
return False
# BB_RESULT BambookPackSnbFromDir ( const char * snbName,, const char * rootDir );
def BambookPackSnbFromDir(snbFileName, rootDir):
if isinstance(snbFileName, unicode):
snbFileName = snbFileName.encode('ascii')
if isinstance(rootDir, unicode):
rootDir = rootDir.encode('ascii')
ret = lib_handle.BambookPackSnbFromDir(snbFileName, rootDir)
if ret == BR_SUCC:
return True
else:
return False
# BB_RESULT BambookUnpackFileFromSnb ( const char * snbName,, const char * relativePath, const char * outfname );
def BambookUnpackFileFromSnb(snbFileName, relPath, outFileName):
if isinstance(snbFileName, unicode):
snbFileName = snbFileName.encode('ascii')
if isinstance(relPath, unicode):
relPath = relPath.encode('ascii')
if isinstance(outFileName, unicode):
outFileName = outFileName.encode('ascii')
ret = lib_handle.BambookUnpackFileFromSnb(snbFileName, relPath, outFileName)
if ret == BR_SUCC:
return True
else:
return False
class Bambook:
def __init__(self):
self.handle = None
def Connect(self, ip = DEFAULT_BAMBOOK_IP, timeout = 10000):
if ip == None or ip == '':
ip = DEFAULT_BAMBOOK_IP
self.handle = BambookConnect(ip, timeout)
if self.handle and self.handle != 0:
return True
else:
return False
def Disconnect(self):
if self.handle:
return BambookDisconnect(self.handle)
return False
def GetState(self):
if self.handle:
return BambookGetConnectStatus(self.handle)
return CONN_DISCONNECTED
def GetDeviceInfo(self):
if self.handle:
return BambookGetDeviceInfo(self.handle)
return None
def SendFile(self, fileName, guid = None):
import uuid
if self.handle:
taskID = job.NewJob()
if guid:
if BambookReplacePrivBook(self.handle, fileName, guid,
bambookTransferCallback, taskID):
if(job.WaitJob(taskID)):
job.DeleteJob(taskID)
return guid
else:
job.DeleteJob(taskID)
return None
else:
job.DeleteJob(taskID)
return None
else:
guid = hashlib.md5(str(uuid.uuid4())).hexdigest()[0:15] + ".snb"
if BambookReplacePrivBook(self.handle, fileName, guid,
bambookTransferCallback, taskID):
if job.WaitJob(taskID):
job.DeleteJob(taskID)
return guid
else:
job.DeleteJob(taskID)
return None
else:
job.DeleteJob(taskID)
return None
return False
def GetFile(self, guid, fileName):
if self.handle:
taskID = job.NewJob()
ret = BambookFetchPrivBook(self.handle, guid, fileName, bambookTransferCallback, taskID)
if ret:
ret = job.WaitJob(taskID)
job.DeleteJob(taskID)
return ret
else:
job.DeleteJob(taskID)
return False
return False
def DeleteFile(self, guid):
if self.handle:
ret = BambookDeletePrivBook(self.handle, guid)
return ret
return False
def GetBookList(self):
if self.handle:
books = []
bookInfo = PrivBookInfo()
bi = ctypes.pointer(bookInfo)
ret = BambookGetFirstPrivBookInfo(self.handle, bi)
while ret:
books.append(bi.contents.Clone())
ret = BambookGetNextPrivBookInfo(self.handle, bi)
return books
@staticmethod
def GetSDKVersion():
return BambookGetSDKVersion()
@staticmethod
def VerifySNB(fileName):
return BambookVerifySnbFile(fileName);
@staticmethod
def ExtractSNBContent(fileName, relPath, path):
return BambookUnpackFileFromSnb(fileName, relPath, path)
@staticmethod
def ExtractSNB(fileName, path):
ret = BambookUnpackFileFromSnb(fileName, 'snbf/book.snbf', path + '/snbf/book.snbf')
if not ret:
return False
ret = BambookUnpackFileFromSnb(fileName, 'snbf/toc.snbf', path + '/snbf/toc.snbf')
if not ret:
return False
return True
@staticmethod
def PackageSNB(fileName, path):
return BambookPackSnbFromDir(fileName, path)
def passed():
print "> Pass"
def failed():
print "> Failed"
if __name__ == "__main__":
print "Bambook SDK Unit Test"
bb = Bambook()
print "Disconnect State"
if bb.GetState() == CONN_DISCONNECTED:
passed()
else:
failed()
print "Get SDK Version"
if bb.GetSDKVersion() == BAMBOOK_SDK_VERSION:
passed()
else:
failed()
print "Verify good SNB File"
if bb.VerifySNB(u'/tmp/f8268e6c1f4e78c.snb'):
passed()
else:
failed()
print "Verify bad SNB File"
if not bb.VerifySNB('./libwrapper.py'):
passed()
else:
failed()
print "Extract SNB File"
if bb.ExtractSNB('./test.snb', '/tmp/test'):
passed()
else:
failed()
print "Packet SNB File"
if bb.PackageSNB('/tmp/tmp.snb', '/tmp/test') and bb.VerifySNB('/tmp/tmp.snb'):
passed()
else:
failed()
print "Connect to Bambook"
if bb.Connect('192.168.250.2', 10000) and bb.GetState() == CONN_CONNECTED:
passed()
else:
failed()
print "Get Bambook Info"
devInfo = bb.GetDeviceInfo()
if devInfo:
# print "Info Size: ", devInfo.cbSize
# print "SN: ", devInfo.sn
# print "Firmware: ", devInfo.firmwareVersion
# print "Capacity: ", devInfo.deviceVolume
# print "Free: ", devInfo.spareVolume
if devInfo.cbSize == 52 and devInfo.deviceVolume == 1714232:
passed()
else:
failed()
print "Send file"
if bb.SendFile('/tmp/tmp.snb'):
passed()
else:
failed()
print "Get book list"
books = bb.GetBookList()
if len(books) > 10:
passed()
else:
failed()
print "Get book"
if bb.GetFile('f8268e6c1f4e78c.snb', '/tmp') and bb.VerifySNB('/tmp/f8268e6c1f4e78c.snb'):
passed()
else:
failed()
print "Disconnect"
if bb.Disconnect():
passed()
else:
failed()

View File

@ -13,8 +13,6 @@ from calibre.gui2.actions import InterfaceAction
from calibre.utils.smtp import config as email_config
from calibre.utils.config import tweaks
from calibre.constants import iswindows, isosx, get_osx_version
from calibre.customize.ui import is_disabled
from calibre.devices.bambook.driver import BAMBOOK
from calibre.gui2.dialogs.smartdevice import SmartdeviceDialog
from calibre.gui2 import info_dialog, question_dialog
from calibre.library.server import server_config as content_server_config
@ -23,7 +21,6 @@ class ShareConnMenu(QMenu): # {{{
connect_to_folder = pyqtSignal()
connect_to_itunes = pyqtSignal()
connect_to_bambook = pyqtSignal()
config_email = pyqtSignal()
toggle_server = pyqtSignal()
@ -46,16 +43,6 @@ class ShareConnMenu(QMenu): # {{{
self.connect_to_itunes_action = mitem
itunes_ok = iswindows or (isosx and get_osx_version() < (10, 9, 0))
mitem.setVisible(itunes_ok)
mitem = self.addAction(QIcon(I('devices/bambook.png')), _('Connect to Bambook'))
mitem.setEnabled(True)
mitem.triggered.connect(lambda x : self.connect_to_bambook.emit())
self.connect_to_bambook_action = mitem
bambook_visible = False
if not is_disabled(BAMBOOK):
device_ip = BAMBOOK.settings().extra_customization
if device_ip:
bambook_visible = True
self.connect_to_bambook_action.setVisible(bambook_visible)
self.addSeparator()
self.toggle_server_action = \
@ -76,7 +63,7 @@ class ShareConnMenu(QMenu): # {{{
r = parent.keyboard.register_shortcut
prefix = 'Share/Connect Menu '
gr = ConnectShareAction.action_spec[0]
for attr in ('folder', 'bambook', 'itunes'):
for attr in ('folder', 'itunes'):
if not (iswindows or isosx) and attr == 'itunes':
continue
ac = getattr(self, 'connect_to_%s_action'%attr)
@ -165,7 +152,6 @@ class ShareConnMenu(QMenu): # {{{
def set_state(self, device_connected, device):
self.connect_to_folder_action.setEnabled(not device_connected)
self.connect_to_itunes_action.setEnabled(not device_connected)
self.connect_to_bambook_action.setEnabled(not device_connected)
# }}}
@ -206,7 +192,6 @@ class ConnectShareAction(InterfaceAction):
self.qaction.setMenu(self.share_conn_menu)
self.share_conn_menu.connect_to_folder.connect(self.gui.connect_to_folder)
self.share_conn_menu.connect_to_itunes.connect(self.gui.connect_to_itunes)
self.share_conn_menu.connect_to_bambook.connect(self.gui.connect_to_bambook)
def location_selected(self, loc):
enabled = loc == 'library'

View File

@ -30,7 +30,6 @@ from calibre.devices.errors import (FreeSpaceError, WrongDestinationError,
BlacklistedDevice)
from calibre.devices.apple.driver import ITUNES_ASYNC
from calibre.devices.folder_device.driver import FOLDER_DEVICE
from calibre.devices.bambook.driver import BAMBOOK, BAMBOOKWifi
from calibre.constants import DEBUG
from calibre.utils.config import tweaks, device_prefs
from calibre.utils.img import scale_image
@ -941,10 +940,6 @@ class DeviceMixin(object): # {{{
if dir is not None:
self.device_manager.mount_device(kls=FOLDER_DEVICE, kind='folder', path=dir)
def connect_to_bambook(self):
self.device_manager.mount_device(kls=BAMBOOKWifi, kind='bambook',
path=BAMBOOK.settings().extra_customization)
def connect_to_itunes(self):
self.device_manager.mount_device(kls=ITUNES_ASYNC, kind='itunes', path=None)

View File

@ -353,13 +353,6 @@ class EZReaderPP(HanlinV5):
manufacturer = 'Astak'
id = 'ezreader_pp'
class Bambook(Device):
name = 'Sanda Bambook'
output_format = 'SNB'
manufacturer = 'Sanda'
id = 'bambook'
output_profile = 'bambook'
# }}}
def get_devices():