mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
1) folder device fixes
2) added configuration of the folder device 3) fixed set_books_in_library to save/restore search state, necessary because it must scan the entire database, not just the search results. 4) removed the HTC driver
This commit is contained in:
parent
8b197ebd66
commit
70a3207906
@ -455,7 +455,7 @@ from calibre.devices.edge.driver import EDGE
|
||||
from calibre.devices.teclast.driver import TECLAST_K3
|
||||
from calibre.devices.sne.driver import SNE
|
||||
from calibre.devices.misc import PALMPRE, KOBO, AVANT
|
||||
from calibre.devices.htc_td2.driver import HTC_TD2
|
||||
from calibre.devices.folder_device.driver import FOLDER_DEVICE_FOR_CONFIG
|
||||
|
||||
from calibre.ebooks.metadata.fetch import GoogleBooks, ISBNDB, Amazon
|
||||
from calibre.library.catalog import CSV_XML, EPUB_MOBI
|
||||
@ -540,7 +540,7 @@ plugins += [
|
||||
PALMPRE,
|
||||
KOBO,
|
||||
AZBOOKA,
|
||||
HTC_TD2,
|
||||
FOLDER_DEVICE_FOR_CONFIG,
|
||||
AVANT,
|
||||
]
|
||||
plugins += [x for x in list(locals().values()) if isinstance(x, type) and \
|
||||
|
@ -4,36 +4,48 @@ Created on 15 May 2010
|
||||
@author: charles
|
||||
'''
|
||||
import os
|
||||
import time
|
||||
|
||||
from calibre.customize.ui import available_output_formats
|
||||
from calibre.devices.usbms.driver import USBMS, BookList
|
||||
from calibre.devices.interface import DevicePlugin
|
||||
from calibre.devices.usbms.deviceconfig import DeviceConfig
|
||||
from calibre.utils.filenames import ascii_filename as sanitize, shorten_components_to
|
||||
|
||||
# This class is added to the standard device plugin chain, so that it can
|
||||
# be configured. It has invalid vendor_id etc, so it will never match a
|
||||
# device. The 'real' FOLDER_DEVICE will use the config from it.
|
||||
class FOLDER_DEVICE_FOR_CONFIG(USBMS):
|
||||
name = 'Folder Device Interface'
|
||||
gui_name = 'Folder Device'
|
||||
description = _('Use an arbitrary folder as a device.')
|
||||
author = 'John Schember/Charles Haley'
|
||||
supported_platforms = ['windows', 'osx', 'linux']
|
||||
FORMATS = ['epub', 'fb2', 'mobi', 'lrf', 'tcr', 'pmlz', 'lit', 'rtf', 'rb', 'pdf', 'oeb', 'txt', 'pdb']
|
||||
|
||||
class FOLDER_DEVICE(USBMS):
|
||||
type = _('Device Interface')
|
||||
|
||||
# Ordered list of supported formats
|
||||
name = 'Folder Device Interface'
|
||||
gui_name = 'Folder Device'
|
||||
description = _('Use an arbitrary folder as a device.')
|
||||
author = 'John Schember/Charles Haley'
|
||||
supported_platforms = ['windows', 'osx', 'linux']
|
||||
FORMATS = ['epub', 'fb2', 'mobi', 'lrf', 'tcr', 'pmlz', 'lit', 'rtf', 'rb', 'pdf', 'oeb', 'txt', 'pdb']
|
||||
|
||||
THUMBNAIL_HEIGHT = 68 # Height for thumbnails on device
|
||||
# Whether the metadata on books can be set via the GUI.
|
||||
|
||||
CAN_SET_METADATA = True
|
||||
SUPPORTS_SUB_DIRS = True
|
||||
DELETE_EXTS = []
|
||||
#: Path separator for paths to books on device
|
||||
path_sep = os.sep
|
||||
|
||||
#: Icon for this device
|
||||
icon = I('reader.svg')
|
||||
icon = I('sd.svg')
|
||||
METADATA_CACHE = '.metadata.calibre'
|
||||
|
||||
_main_prefix = None
|
||||
_main_prefix = ''
|
||||
_card_a_prefix = None
|
||||
_card_b_prefix = None
|
||||
|
||||
is_connected = False
|
||||
|
||||
def __init__(self, path):
|
||||
if not os.path.isdir(path):
|
||||
raise IOError, 'Path is not a folder'
|
||||
self._main_prefix = path
|
||||
self.booklist_class = BookList
|
||||
self.is_connected = True
|
||||
@ -47,6 +59,7 @@ class FOLDER_DEVICE(USBMS):
|
||||
return cls.name
|
||||
|
||||
def disconnect_from_folder(self):
|
||||
self._main_prefix = ''
|
||||
self.is_connected = False
|
||||
|
||||
def is_usb_connected(self, devices_on_system, debug=False,
|
||||
@ -54,8 +67,8 @@ class FOLDER_DEVICE(USBMS):
|
||||
return self.is_connected, self
|
||||
|
||||
def open(self):
|
||||
if self._main_prefix is None:
|
||||
raise NotImplementedError()
|
||||
if not self._main_prefix:
|
||||
return False
|
||||
return True
|
||||
|
||||
def set_progress_reporter(self, report_progress):
|
||||
@ -64,11 +77,9 @@ class FOLDER_DEVICE(USBMS):
|
||||
def card_prefix(self, end_session=True):
|
||||
return (None, None)
|
||||
|
||||
def total_space(self, end_session=True):
|
||||
return (1024*1024*1024, 0, 0)
|
||||
|
||||
def free_space(self, end_session=True):
|
||||
return (1024*1024*1024, 0, 0)
|
||||
|
||||
def get_main_ebook_dir(self):
|
||||
return ''
|
||||
|
||||
@classmethod
|
||||
def settings(self):
|
||||
return FOLDER_DEVICE_FOR_CONFIG._config().parse()
|
||||
|
@ -1,10 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from __future__ import with_statement
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
|
||||
|
@ -1,45 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
from calibre.devices.usbms.driver import USBMS
|
||||
|
||||
class HTC_TD2(USBMS):
|
||||
|
||||
name = 'HTC TD2 Phone driver'
|
||||
gui_name = 'HTC TD2'
|
||||
description = _('Communicate with HTC TD2 phones.')
|
||||
author = 'Charles Haley'
|
||||
supported_platforms = ['osx', 'linux']
|
||||
|
||||
# Ordered list of supported formats
|
||||
FORMATS = ['epub', 'pdf']
|
||||
|
||||
VENDOR_ID = {
|
||||
# HTC
|
||||
# 0x0bb4 : { 0x0c30 : [0x000]},
|
||||
0xFbb4 : { 0x0c30 : [0x000]},
|
||||
}
|
||||
EBOOK_DIR_MAIN = ['EBooks']
|
||||
EXTRA_CUSTOMIZATION_MESSAGE = _('Comma separated list of directories to '
|
||||
'send e-books to on the device. The first one that exists will '
|
||||
'be used')
|
||||
EXTRA_CUSTOMIZATION_DEFAULT = ', '.join(EBOOK_DIR_MAIN)
|
||||
|
||||
VENDOR_NAME = ['']
|
||||
WINDOWS_MAIN_MEM = ['']
|
||||
|
||||
MAIN_MEMORY_VOLUME_LABEL = 'HTC Phone Internal Memory'
|
||||
|
||||
SUPPORTS_SUB_DIRS = True
|
||||
|
||||
def post_open_callback(self):
|
||||
opts = self.settings()
|
||||
dirs = opts.extra_customization
|
||||
if not dirs:
|
||||
dirs = self.EBOOK_DIR_MAIN
|
||||
else:
|
||||
dirs = [x.strip() for x in dirs.split(',')]
|
||||
self.EBOOK_DIR_MAIN = dirs
|
@ -113,15 +113,17 @@ class Device(DeviceConfig, DevicePlugin):
|
||||
def _windows_space(cls, prefix):
|
||||
if not prefix:
|
||||
return 0, 0
|
||||
if prefix.endswith(os.sep):
|
||||
prefix = prefix[:-1]
|
||||
win32file = __import__('win32file', globals(), locals(), [], -1)
|
||||
try:
|
||||
sectors_per_cluster, bytes_per_sector, free_clusters, total_clusters = \
|
||||
win32file.GetDiskFreeSpace(prefix[:-1])
|
||||
win32file.GetDiskFreeSpace(prefix)
|
||||
except Exception, err:
|
||||
if getattr(err, 'args', [None])[0] == 21: # Disk not ready
|
||||
time.sleep(3)
|
||||
sectors_per_cluster, bytes_per_sector, free_clusters, total_clusters = \
|
||||
win32file.GetDiskFreeSpace(prefix[:-1])
|
||||
win32file.GetDiskFreeSpace(prefix)
|
||||
else: raise
|
||||
mult = sectors_per_cluster * bytes_per_sector
|
||||
return total_clusters * mult, free_clusters * mult
|
||||
|
@ -428,23 +428,24 @@ class DeviceMenu(QMenu):
|
||||
if opts.accounts:
|
||||
self.addSeparator()
|
||||
self.addMenu(self.email_to_menu)
|
||||
|
||||
self.addSeparator()
|
||||
mitem = self.addAction(_('Connect to folder'))
|
||||
mitem.setEnabled(True)
|
||||
mitem.triggered.connect(lambda x : self.connect_to_folder.emit())
|
||||
self.connect_to_folder_action = mitem
|
||||
|
||||
mitem = self.addAction(_('Disconnect from folder'))
|
||||
mitem.setEnabled(False)
|
||||
mitem.triggered.connect(lambda x : self.disconnect_from_folder.emit())
|
||||
self.disconnect_from_folder_action = mitem
|
||||
|
||||
self.addSeparator()
|
||||
annot = self.addAction(_('Fetch annotations (experimental)'))
|
||||
annot.setEnabled(False)
|
||||
annot.triggered.connect(lambda x :
|
||||
self.fetch_annotations.emit())
|
||||
self.annotation_action = annot
|
||||
|
||||
mitem = self.addAction(_('Connect to folder (experimental)'))
|
||||
mitem.setEnabled(True)
|
||||
mitem.triggered.connect(lambda x : self.connect_to_folder.emit())
|
||||
self.connect_to_folder_action = mitem
|
||||
|
||||
mitem = self.addAction(_('Disconnect from folder (experimental)'))
|
||||
mitem.setEnabled(False)
|
||||
mitem.triggered.connect(lambda x : self.disconnect_from_folder.emit())
|
||||
self.disconnect_from_folder_action = mitem
|
||||
|
||||
self.enable_device_actions(False)
|
||||
|
||||
def change_default_action(self, action):
|
||||
@ -1089,8 +1090,17 @@ class DeviceGUI(object):
|
||||
# First build a cache of the library, so the search isn't On**2
|
||||
self.db_book_title_cache = {}
|
||||
self.db_book_uuid_cache = set()
|
||||
for idx in range(self.library_view.model().db.count()):
|
||||
mi = self.library_view.model().db.get_metadata(idx, index_is_id=False)
|
||||
db = self.library_view.model().db
|
||||
# The following is a terrible hack, made necessary because the db
|
||||
# result_cache will always use the results filtered by the current
|
||||
# search. We need all the db entries here. Choice was to either
|
||||
# cache the search results so we can use the entire db, to duplicate
|
||||
# large parts of the get_metadata code, or to use db_ids and pay the
|
||||
# large performance penalty of zillions of SQL queries. Choice:
|
||||
# save/restore the search state.
|
||||
state = db.get_state_before_scan()
|
||||
for idx in range(db.count()):
|
||||
mi = db.get_metadata(idx, index_is_id=False)
|
||||
title = re.sub('(?u)\W|[_]', '', mi.title.lower())
|
||||
if title not in self.db_book_title_cache:
|
||||
self.db_book_title_cache[title] = {'authors':{}, 'db_ids':{}}
|
||||
@ -1099,12 +1109,13 @@ class DeviceGUI(object):
|
||||
self.db_book_title_cache[title]['authors'][authors] = mi
|
||||
self.db_book_title_cache[title]['db_ids'][mi.application_id] = mi
|
||||
self.db_book_uuid_cache.add(mi.uuid)
|
||||
db.restore_state_after_scan(state)
|
||||
|
||||
# Now iterate through all the books on the device, setting the in_library field
|
||||
# Fastest and most accurate key is the uuid. Second is the application_id, which
|
||||
# is really the db key, but as this can accidentally match across libraries we
|
||||
# also verify the title. The db_id exists on Sony devices. Fallback is title
|
||||
# and author match
|
||||
# Now iterate through all the books on the device, setting the
|
||||
# in_library field Fastest and most accurate key is the uuid. Second is
|
||||
# the application_id, which is really the db key, but as this can
|
||||
# accidentally match across libraries we also verify the title. The
|
||||
# db_id exists on Sony devices. Fallback is title and author match
|
||||
resend_metadata = False
|
||||
for booklist in booklists:
|
||||
for book in booklist:
|
||||
@ -1135,5 +1146,5 @@ class DeviceGUI(object):
|
||||
resend_metadata = True
|
||||
if resend_metadata:
|
||||
# Correcting metadata cache on device.
|
||||
if self.device_manager.is_connected:
|
||||
if self.device_manager.is_device_connected:
|
||||
self.device_manager.sync_booklists(None, booklists)
|
||||
|
@ -669,15 +669,11 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
||||
def connect_to_folder(self):
|
||||
dir = choose_dir(self, 'Select Device Folder', 'Select folder to open')
|
||||
if dir is not None:
|
||||
print dir
|
||||
self.device_manager.connect_to_folder(dir)
|
||||
self._sync_menu.connect_to_folder_action.setEnabled(False)
|
||||
self._sync_menu.disconnect_from_folder_action.setEnabled(True)
|
||||
|
||||
def disconnect_from_folder(self):
|
||||
self.device_manager.disconnect_folder()
|
||||
self._sync_menu.connect_to_folder_action.setEnabled(True)
|
||||
self._sync_menu.disconnect_from_folder_action.setEnabled(False)
|
||||
|
||||
def create_device_menu(self):
|
||||
self._sync_menu = DeviceMenu(self)
|
||||
@ -965,6 +961,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
||||
self.refresh_ondevice_info (device_connected = True, reset_only = True)
|
||||
else:
|
||||
self._sync_menu.connect_to_folder_action.setEnabled(True)
|
||||
self._sync_menu.disconnect_from_folder_action.setEnabled(False)
|
||||
self.save_device_view_settings()
|
||||
self.device_connected = False
|
||||
self._sync_menu.enable_device_actions(False)
|
||||
|
@ -553,6 +553,14 @@ class ResultCache(SearchQueryParser):
|
||||
if item is not None:
|
||||
item[ondevice_col] = db.book_on_device_string(item[0])
|
||||
|
||||
def get_state_before_scan(self):
|
||||
retval = self._map_filtered
|
||||
self._map_filtered = self._map
|
||||
return retval
|
||||
|
||||
def restore_state_after_scan(self, map_filtered):
|
||||
self._map_filtered = map_filtered
|
||||
|
||||
def refresh(self, db, field=None, ascending=True):
|
||||
temp = db.conn.get('SELECT * FROM meta2')
|
||||
self._data = list(itertools.repeat(None, temp[-1][0]+2)) if temp else []
|
||||
|
@ -245,6 +245,8 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
||||
self.has_id = self.data.has_id
|
||||
self.count = self.data.count
|
||||
|
||||
self.get_state_before_scan = self.data.get_state_before_scan
|
||||
self.restore_state_after_scan = self.data.restore_state_after_scan
|
||||
self.refresh_ondevice = functools.partial(self.data.refresh_ondevice, self)
|
||||
|
||||
self.refresh()
|
||||
|
Loading…
x
Reference in New Issue
Block a user