mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Merge from trunk
This commit is contained in:
commit
a13b6065c6
@ -19,6 +19,45 @@
|
||||
# new recipes:
|
||||
# - title:
|
||||
|
||||
- version: 0.7.56
|
||||
date: 2011-04-17
|
||||
|
||||
new features:
|
||||
- title: "This is primarily a bug fix release that fixes a bug in 0.7.55 that caused calibre to rescan the files on the device every time the device is connected. If you updated to 0.7.55 it is highly recommended you update to 0.7.56"
|
||||
|
||||
- title: "Device driver for Coby Kyros"
|
||||
|
||||
- title: "Remove the quick access to search options from next to the search bar, as we now have a separate search highlights toggle button"
|
||||
|
||||
- title: "MOBI Output: Ensure that MOBI files always have 8KB worth of null bytes at the end of record 0. This appears to be necessary for Amazon to be able to add DRM to calibre generated MOBI files sent to their publishing service."
|
||||
|
||||
- title: "Add a tool to inspect MOBI files. To use: calibre-debug -m file.mobi"
|
||||
|
||||
bug fixes:
|
||||
- title: "Fixed regression taht caused calibre to rescan files on the device on every reconnect"
|
||||
|
||||
- title: "Fix donate button causing the toolbar to be too large on OS X"
|
||||
|
||||
- title: "MOBI Input: Fix detection of Table of Contents for MOBI files that have a page break between the location designated as the Table of Contents and the actual table of contents."
|
||||
tickets: [763504]
|
||||
|
||||
- title: "Comic Input: Fix handling of some CBZ files that have wrongly encoded non ASCII filenames on windows."
|
||||
tickets: [763280]
|
||||
|
||||
- title: "PML Input: Fix multi-line chapter title causing a spurious page break"
|
||||
tickets: [763238]
|
||||
|
||||
- title: "EPUB Input: Speed up processing of files with very large manifest/spines"
|
||||
|
||||
- title: "Fix regression that broke cover:False searches in 0.7.55"
|
||||
|
||||
improved recipes:
|
||||
- Suedduetsche Zeitung
|
||||
- Irish Times
|
||||
- Big Oven
|
||||
- NSPM
|
||||
|
||||
|
||||
- version: 0.7.55
|
||||
date: 2011-04-15
|
||||
|
||||
|
@ -2,7 +2,7 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
__appname__ = 'calibre'
|
||||
__version__ = '0.7.55'
|
||||
__version__ = '0.7.56'
|
||||
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
||||
|
||||
import re, importlib
|
||||
|
@ -201,8 +201,9 @@ class ITUNES(DriverBase):
|
||||
# 0x1294 iPhone 3GS
|
||||
# 0x1297 iPhone 4
|
||||
# 0x129a iPad
|
||||
# 0x12a2 iPad2
|
||||
VENDOR_ID = [0x05ac]
|
||||
PRODUCT_ID = [0x1292,0x1293,0x1294,0x1297,0x1299,0x129a]
|
||||
PRODUCT_ID = [0x1292,0x1293,0x1294,0x1297,0x1299,0x129a,0x12a2]
|
||||
BCD = [0x01]
|
||||
|
||||
# Plugboard ID
|
||||
@ -421,7 +422,7 @@ class ITUNES(DriverBase):
|
||||
|
||||
cached_books[this_book.path] = {
|
||||
'title':book.name(),
|
||||
'author':[book.artist()],
|
||||
'author':book.artist().split(' & '),
|
||||
'lib_book':library_books[this_book.path] if this_book.path in library_books else None,
|
||||
'dev_book':book,
|
||||
'uuid': book.composer()
|
||||
@ -459,7 +460,7 @@ class ITUNES(DriverBase):
|
||||
|
||||
cached_books[this_book.path] = {
|
||||
'title':book.Name,
|
||||
'author':book.Artist,
|
||||
'author':book.artist().split(' & '),
|
||||
'lib_book':library_books[this_book.path] if this_book.path in library_books else None,
|
||||
'uuid': book.Composer,
|
||||
'format': 'pdf' if book.KindAsString.startswith('PDF') else 'epub'
|
||||
@ -1021,7 +1022,9 @@ class ITUNES(DriverBase):
|
||||
if isosx:
|
||||
for (i,file) in enumerate(files):
|
||||
format = file.rpartition('.')[2].lower()
|
||||
path = self.path_template % (metadata[i].title, metadata[i].author[0],format)
|
||||
path = self.path_template % (metadata[i].title,
|
||||
authors_to_string(metadata[i].authors),
|
||||
format)
|
||||
self._remove_existing_copy(path, metadata[i])
|
||||
fpath = self._get_fpath(file, metadata[i], format, update_md=True)
|
||||
db_added, lb_added = self._add_new_copy(fpath, metadata[i])
|
||||
@ -1034,9 +1037,11 @@ class ITUNES(DriverBase):
|
||||
if DEBUG:
|
||||
self.log.info("ITUNES.upload_books()")
|
||||
self.log.info(" adding '%s' by '%s' uuid:%s to self.cached_books" %
|
||||
( metadata[i].title, metadata[i].author, metadata[i].uuid))
|
||||
(metadata[i].title,
|
||||
authors_to_string(metadata[i].authors),
|
||||
metadata[i].uuid))
|
||||
self.cached_books[this_book.path] = {
|
||||
'author': metadata[i].author,
|
||||
'author': authors_to_string(metadata[i].authors),
|
||||
'dev_book': db_added,
|
||||
'format': format,
|
||||
'lib_book': lb_added,
|
||||
@ -1055,7 +1060,9 @@ class ITUNES(DriverBase):
|
||||
|
||||
for (i,file) in enumerate(files):
|
||||
format = file.rpartition('.')[2].lower()
|
||||
path = self.path_template % (metadata[i].title, metadata[i].author[0],format)
|
||||
path = self.path_template % (metadata[i].title,
|
||||
authors_to_string(metadata[i].authors),
|
||||
format)
|
||||
self._remove_existing_copy(path, metadata[i])
|
||||
fpath = self._get_fpath(file, metadata[i],format, update_md=True)
|
||||
db_added, lb_added = self._add_new_copy(fpath, metadata[i])
|
||||
@ -1075,9 +1082,11 @@ class ITUNES(DriverBase):
|
||||
if DEBUG:
|
||||
self.log.info("ITUNES.upload_books()")
|
||||
self.log.info(" adding '%s' by '%s' uuid:%s to self.cached_books" %
|
||||
( metadata[i].title, metadata[i].author, metadata[i].uuid))
|
||||
(metadata[i].title,
|
||||
authors_to_string(metadata[i].authors),
|
||||
metadata[i].uuid))
|
||||
self.cached_books[this_book.path] = {
|
||||
'author': metadata[i].author[0],
|
||||
'author': authors_to_string(metadata[i].authors),
|
||||
'dev_book': db_added,
|
||||
'format': format,
|
||||
'lib_book': lb_added,
|
||||
@ -1190,7 +1199,7 @@ class ITUNES(DriverBase):
|
||||
base_fn = base_fn.rpartition('.')[0]
|
||||
db_added = self._find_device_book(
|
||||
{ 'title': base_fn if format == 'pdf' else metadata.title,
|
||||
'author': metadata.authors[0],
|
||||
'author': authors_to_string(metadata.authors),
|
||||
'uuid': metadata.uuid,
|
||||
'format': format})
|
||||
return db_added
|
||||
@ -1255,7 +1264,7 @@ class ITUNES(DriverBase):
|
||||
base_fn = base_fn.rpartition('.')[0]
|
||||
added = self._find_library_book(
|
||||
{ 'title': base_fn if format == 'pdf' else metadata.title,
|
||||
'author': metadata.author[0],
|
||||
'author': authors_to_string(metadata.authors),
|
||||
'uuid': metadata.uuid,
|
||||
'format': format})
|
||||
return added
|
||||
@ -1314,7 +1323,7 @@ class ITUNES(DriverBase):
|
||||
with open(metadata.cover,'r+b') as cd:
|
||||
cover_data = cd.read()
|
||||
except:
|
||||
self.problem_titles.append("'%s' by %s" % (metadata.title, metadata.author[0]))
|
||||
self.problem_titles.append("'%s' by %s" % (metadata.title, authors_to_string(metadata.authors)))
|
||||
self.log.error(" error scaling '%s' for '%s'" % (metadata.cover,metadata.title))
|
||||
|
||||
import traceback
|
||||
@ -1389,7 +1398,7 @@ class ITUNES(DriverBase):
|
||||
thumb_path = path.rpartition('.')[0] + '.jpg'
|
||||
zfw.writestr(thumb_path, thumb)
|
||||
except:
|
||||
self.problem_titles.append("'%s' by %s" % (metadata.title, metadata.author[0]))
|
||||
self.problem_titles.append("'%s' by %s" % (metadata.title, authors_to_string(metadata.authors)))
|
||||
self.log.error(" error converting '%s' to thumb for '%s'" % (metadata.cover,metadata.title))
|
||||
finally:
|
||||
try:
|
||||
@ -1407,7 +1416,7 @@ class ITUNES(DriverBase):
|
||||
if DEBUG:
|
||||
self.log.info(" ITUNES._create_new_book()")
|
||||
|
||||
this_book = Book(metadata.title, authors_to_string(metadata.author))
|
||||
this_book = Book(metadata.title, authors_to_string(metadata.authors))
|
||||
this_book.datetime = time.gmtime()
|
||||
this_book.db_id = None
|
||||
this_book.device_collections = []
|
||||
@ -2451,7 +2460,7 @@ class ITUNES(DriverBase):
|
||||
for book in self.cached_books:
|
||||
if self.cached_books[book]['uuid'] == metadata.uuid or \
|
||||
(self.cached_books[book]['title'] == metadata.title and \
|
||||
self.cached_books[book]['author'] == metadata.authors[0]):
|
||||
self.cached_books[book]['author'] == authors_to_string(metadata.authors)):
|
||||
self.update_list.append(self.cached_books[book])
|
||||
self._remove_from_device(self.cached_books[book])
|
||||
if DEBUG:
|
||||
@ -2470,7 +2479,7 @@ class ITUNES(DriverBase):
|
||||
for book in self.cached_books:
|
||||
if self.cached_books[book]['uuid'] == metadata.uuid or \
|
||||
(self.cached_books[book]['title'] == metadata.title and \
|
||||
self.cached_books[book]['author'] == metadata.authors[0]):
|
||||
self.cached_books[book]['author'] == authors_to_string(metadata.authors)):
|
||||
self.update_list.append(self.cached_books[book])
|
||||
self._remove_from_iTunes(self.cached_books[book])
|
||||
if DEBUG:
|
||||
@ -2945,7 +2954,7 @@ class ITUNES(DriverBase):
|
||||
pb = self.plugboard_func(self.DEVICE_PLUGBOARD_NAME, format, self.plugboards)
|
||||
newmi = book.deepcopy_metadata()
|
||||
newmi.template_to_attribute(book, pb)
|
||||
if DEBUG:
|
||||
if pb is not None and DEBUG:
|
||||
self.log.info(" transforming %s using %s:" % (format, pb))
|
||||
self.log.info(" title: %s %s" % (book.title, ">>> %s" %
|
||||
newmi.title if book.title != newmi.title else ''))
|
||||
@ -3062,7 +3071,7 @@ class ITUNES_ASYNC(ITUNES):
|
||||
|
||||
cached_books[this_book.path] = {
|
||||
'title':library_books[book].name(),
|
||||
'author':[library_books[book].artist()],
|
||||
'author':library_books[book].artist().split(' & '),
|
||||
'lib_book':library_books[book],
|
||||
'dev_book':None,
|
||||
'uuid': library_books[book].composer(),
|
||||
@ -3102,7 +3111,7 @@ class ITUNES_ASYNC(ITUNES):
|
||||
|
||||
cached_books[this_book.path] = {
|
||||
'title':library_books[book].Name,
|
||||
'author':library_books[book].Artist,
|
||||
'author':library_books[book].Artist.split(' & '),
|
||||
'lib_book':library_books[book],
|
||||
'uuid': library_books[book].Composer,
|
||||
'format': format
|
||||
@ -3288,7 +3297,7 @@ class Book(Metadata):
|
||||
See ebooks.metadata.book.base
|
||||
'''
|
||||
def __init__(self,title,author):
|
||||
Metadata.__init__(self, title, authors=[author])
|
||||
Metadata.__init__(self, title, authors=author.split(' & '))
|
||||
|
||||
@property
|
||||
def title_sorter(self):
|
||||
|
@ -52,6 +52,9 @@ class CHMInput(InputFormatPlugin):
|
||||
|
||||
metadata = get_metadata_from_reader(self._chm_reader)
|
||||
self._chm_reader.CloseCHM()
|
||||
#print tdir
|
||||
#from calibre import ipython
|
||||
#ipython()
|
||||
|
||||
odi = options.debug_pipeline
|
||||
options.debug_pipeline = None
|
||||
|
@ -147,7 +147,8 @@ class CHMReader(CHMFile):
|
||||
if self.hhc_path == '.hhc' and self.hhc_path not in files:
|
||||
from calibre import walk
|
||||
for x in walk(output_dir):
|
||||
if os.path.basename(x).lower() in ('index.htm', 'index.html'):
|
||||
if os.path.basename(x).lower() in ('index.htm', 'index.html',
|
||||
'contents.htm', 'contents.html'):
|
||||
self.hhc_path = os.path.relpath(x, output_dir)
|
||||
break
|
||||
|
||||
|
@ -12,6 +12,7 @@ from Queue import Empty
|
||||
|
||||
from calibre.customize.conversion import InputFormatPlugin, OptionRecommendation
|
||||
from calibre import extract, CurrentDir, prints
|
||||
from calibre.constants import filesystem_encoding
|
||||
from calibre.ptempfile import PersistentTemporaryDirectory
|
||||
from calibre.utils.ipc.server import Server
|
||||
from calibre.utils.ipc.job import ParallelJob
|
||||
@ -21,6 +22,10 @@ def extract_comic(path_to_comic_file):
|
||||
Un-archive the comic file.
|
||||
'''
|
||||
tdir = PersistentTemporaryDirectory(suffix='_comic_extract')
|
||||
if not isinstance(tdir, unicode):
|
||||
# Needed in case the zip file has wrongly encoded unicode file/dir
|
||||
# names
|
||||
tdir = tdir.decode(filesystem_encoding)
|
||||
extract(path_to_comic_file, tdir)
|
||||
return tdir
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
#define BUFFER 6000
|
||||
|
||||
#define MIN(x, y) ( ((x) < (y)) ? (x) : (y) )
|
||||
#define MAX(x, y) ( ((x) > (y)) ? (x) : (y) )
|
||||
|
||||
typedef unsigned short int Byte;
|
||||
typedef struct {
|
||||
@ -53,7 +54,7 @@ cpalmdoc_decompress(PyObject *self, PyObject *args) {
|
||||
// Map chars to bytes
|
||||
for (j = 0; j < input_len; j++)
|
||||
input[j] = (_input[j] < 0) ? _input[j]+256 : _input[j];
|
||||
output = (char *)PyMem_Malloc(sizeof(char)*BUFFER);
|
||||
output = (char *)PyMem_Malloc(sizeof(char)*(MAX(BUFFER, 5*input_len)));
|
||||
if (output == NULL) return PyErr_NoMemory();
|
||||
|
||||
while (i < input_len) {
|
||||
|
@ -294,8 +294,24 @@ class Source(Plugin):
|
||||
Excludes connectives and punctuation.
|
||||
'''
|
||||
if title:
|
||||
pat = re.compile(r'''[-,:;+!@#$%^&*(){}.`~"'\s\[\]/]''')
|
||||
title = pat.sub(' ', title)
|
||||
title_patterns = [(re.compile(pat, re.IGNORECASE), repl) for pat, repl in
|
||||
[
|
||||
# Remove things like: (2010) (Omnibus) etc.
|
||||
(r'(?i)[({\[](\d{4}|omnibus|anthology|hardcover|paperback|mass\s*market|edition|ed\.)[\])}]', ''),
|
||||
# Remove any strings that contain the substring edition inside
|
||||
# parentheses
|
||||
(r'(?i)[({\[].*?(edition|ed.).*?[\]})]', ''),
|
||||
# Remove commas used a separators in numbers
|
||||
(r'(\d+),(\d+)', r'\1\2'),
|
||||
# Remove hyphens only if they have whitespace before them
|
||||
(r'(\s-)', ' '),
|
||||
# Remove single quotes
|
||||
(r"'", ''),
|
||||
# Replace other special chars with a space
|
||||
(r'''[:,;+!@#$%^&*(){}.`~"\s\[\]/]''', ' ')
|
||||
]]
|
||||
for pat, repl in title_patterns:
|
||||
title = pat.sub(repl, title)
|
||||
tokens = title.split()
|
||||
for token in tokens:
|
||||
token = token.strip()
|
||||
|
@ -114,8 +114,12 @@ class ISBNMerge(object):
|
||||
|
||||
return self.results
|
||||
|
||||
def merge_metadata_results(self):
|
||||
' Merge results with identical title and authors '
|
||||
def merge_metadata_results(self, merge_on_identifiers=False):
|
||||
'''
|
||||
Merge results with identical title and authors or an identical
|
||||
identifier
|
||||
'''
|
||||
# First title/author
|
||||
groups = {}
|
||||
for result in self.results:
|
||||
title = lower(result.title if result.title else '')
|
||||
@ -135,6 +139,44 @@ class ISBNMerge(object):
|
||||
result = rgroup[0]
|
||||
self.results.append(result)
|
||||
|
||||
if merge_on_identifiers:
|
||||
# Now identifiers
|
||||
groups, empty = {}, []
|
||||
for result in self.results:
|
||||
key = set()
|
||||
for typ, val in result.identifiers.iteritems():
|
||||
if typ and val:
|
||||
key.add((typ, val))
|
||||
if key:
|
||||
key = frozenset(key)
|
||||
match = None
|
||||
for candidate in list(groups):
|
||||
if candidate.intersection(key):
|
||||
# We have at least one identifier in common
|
||||
match = candidate.union(key)
|
||||
results = groups.pop(candidate)
|
||||
results.append(result)
|
||||
groups[match] = results
|
||||
break
|
||||
if match is None:
|
||||
groups[key] = [result]
|
||||
else:
|
||||
empty.append(result)
|
||||
|
||||
if len(groups) != len(self.results):
|
||||
self.results = []
|
||||
for rgroup in groups.itervalues():
|
||||
rel = [r.average_source_relevance for r in rgroup]
|
||||
if len(rgroup) > 1:
|
||||
result = self.merge(rgroup, None, do_asr=False)
|
||||
result.average_source_relevance = sum(rel)/len(rel)
|
||||
elif rgroup:
|
||||
result = rgroup[0]
|
||||
self.results.append(result)
|
||||
|
||||
if empty:
|
||||
self.results.extend(empty)
|
||||
|
||||
self.results.sort(key=attrgetter('average_source_relevance'))
|
||||
|
||||
def merge_isbn_results(self):
|
||||
@ -408,7 +450,7 @@ if __name__ == '__main__': # tests {{{
|
||||
{'identifiers':{'isbn': '9780307459671'},
|
||||
'title':'Invisible Gorilla', 'authors':['Christopher Chabris']},
|
||||
[title_test('The Invisible Gorilla',
|
||||
exact=True), authors_test(['Christopher F. Chabris', 'Daniel Simons'])]
|
||||
exact=True), authors_test(['Christopher Chabris', 'Daniel Simons'])]
|
||||
|
||||
),
|
||||
|
||||
|
@ -15,14 +15,17 @@ from calibre.customize.ui import metadata_plugins
|
||||
from calibre import prints, sanitize_file_name2
|
||||
from calibre.ebooks.metadata import check_isbn
|
||||
from calibre.ebooks.metadata.sources.base import (create_log,
|
||||
get_cached_cover_urls)
|
||||
get_cached_cover_urls, msprefs)
|
||||
|
||||
def isbn_test(isbn):
|
||||
isbn_ = check_isbn(isbn)
|
||||
|
||||
def test(mi):
|
||||
misbn = check_isbn(mi.isbn)
|
||||
return misbn and misbn == isbn_
|
||||
if misbn and misbn == isbn_:
|
||||
return True
|
||||
prints('ISBN test failed. Expected: \'%s\' found \'%s\''%(isbn_, misbn))
|
||||
return False
|
||||
|
||||
return test
|
||||
|
||||
@ -32,8 +35,11 @@ def title_test(title, exact=False):
|
||||
|
||||
def test(mi):
|
||||
mt = mi.title.lower()
|
||||
return (exact and mt == title) or \
|
||||
(not exact and title in mt)
|
||||
if (exact and mt == title) or \
|
||||
(not exact and title in mt):
|
||||
return True
|
||||
prints('Title test failed. Expected: \'%s\' found \'%s\''%(title, mt))
|
||||
return False
|
||||
|
||||
return test
|
||||
|
||||
@ -42,7 +48,22 @@ def authors_test(authors):
|
||||
|
||||
def test(mi):
|
||||
au = set([x.lower() for x in mi.authors])
|
||||
return au == authors
|
||||
if msprefs['swap_author_names']:
|
||||
def revert_to_fn_ln(a):
|
||||
if ',' not in a:
|
||||
return a
|
||||
parts = a.split(',', 1)
|
||||
t = parts[-1]
|
||||
parts = parts[:-1]
|
||||
parts.insert(0, t)
|
||||
return ' '.join(parts)
|
||||
|
||||
au = set([revert_to_fn_ln(x) for x in au])
|
||||
|
||||
if au == authors:
|
||||
return True
|
||||
prints('Author test failed. Expected: \'%s\' found \'%s\''%(authors, au))
|
||||
return False
|
||||
|
||||
return test
|
||||
|
||||
|
@ -716,6 +716,7 @@ class MobiReader(object):
|
||||
ent_pat = re.compile(r'&(\S+?);')
|
||||
if elems:
|
||||
tocobj = TOC()
|
||||
found = False
|
||||
reached = False
|
||||
for x in root.iter():
|
||||
if x == elems[-1]:
|
||||
@ -732,7 +733,8 @@ class MobiReader(object):
|
||||
text = ent_pat.sub(entity_to_unicode, text)
|
||||
tocobj.add_item(toc.partition('#')[0], href[1:],
|
||||
text)
|
||||
if reached and x.get('class', None) == 'mbp_pagebreak':
|
||||
found = True
|
||||
if reached and found and x.get('class', None) == 'mbp_pagebreak':
|
||||
break
|
||||
if tocobj is not None:
|
||||
opf.set_toc(tocobj)
|
||||
|
@ -24,7 +24,7 @@ from calibre.translations.dynamic import translate
|
||||
from calibre.ebooks.chardet import xml_to_unicode
|
||||
from calibre.ebooks.oeb.entitydefs import ENTITYDEFS
|
||||
from calibre.ebooks.conversion.preprocess import CSSPreProcessor
|
||||
from calibre import isbytestring
|
||||
from calibre import isbytestring, as_unicode
|
||||
|
||||
RECOVER_PARSER = etree.XMLParser(recover=True, no_network=True)
|
||||
|
||||
@ -643,7 +643,7 @@ class Metadata(object):
|
||||
return unicode(self.value).encode('ascii', 'xmlcharrefreplace')
|
||||
|
||||
def __unicode__(self):
|
||||
return unicode(self.value)
|
||||
return as_unicode(self.value)
|
||||
|
||||
def to_opf1(self, dcmeta=None, xmeta=None, nsrmap={}):
|
||||
attrib = {}
|
||||
|
@ -648,6 +648,18 @@ def open_url(qurl):
|
||||
if isfrozen and islinux and paths:
|
||||
os.environ['LD_LIBRARY_PATH'] = os.pathsep.join(paths)
|
||||
|
||||
def get_current_db():
|
||||
'''
|
||||
This method will try to return the current database in use by the user as
|
||||
efficiently as possible, i.e. without constructing duplicate
|
||||
LibraryDatabase objects.
|
||||
'''
|
||||
from calibre.gui2.ui import get_gui
|
||||
gui = get_gui()
|
||||
if gui is not None and gui.current_db is not None:
|
||||
return gui.current_db
|
||||
from calibre.library import db
|
||||
return db()
|
||||
|
||||
def open_local_file(path):
|
||||
if iswindows:
|
||||
|
@ -17,7 +17,7 @@ from calibre.gui2.actions import InterfaceAction
|
||||
class GenerateCatalogAction(InterfaceAction):
|
||||
|
||||
name = 'Generate Catalog'
|
||||
action_spec = (_('Create a catalog of the books in your calibre library'), None, None, None)
|
||||
action_spec = (_('Create a catalog of the books in your calibre library'), 'catalog.png', 'Catalog builder', None)
|
||||
dont_add_to = frozenset(['menubar-device', 'toolbar-device', 'context-menu-device'])
|
||||
|
||||
def generate_catalog(self):
|
||||
|
@ -8,14 +8,14 @@ __docformat__ = 'restructuredtext en'
|
||||
|
||||
from functools import partial
|
||||
|
||||
from PyQt4.Qt import Qt, QMenu, QToolButton, QDialog, QVBoxLayout
|
||||
from PyQt4.Qt import QMenu
|
||||
|
||||
from calibre.gui2.actions import InterfaceAction
|
||||
|
||||
class StoreAction(InterfaceAction):
|
||||
|
||||
name = 'Store'
|
||||
action_spec = (_('Store'), 'store.png', None, None)
|
||||
action_spec = (_('Get books'), 'store.png', None, None)
|
||||
|
||||
def genesis(self):
|
||||
self.qaction.triggered.connect(self.search)
|
||||
|
@ -483,8 +483,15 @@ class BookDetails(QWidget): # {{{
|
||||
self.book_info.show_data(data)
|
||||
self.cover_view.show_data(data)
|
||||
self._layout.do_layout(self.rect())
|
||||
self.setToolTip('<p>'+_('Double-click to open Book Details window') +
|
||||
'<br><br>' + _('Path') + ': ' + data.get(_('Path'), ''))
|
||||
try:
|
||||
sz = self.cover_view.pixmap.size()
|
||||
except:
|
||||
sz = QSize(0, 0)
|
||||
self.setToolTip(
|
||||
'<p>'+_('Double-click to open Book Details window') +
|
||||
'<br><br>' + _('Path') + ': ' + data.get(_('Path'), '') +
|
||||
'<br><br>' + _('Cover size: %dx%d')%(sz.width(), sz.height())
|
||||
)
|
||||
|
||||
def reset_info(self):
|
||||
self.show_data({})
|
||||
|
@ -109,6 +109,8 @@ class BookInfo(QDialog, Ui_BookInfo):
|
||||
pixmap = pixmap.scaled(new_width, new_height,
|
||||
Qt.KeepAspectRatio, Qt.SmoothTransformation)
|
||||
self.cover.set_pixmap(pixmap)
|
||||
sz = pixmap.size()
|
||||
self.cover.setToolTip(_('Cover size: %dx%d')%(sz.width(), sz.height()))
|
||||
|
||||
def refresh(self, row):
|
||||
if isinstance(row, QModelIndex):
|
||||
|
@ -68,7 +68,7 @@ class DaysOfWeek(Base):
|
||||
def initialize(self, typ=None, val=None):
|
||||
if typ is None:
|
||||
typ = 'day/time'
|
||||
val = (-1, 9, 0)
|
||||
val = (-1, 6, 0)
|
||||
if typ == 'day/time':
|
||||
val = convert_day_time_schedule(val)
|
||||
|
||||
@ -118,7 +118,7 @@ class DaysOfMonth(Base):
|
||||
|
||||
def initialize(self, typ=None, val=None):
|
||||
if val is None:
|
||||
val = ((1,), 9, 0)
|
||||
val = ((1,), 6, 0)
|
||||
days_of_month, hour, minute = val
|
||||
self.days.setText(', '.join(map(str, map(int, days_of_month))))
|
||||
self.time.setTime(QTime(hour, minute))
|
||||
@ -380,7 +380,7 @@ class SchedulerDialog(QDialog, Ui_Dialog):
|
||||
if d < timedelta(days=366):
|
||||
ld_text = tm
|
||||
else:
|
||||
typ, sch = 'day/time', (-1, 9, 0)
|
||||
typ, sch = 'day/time', (-1, 6, 0)
|
||||
sch_widget = {'day/time': 0, 'days_of_week': 0, 'days_of_month':1,
|
||||
'interval':2}[typ]
|
||||
rb = getattr(self, list(self.SCHEDULE_TYPES)[sch_widget])
|
||||
|
@ -12,6 +12,7 @@ from zipfile import ZipFile, ZIP_DEFLATED, ZIP_STORED
|
||||
|
||||
from PyQt4.Qt import QDialog
|
||||
|
||||
from calibre.constants import isosx, iswindows
|
||||
from calibre.gui2 import open_local_file
|
||||
from calibre.gui2.dialogs.tweak_epub_ui import Ui_Dialog
|
||||
from calibre.libunzip import extract as zipextract
|
||||
@ -42,11 +43,19 @@ class TweakEpub(QDialog, Ui_Dialog):
|
||||
self.move(parent_loc.x(),parent_loc.y())
|
||||
|
||||
def cleanup(self):
|
||||
if isosx:
|
||||
try:
|
||||
import appscript
|
||||
self.finder = appscript.app('Finder')
|
||||
self.finder.Finder_windows[os.path.basename(self._exploded)].close()
|
||||
except:
|
||||
# appscript fails to load on 10.4
|
||||
pass
|
||||
|
||||
# Delete directory containing exploded ePub
|
||||
if self._exploded is not None:
|
||||
shutil.rmtree(self._exploded, ignore_errors=True)
|
||||
|
||||
|
||||
def display_exploded(self):
|
||||
'''
|
||||
Generic subprocess launch of native file browser
|
||||
|
@ -317,6 +317,8 @@ class BaseToolBar(QToolBar): # {{{
|
||||
QToolBar.resizeEvent(self, ev)
|
||||
style = self.get_text_style()
|
||||
self.setToolButtonStyle(style)
|
||||
if hasattr(self, 'd_widget') and hasattr(self.d_widget, 'filler'):
|
||||
self.d_widget.filler.setVisible(style != Qt.ToolButtonIconOnly)
|
||||
|
||||
def get_text_style(self):
|
||||
style = Qt.ToolButtonTextUnderIcon
|
||||
@ -399,7 +401,10 @@ class ToolBar(BaseToolBar): # {{{
|
||||
self.d_widget.layout().addWidget(self.donate_button)
|
||||
if isosx:
|
||||
self.d_widget.setStyleSheet('QWidget, QToolButton {background-color: none; border: none; }')
|
||||
self.d_widget.layout().addWidget(QLabel(u'\u00a0'))
|
||||
self.d_widget.layout().setContentsMargins(0,0,0,0)
|
||||
self.d_widget.setContentsMargins(0,0,0,0)
|
||||
self.d_widget.filler = QLabel(u'\u00a0')
|
||||
self.d_widget.layout().addWidget(self.d_widget.filler)
|
||||
bar.addWidget(self.d_widget)
|
||||
self.showing_donate = True
|
||||
elif what in self.gui.iactions:
|
||||
|
@ -223,7 +223,7 @@ class AuthorSortEdit(EnLineEdit):
|
||||
LABEL = _('Author s&ort:')
|
||||
|
||||
def __init__(self, parent, authors_edit, autogen_button, db,
|
||||
copy_as_to_a_action):
|
||||
copy_a_to_as_action, copy_as_to_a_action):
|
||||
EnLineEdit.__init__(self, parent)
|
||||
self.authors_edit = authors_edit
|
||||
self.db = db
|
||||
@ -242,6 +242,7 @@ class AuthorSortEdit(EnLineEdit):
|
||||
self.textChanged.connect(self.update_state)
|
||||
|
||||
autogen_button.clicked.connect(self.auto_generate)
|
||||
copy_a_to_as_action.triggered.connect(self.auto_generate)
|
||||
copy_as_to_a_action.triggered.connect(self.copy_to_authors)
|
||||
self.update_state()
|
||||
|
||||
|
@ -109,10 +109,12 @@ class MetadataSingleDialogBase(ResizableDialog):
|
||||
'Using this button to create author sort will change author sort from'
|
||||
' red to green.'))
|
||||
b.m = m = QMenu()
|
||||
ac = m.addAction(QIcon(I('back.png')), _('Set author from author sort'))
|
||||
ac = m.addAction(QIcon(I('forward.png')), _('Set author sort from author'))
|
||||
ac2 = m.addAction(QIcon(I('back.png')), _('Set author from author sort'))
|
||||
b.setMenu(m)
|
||||
self.authors = AuthorsEdit(self)
|
||||
self.author_sort = AuthorSortEdit(self, self.authors, b, self.db, ac)
|
||||
self.author_sort = AuthorSortEdit(self, self.authors, b, self.db, ac,
|
||||
ac2)
|
||||
self.basic_metadata_widgets.extend([self.authors, self.author_sort])
|
||||
|
||||
self.swap_title_author_button = QToolButton(self)
|
||||
|
@ -319,9 +319,12 @@ def show_config_widget(category, name, gui=None, show_restart_msg=False,
|
||||
:return: True iff a restart is required for the changes made by the user to
|
||||
take effect
|
||||
'''
|
||||
from calibre.gui2 import gprefs
|
||||
pl = get_plugin(category, name)
|
||||
d = ConfigDialog(parent)
|
||||
d.resize(750, 550)
|
||||
conf_name = 'config_widget_dialog_geometry_%s_%s'%(category, name)
|
||||
geom = gprefs.get(conf_name, None)
|
||||
d.setWindowTitle(_('Configure ') + name)
|
||||
d.setWindowIcon(QIcon(I('config.png')))
|
||||
bb = QDialogButtonBox(d)
|
||||
@ -345,7 +348,11 @@ def show_config_widget(category, name, gui=None, show_restart_msg=False,
|
||||
mygui = True
|
||||
w.genesis(gui)
|
||||
w.initialize()
|
||||
if geom is not None:
|
||||
d.restoreGeometry(geom)
|
||||
d.exec_()
|
||||
geom = bytearray(d.saveGeometry())
|
||||
gprefs[conf_name] = geom
|
||||
rr = getattr(d, 'restart_required', False)
|
||||
if show_restart_msg and rr:
|
||||
from calibre.gui2 import warning_dialog
|
||||
|
@ -73,13 +73,13 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
||||
choices=sorted(list(choices), key=sort_key))
|
||||
|
||||
|
||||
self.current_font = None
|
||||
self.current_font = self.initial_font = None
|
||||
self.change_font_button.clicked.connect(self.change_font)
|
||||
|
||||
|
||||
def initialize(self):
|
||||
ConfigWidgetBase.initialize(self)
|
||||
self.current_font = gprefs['font']
|
||||
self.current_font = self.initial_font = gprefs['font']
|
||||
self.update_font_display()
|
||||
|
||||
def restore_defaults(self):
|
||||
@ -119,7 +119,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
||||
|
||||
def commit(self, *args):
|
||||
rr = ConfigWidgetBase.commit(self, *args)
|
||||
if self.current_font != gprefs['font']:
|
||||
if self.current_font != self.initial_font:
|
||||
gprefs['font'] = self.current_font
|
||||
QApplication.setFont(self.font_display.font())
|
||||
rr = True
|
||||
|
@ -6,7 +6,6 @@ __license__ = 'GPL 3'
|
||||
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import re
|
||||
import urllib2
|
||||
from contextlib import closing
|
||||
|
||||
|
@ -13,9 +13,8 @@ from random import shuffle
|
||||
from threading import Thread
|
||||
from Queue import Queue
|
||||
|
||||
from PyQt4.Qt import Qt, QAbstractItemModel, QDialog, QTimer, QVariant, \
|
||||
QModelIndex, QPixmap, QSize, QCheckBox, QVBoxLayout, QHBoxLayout, \
|
||||
QPushButton, QString, QByteArray
|
||||
from PyQt4.Qt import (Qt, QAbstractItemModel, QDialog, QTimer, QVariant,
|
||||
QModelIndex, QPixmap, QSize, QCheckBox, QVBoxLayout)
|
||||
|
||||
from calibre import browser
|
||||
from calibre.gui2 import NONE
|
||||
|
@ -9,8 +9,8 @@ __docformat__ = 'restructuredtext en'
|
||||
import os
|
||||
from urlparse import urlparse
|
||||
|
||||
from PyQt4.Qt import QWebView, QWebPage, QNetworkCookieJar, QNetworkRequest, QString, \
|
||||
QFileDialog, QNetworkProxy
|
||||
from PyQt4.Qt import QNetworkCookieJar, QFileDialog, QNetworkProxy
|
||||
from PyQt4.QtWebKit import QWebView, QWebPage
|
||||
|
||||
from calibre import USER_AGENT, get_proxies, get_download_filename
|
||||
from calibre.ebooks import BOOK_EXTENSIONS
|
||||
|
@ -88,6 +88,11 @@ class SystemTrayIcon(QSystemTrayIcon): # {{{
|
||||
|
||||
# }}}
|
||||
|
||||
_gui = None
|
||||
|
||||
def get_gui():
|
||||
return _gui
|
||||
|
||||
class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
||||
TagBrowserMixin, CoverFlowMixin, LibraryViewMixin, SearchBoxMixin,
|
||||
SavedSearchBoxMixin, SearchRestrictionMixin, LayoutMixin, UpdateMixin,
|
||||
@ -97,7 +102,9 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
||||
|
||||
|
||||
def __init__(self, opts, parent=None, gui_debug=None):
|
||||
global _gui
|
||||
MainWindow.__init__(self, opts, parent=parent, disable_automatic_gc=True)
|
||||
_gui = self
|
||||
self.opts = opts
|
||||
self.device_connected = None
|
||||
self.gui_debug = gui_debug
|
||||
|
@ -426,7 +426,7 @@ def do_show_metadata(db, id, as_opf):
|
||||
mi = OPFCreator(os.getcwd(), mi)
|
||||
mi.render(sys.stdout)
|
||||
else:
|
||||
print unicode(mi).encode(preferred_encoding)
|
||||
prints(unicode(mi))
|
||||
|
||||
def show_metadata_option_parser():
|
||||
parser = get_parser(_(
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user