Merge from trunk

This commit is contained in:
Charles Haley 2010-06-08 08:17:59 +01:00
commit 468cc178d5
8 changed files with 67 additions and 43 deletions

View File

@ -16,7 +16,7 @@ class NYTimes(BasicNewsRecipe):
title = 'New York Times Top Stories' title = 'New York Times Top Stories'
__author__ = 'GRiker' __author__ = 'GRiker'
language = _('English') language = 'en'
description = 'Top Stories from the New York Times' description = 'Top Stories from the New York Times'
# List of sections typically included in Top Stories. Use a keyword from the # List of sections typically included in Top Stories. Use a keyword from the

View File

@ -14,7 +14,6 @@ from calibre.devices.interface import DevicePlugin
from calibre.ebooks.BeautifulSoup import BeautifulSoup from calibre.ebooks.BeautifulSoup import BeautifulSoup
from calibre.ebooks.metadata import MetaInformation from calibre.ebooks.metadata import MetaInformation
from calibre.library.server.utils import strftime from calibre.library.server.utils import strftime
from calibre.ptempfile import PersistentTemporaryFile
from calibre.utils.config import Config, config_dir from calibre.utils.config import Config, config_dir
from calibre.utils.date import parse_date from calibre.utils.date import parse_date
from calibre.utils.logging import Log from calibre.utils.logging import Log
@ -775,10 +774,12 @@ class ITUNES(DevicePlugin):
self._remove_from_iTunes(self.cached_books[path]) self._remove_from_iTunes(self.cached_books[path])
# Add to iTunes Library|Books # Add to iTunes Library|Books
if isinstance(file,PersistentTemporaryFile): fpath = file
added = self.iTunes.add(appscript.mactypes.File(file._name)) if getattr(file, 'orig_file_path', None) is not None:
else: fpath = file.orig_file_path
added = self.iTunes.add(appscript.mactypes.File(file)) elif getattr(file, 'name', None) is not None:
fpath = file.name
added = self.iTunes.add(appscript.mactypes.File(fpath))
thumb = None thumb = None
if metadata[i].cover: if metadata[i].cover:
@ -815,7 +816,7 @@ class ITUNES(DevicePlugin):
this_book.device_collections = [] this_book.device_collections = []
this_book.library_id = added this_book.library_id = added
this_book.path = path this_book.path = path
this_book.size = self._get_device_book_size(file, added.size()) this_book.size = self._get_device_book_size(fpath, added.size())
this_book.thumbnail = thumb this_book.thumbnail = thumb
this_book.iTunes_id = added this_book.iTunes_id = added
@ -922,12 +923,15 @@ class ITUNES(DevicePlugin):
self.log.info(" '%s' not in cached_books" % metadata[i].title) self.log.info(" '%s' not in cached_books" % metadata[i].title)
# Add to iTunes Library|Books # Add to iTunes Library|Books
if isinstance(file,PersistentTemporaryFile): fpath = file
op_status = lib_books.AddFile(file._name) if getattr(file, 'orig_file_path', None) is not None:
self.log.info("ITUNES.upload_books():\n iTunes adding '%s'" % file._name) fpath = file.orig_file_path
else: elif getattr(file, 'name', None) is not None:
op_status = lib_books.AddFile(file) fpath = file.name
self.log.info(" iTunes adding '%s'" % file)
op_status = lib_books.AddFile(fpath)
self.log.info("ITUNES.upload_books():\n iTunes adding '%s'"
% fpath)
if DEBUG: if DEBUG:
sys.stdout.write(" iTunes copying '%s' ..." % metadata[i].title) sys.stdout.write(" iTunes copying '%s' ..." % metadata[i].title)
@ -1464,7 +1468,7 @@ class ITUNES(DevicePlugin):
# Read the current storage path for iTunes media # Read the current storage path for iTunes media
cmd = "defaults read com.apple.itunes NSNavLastRootDirectory" cmd = "defaults read com.apple.itunes NSNavLastRootDirectory"
proc = subprocess.Popen( cmd, shell=True, cwd=os.curdir, stdout=subprocess.PIPE) proc = subprocess.Popen( cmd, shell=True, cwd=os.curdir, stdout=subprocess.PIPE)
retcode = proc.wait() proc.wait()
media_dir = os.path.abspath(proc.communicate()[0].strip()) media_dir = os.path.abspath(proc.communicate()[0].strip())
if os.path.exists(media_dir): if os.path.exists(media_dir):
self.iTunes_media = media_dir self.iTunes_media = media_dir

View File

@ -287,7 +287,9 @@ class DevicePlugin(Plugin):
This method should raise a L{FreeSpaceError} if there is not enough This method should raise a L{FreeSpaceError} if there is not enough
free space on the device. The text of the FreeSpaceError must contain the free space on the device. The text of the FreeSpaceError must contain the
word "card" if C{on_card} is not None otherwise it must contain the word "memory". word "card" if C{on_card} is not None otherwise it must contain the word "memory".
:files: A list of paths and/or file-like objects. :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.
:names: A list of file names that the books should have :names: A list of file names that the books should have
once uploaded to the device. len(names) == len(files) once uploaded to the device. len(names) == len(files)
:return: A list of 3-element tuples. The list is meant to be passed :return: A list of 3-element tuples. The list is meant to be passed

View File

@ -108,7 +108,7 @@ class BookList(_BookList):
def add_book(self, book, replace_metadata): def add_book(self, book, replace_metadata):
try: try:
b = self.index(book) b = self.index(book)
except ValueError, IndexError: except (ValueError, IndexError):
b = None b = None
if b is None: if b is None:
self.append(book) self.append(book)

View File

@ -765,12 +765,8 @@ class Device(DeviceConfig, DevicePlugin):
path = existing[0] path = existing[0]
def get_size(obj): def get_size(obj):
if hasattr(obj, 'seek'): path = getattr(obj, 'name', obj)
obj.seek(0, os.SEEK_END) return os.path.getsize(path)
size = obj.tell()
obj.seek(0)
return size
return os.path.getsize(obj)
sizes = [get_size(f) for f in files] sizes = [get_size(f) for f in files]
size = sum(sizes) size = sum(sizes)

View File

@ -1,6 +1,8 @@
from __future__ import with_statement from __future__ import with_statement
__license__ = 'GPL v3' __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>' __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
# Imports {{{
import os, traceback, Queue, time, socket, cStringIO, re import os, traceback, Queue, time, socket, cStringIO, re
from threading import Thread, RLock from threading import Thread, RLock
from itertools import repeat from itertools import repeat
@ -27,7 +29,9 @@ from calibre.utils.smtp import compose_mail, sendmail, extract_email_address, \
config as email_config config as email_config
from calibre.devices.folder_device.driver import FOLDER_DEVICE from calibre.devices.folder_device.driver import FOLDER_DEVICE
class DeviceJob(BaseJob): # }}}
class DeviceJob(BaseJob): # {{{
def __init__(self, func, done, job_manager, args=[], kwargs={}, def __init__(self, func, done, job_manager, args=[], kwargs={},
description=''): description=''):
@ -78,8 +82,9 @@ class DeviceJob(BaseJob):
def log_file(self): def log_file(self):
return cStringIO.StringIO(self._details.encode('utf-8')) return cStringIO.StringIO(self._details.encode('utf-8'))
# }}}
class DeviceManager(Thread): class DeviceManager(Thread): # {{{
def __init__(self, connected_slot, job_manager, open_feedback_slot, sleep_time=2): def __init__(self, connected_slot, job_manager, open_feedback_slot, sleep_time=2):
''' '''
@ -338,8 +343,9 @@ class DeviceManager(Thread):
return self.create_job(self._view_book, done, args=[path, target], return self.create_job(self._view_book, done, args=[path, target],
description=_('View book on device')) description=_('View book on device'))
# }}}
class DeviceAction(QAction): class DeviceAction(QAction): # {{{
a_s = pyqtSignal(object) a_s = pyqtSignal(object)
@ -356,9 +362,9 @@ class DeviceAction(QAction):
def __repr__(self): def __repr__(self):
return self.__class__.__name__ + ':%s:%s:%s'%(self.dest, self.delete, return self.__class__.__name__ + ':%s:%s:%s'%(self.dest, self.delete,
self.specific) self.specific)
# }}}
class DeviceMenu(QMenu): # {{{
class DeviceMenu(QMenu):
fetch_annotations = pyqtSignal() fetch_annotations = pyqtSignal()
connect_to_folder = pyqtSignal() connect_to_folder = pyqtSignal()
@ -532,8 +538,9 @@ class DeviceMenu(QMenu):
annot_enable = enable and getattr(device, 'SUPPORTS_ANNOTATIONS', False) annot_enable = enable and getattr(device, 'SUPPORTS_ANNOTATIONS', False)
self.annotation_action.setEnabled(annot_enable) self.annotation_action.setEnabled(annot_enable)
# }}}
class Emailer(Thread): class Emailer(Thread): # {{{
def __init__(self, timeout=60): def __init__(self, timeout=60):
Thread.__init__(self) Thread.__init__(self)
@ -590,6 +597,7 @@ class Emailer(Thread):
results.append([jobname, e, traceback.format_exc()]) results.append([jobname, e, traceback.format_exc()])
callback(results) callback(results)
# }}}
class DeviceGUI(object): class DeviceGUI(object):
@ -637,7 +645,7 @@ class DeviceGUI(object):
if not ids or len(ids) == 0: if not ids or len(ids) == 0:
return return
files, _auto_ids = self.library_view.model().get_preferred_formats_from_ids(ids, files, _auto_ids = self.library_view.model().get_preferred_formats_from_ids(ids,
fmts, paths=True, set_metadata=True, fmts, set_metadata=True,
specific_format=specific_format, specific_format=specific_format,
exclude_auto=do_auto_convert) exclude_auto=do_auto_convert)
if do_auto_convert: if do_auto_convert:
@ -647,7 +655,6 @@ class DeviceGUI(object):
_auto_ids = [] _auto_ids = []
full_metadata = self.library_view.model().metadata_for(ids) full_metadata = self.library_view.model().metadata_for(ids)
files = [getattr(f, 'name', None) for f in files]
bad, remove_ids, jobnames = [], [], [] bad, remove_ids, jobnames = [], [], []
texts, subjects, attachments, attachment_names = [], [], [], [] texts, subjects, attachments, attachment_names = [], [], [], []
@ -760,7 +767,7 @@ class DeviceGUI(object):
for account, fmts in accounts: for account, fmts in accounts:
files, auto = self.library_view.model().\ files, auto = self.library_view.model().\
get_preferred_formats_from_ids([id], fmts) get_preferred_formats_from_ids([id], fmts)
files = [f.name for f in files if f is not None] files = [f for f in files if f is not None]
if not files: if not files:
continue continue
attachment = files[0] attachment = files[0]
@ -824,7 +831,7 @@ class DeviceGUI(object):
prefix = prefix.decode(preferred_encoding, 'replace') prefix = prefix.decode(preferred_encoding, 'replace')
prefix = ascii_filename(prefix) prefix = ascii_filename(prefix)
names.append('%s_%d%s'%(prefix, id, names.append('%s_%d%s'%(prefix, id,
os.path.splitext(f.name)[1])) os.path.splitext(f)[1]))
if mi.cover and os.access(mi.cover, os.R_OK): if mi.cover and os.access(mi.cover, os.R_OK):
mi.thumbnail = self.cover_to_thumbnail(open(mi.cover, mi.thumbnail = self.cover_to_thumbnail(open(mi.cover,
'rb').read()) 'rb').read())
@ -837,7 +844,7 @@ class DeviceGUI(object):
on_card = space.get(sorted(space.keys(), reverse=True)[0], None) on_card = space.get(sorted(space.keys(), reverse=True)[0], None)
self.upload_books(files, names, metadata, self.upload_books(files, names, metadata,
on_card=on_card, on_card=on_card,
memory=[[f.name for f in files], remove]) memory=[files, remove])
self.status_bar.showMessage(_('Sending catalogs to device.'), 5000) self.status_bar.showMessage(_('Sending catalogs to device.'), 5000)
@ -884,7 +891,7 @@ class DeviceGUI(object):
prefix = prefix.decode(preferred_encoding, 'replace') prefix = prefix.decode(preferred_encoding, 'replace')
prefix = ascii_filename(prefix) prefix = ascii_filename(prefix)
names.append('%s_%d%s'%(prefix, id, names.append('%s_%d%s'%(prefix, id,
os.path.splitext(f.name)[1])) os.path.splitext(f)[1]))
if mi.cover and os.access(mi.cover, os.R_OK): if mi.cover and os.access(mi.cover, os.R_OK):
mi.thumbnail = self.cover_to_thumbnail(open(mi.cover, mi.thumbnail = self.cover_to_thumbnail(open(mi.cover,
'rb').read()) 'rb').read())
@ -898,7 +905,7 @@ class DeviceGUI(object):
on_card = space.get(sorted(space.keys(), reverse=True)[0], None) on_card = space.get(sorted(space.keys(), reverse=True)[0], None)
self.upload_books(files, names, metadata, self.upload_books(files, names, metadata,
on_card=on_card, on_card=on_card,
memory=[[f.name for f in files], remove]) memory=[files, remove])
self.status_bar.showMessage(_('Sending news to device.'), 5000) self.status_bar.showMessage(_('Sending news to device.'), 5000)
@ -914,7 +921,7 @@ class DeviceGUI(object):
_files, _auto_ids = self.library_view.model().get_preferred_formats_from_ids(ids, _files, _auto_ids = self.library_view.model().get_preferred_formats_from_ids(ids,
settings.format_map, settings.format_map,
paths=True, set_metadata=True, set_metadata=True,
specific_format=specific_format, specific_format=specific_format,
exclude_auto=do_auto_convert) exclude_auto=do_auto_convert)
if do_auto_convert: if do_auto_convert:
@ -930,9 +937,8 @@ class DeviceGUI(object):
mi.thumbnail = self.cover_to_thumbnail(open(mi.cover, 'rb').read()) mi.thumbnail = self.cover_to_thumbnail(open(mi.cover, 'rb').read())
imetadata = iter(metadata) imetadata = iter(metadata)
files = [getattr(f, 'name', None) for f in _files]
bad, good, gf, names, remove_ids = [], [], [], [], [] bad, good, gf, names, remove_ids = [], [], [], [], []
for f in files: for f in _files:
mi = imetadata.next() mi = imetadata.next()
id = ids.next() id = ids.next()
if f is None: if f is None:

View File

@ -21,7 +21,8 @@ from calibre.utils.date import dt_factory, qt_to_dt, isoformat
from calibre.ebooks.metadata.meta import set_metadata as _set_metadata from calibre.ebooks.metadata.meta import set_metadata as _set_metadata
from calibre.utils.search_query_parser import SearchQueryParser from calibre.utils.search_query_parser import SearchQueryParser
from calibre.library.caches import _match, CONTAINS_MATCH, EQUALS_MATCH, REGEXP_MATCH from calibre.library.caches import _match, CONTAINS_MATCH, EQUALS_MATCH, REGEXP_MATCH
from calibre import strftime from calibre import strftime, isbytestring
from calibre.constants import filesystem_encoding
from calibre.gui2.library import DEFAULT_SORT from calibre.gui2.library import DEFAULT_SORT
def human_readable(size, precision=1): def human_readable(size, precision=1):
@ -33,6 +34,13 @@ TIME_FMT = '%d %b %Y'
ALIGNMENT_MAP = {'left': Qt.AlignLeft, 'right': Qt.AlignRight, 'center': ALIGNMENT_MAP = {'left': Qt.AlignLeft, 'right': Qt.AlignRight, 'center':
Qt.AlignHCenter} Qt.AlignHCenter}
class FormatPath(unicode):
def __new__(cls, path, orig_file_path):
ans = unicode.__new__(cls, path)
ans.orig_file_path = orig_file_path
return ans
class BooksModel(QAbstractTableModel): # {{{ class BooksModel(QAbstractTableModel): # {{{
about_to_be_sorted = pyqtSignal(object, name='aboutToBeSorted') about_to_be_sorted = pyqtSignal(object, name='aboutToBeSorted')
@ -379,7 +387,7 @@ class BooksModel(QAbstractTableModel): # {{{
else: else:
return metadata return metadata
def get_preferred_formats_from_ids(self, ids, formats, paths=False, def get_preferred_formats_from_ids(self, ids, formats,
set_metadata=False, specific_format=None, set_metadata=False, specific_format=None,
exclude_auto=False, mode='r+b'): exclude_auto=False, mode='r+b'):
ans = [] ans = []
@ -404,12 +412,20 @@ class BooksModel(QAbstractTableModel): # {{{
as_file=True)) as src: as_file=True)) as src:
shutil.copyfileobj(src, pt) shutil.copyfileobj(src, pt)
pt.flush() pt.flush()
if getattr(src, 'name', None):
pt.orig_file_path = os.path.abspath(src.name)
pt.seek(0) pt.seek(0)
if set_metadata: if set_metadata:
_set_metadata(pt, self.db.get_metadata(id, get_cover=True, index_is_id=True), _set_metadata(pt, self.db.get_metadata(id, get_cover=True, index_is_id=True),
format) format)
pt.close() if paths else pt.seek(0) pt.close()
ans.append(pt) def to_uni(x):
if isbytestring(x):
x = x.decode(filesystem_encoding)
return x
name, op = map(to_uni, map(os.path.abspath, (pt.name,
pt.orig_file_path)))
ans.append(FormatPath(name, op))
else: else:
need_auto.append(id) need_auto.append(id)
if not exclude_auto: if not exclude_auto:

View File

@ -186,7 +186,7 @@ class TagsView(QTreeView): # {{{
def is_visible(self, idx): def is_visible(self, idx):
item = idx.internalPointer() item = idx.internalPointer()
if item.type == TagTreeItem.TAG: if getattr(item, 'type', None) == TagTreeItem.TAG:
idx = idx.parent() idx = idx.parent()
return self.isExpanded(idx) return self.isExpanded(idx)