mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-06-23 15:30:45 -04:00
Sync to trunk.
This commit is contained in:
commit
60e8fb65bf
@ -5,8 +5,8 @@ class AdvancedUserRecipe1306097511(BasicNewsRecipe):
|
|||||||
description = 'News as provide by The Metro -UK'
|
description = 'News as provide by The Metro -UK'
|
||||||
|
|
||||||
__author__ = 'Dave Asbury'
|
__author__ = 'Dave Asbury'
|
||||||
|
#last update 3/12/11
|
||||||
cover_url = 'http://profile.ak.fbcdn.net/hprofile-ak-snc4/276636_117118184990145_2132092232_n.jpg'
|
cover_url = 'http://profile.ak.fbcdn.net/hprofile-ak-snc4/276636_117118184990145_2132092232_n.jpg'
|
||||||
|
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
oldest_article = 1
|
oldest_article = 1
|
||||||
max_articles_per_feed = 20
|
max_articles_per_feed = 20
|
||||||
@ -32,9 +32,11 @@ class AdvancedUserRecipe1306097511(BasicNewsRecipe):
|
|||||||
dict(name='div', attrs={'class':'art-lft'}),
|
dict(name='div', attrs={'class':'art-lft'}),
|
||||||
dict(name='p')
|
dict(name='p')
|
||||||
]
|
]
|
||||||
remove_tags = [dict(name='div', attrs={'class':[ 'news m12 clrd clr-b p5t shareBtm', 'commentForm', 'metroCommentInnerWrap',
|
remove_tags = [
|
||||||
'art-rgt','pluck-app pluck-comm','news m12 clrd clr-l p5t', 'flt-r' ]}),
|
dict(name = 'div',attrs={'id' : ['comments-news','formSubmission']}),
|
||||||
dict(attrs={'class':[ 'metroCommentFormWrap','commentText','commentsNav','avatar','submDateAndTime']})
|
dict(name='div', attrs={'class':[ 'news m12 clrd clr-b p5t shareBtm', 'commentForm', 'metroCommentInnerWrap',
|
||||||
|
'art-rgt','pluck-app pluck-comm','news m12 clrd clr-l p5t', 'flt-r','username','clrd' ]}),
|
||||||
|
dict(attrs={'class':['username', 'metroCommentFormWrap','commentText','commentsNav','avatar','submDateAndTime','addYourComment','displayName']})
|
||||||
,dict(name='div', attrs={'class' : 'clrd art-fd fd-gr1-b'})
|
,dict(name='div', attrs={'class' : 'clrd art-fd fd-gr1-b'})
|
||||||
]
|
]
|
||||||
feeds = [
|
feeds = [
|
||||||
|
@ -7,11 +7,14 @@ class TVXS(BasicNewsRecipe):
|
|||||||
__author__ = 'hargikas'
|
__author__ = 'hargikas'
|
||||||
description = 'News from Greece'
|
description = 'News from Greece'
|
||||||
max_articles_per_feed = 100
|
max_articles_per_feed = 100
|
||||||
oldest_article = 100
|
oldest_article = 3
|
||||||
|
simultaneous_downloads = 1
|
||||||
publisher = 'TVXS'
|
publisher = 'TVXS'
|
||||||
category = 'news, GR'
|
category = 'news, GR'
|
||||||
language = 'el'
|
language = 'el'
|
||||||
encoding = None
|
encoding = None
|
||||||
|
use_embedded_content = False
|
||||||
|
remove_empty_feeds = True
|
||||||
#conversion_options = { 'linearize_tables': True}
|
#conversion_options = { 'linearize_tables': True}
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
remove_tags_before = dict(name='h1',attrs={'class':'print-title'})
|
remove_tags_before = dict(name='h1',attrs={'class':'print-title'})
|
||||||
|
@ -131,7 +131,7 @@ class ZeitEPUBAbo(BasicNewsRecipe):
|
|||||||
browser.form['pass']=self.password
|
browser.form['pass']=self.password
|
||||||
browser.submit()
|
browser.submit()
|
||||||
# now find the correct file, we will still use the ePub file
|
# now find the correct file, we will still use the ePub file
|
||||||
epublink = browser.find_link(text_regex=re.compile('.*Ausgabe als Datei im ePub-Format.*'))
|
epublink = browser.find_link(text_regex=re.compile('.*Download als Datei im ePub-Format für eReader.*'))
|
||||||
response = browser.follow_link(epublink)
|
response = browser.follow_link(epublink)
|
||||||
self.report_progress(1,_('next step'))
|
self.report_progress(1,_('next step'))
|
||||||
|
|
||||||
|
@ -224,7 +224,7 @@ class TREKSTOR(USBMS):
|
|||||||
FORMATS = ['epub', 'txt', 'pdf']
|
FORMATS = ['epub', 'txt', 'pdf']
|
||||||
|
|
||||||
VENDOR_ID = [0x1e68]
|
VENDOR_ID = [0x1e68]
|
||||||
PRODUCT_ID = [0x0041, 0x0042, 0x0052,
|
PRODUCT_ID = [0x0041, 0x0042, 0x0052, 0x004e,
|
||||||
0x003e # This is for the EBOOK_PLAYER_5M https://bugs.launchpad.net/bugs/792091
|
0x003e # This is for the EBOOK_PLAYER_5M https://bugs.launchpad.net/bugs/792091
|
||||||
]
|
]
|
||||||
BCD = [0x0002]
|
BCD = [0x0002]
|
||||||
|
@ -296,6 +296,13 @@ class PRST1(USBMS):
|
|||||||
lpath = row[0].replace('\\', '/')
|
lpath = row[0].replace('\\', '/')
|
||||||
db_books[lpath] = row[1]
|
db_books[lpath] = row[1]
|
||||||
|
|
||||||
|
# Work-around for Sony Bug (SD Card DB not using right SQLite sequence)
|
||||||
|
if source_id == 1:
|
||||||
|
sdcard_sequence_start = '4294967296'
|
||||||
|
query = 'UPDATE sqlite_sequence SET seq = ? WHERE seq < ?'
|
||||||
|
t = (sdcard_sequence_start, sdcard_sequence_start,)
|
||||||
|
cursor.execute(query, t)
|
||||||
|
|
||||||
for book in booklist:
|
for book in booklist:
|
||||||
# Run through plugboard if needed
|
# Run through plugboard if needed
|
||||||
if plugboard is not None:
|
if plugboard is not None:
|
||||||
@ -322,12 +329,10 @@ class PRST1(USBMS):
|
|||||||
title = newmi.title or _('Unknown')
|
title = newmi.title or _('Unknown')
|
||||||
|
|
||||||
# Get modified date
|
# Get modified date
|
||||||
|
# If there was a detected offset, use that. Otherwise use UTC (same as Sony software)
|
||||||
modified_date = os.path.getmtime(book.path) * 1000
|
modified_date = os.path.getmtime(book.path) * 1000
|
||||||
if self.device_offset is not None:
|
if self.device_offset is not None:
|
||||||
modified_date = modified_date + self.device_offset
|
modified_date = modified_date + self.device_offset
|
||||||
else:
|
|
||||||
time_offset = -time.altzone if time.daylight else -time.timezone
|
|
||||||
modified_date = modified_date + (time_offset * 1000)
|
|
||||||
|
|
||||||
if lpath not in db_books:
|
if lpath not in db_books:
|
||||||
query = '''
|
query = '''
|
||||||
@ -578,17 +583,17 @@ class PRST1(USBMS):
|
|||||||
# Setting this to the SONY periodical schema apparently causes errors
|
# Setting this to the SONY periodical schema apparently causes errors
|
||||||
# with some periodicals, therefore set it to null, since the special
|
# with some periodicals, therefore set it to null, since the special
|
||||||
# periodical navigation doesn't work anyway.
|
# periodical navigation doesn't work anyway.
|
||||||
periodical_schema = 'null'
|
periodical_schema = None
|
||||||
|
|
||||||
query = '''
|
query = '''
|
||||||
UPDATE books
|
UPDATE books
|
||||||
SET conforms_to = %s,
|
SET conforms_to = ?,
|
||||||
periodical_name = ?,
|
periodical_name = ?,
|
||||||
description = ?,
|
description = ?,
|
||||||
publication_date = ?
|
publication_date = ?
|
||||||
WHERE _id = ?
|
WHERE _id = ?
|
||||||
'''%periodical_schema
|
'''
|
||||||
t = (name, None, pubdate, book.bookId,)
|
t = (periodical_schema, name, None, pubdate, book.bookId,)
|
||||||
cursor.execute(query, t)
|
cursor.execute(query, t)
|
||||||
|
|
||||||
connection.commit()
|
connection.commit()
|
||||||
|
@ -53,7 +53,6 @@ def substitute_entites(raw):
|
|||||||
_CHARSET_ALIASES = { "macintosh" : "mac-roman",
|
_CHARSET_ALIASES = { "macintosh" : "mac-roman",
|
||||||
"x-sjis" : "shift-jis" }
|
"x-sjis" : "shift-jis" }
|
||||||
|
|
||||||
|
|
||||||
def force_encoding(raw, verbose, assume_utf8=False):
|
def force_encoding(raw, verbose, assume_utf8=False):
|
||||||
from calibre.constants import preferred_encoding
|
from calibre.constants import preferred_encoding
|
||||||
try:
|
try:
|
||||||
@ -74,6 +73,36 @@ def force_encoding(raw, verbose, assume_utf8=False):
|
|||||||
encoding = 'utf-8'
|
encoding = 'utf-8'
|
||||||
return encoding
|
return encoding
|
||||||
|
|
||||||
|
def detect_xml_encoding(raw, verbose=False, assume_utf8=False):
|
||||||
|
if not raw or isinstance(raw, unicode):
|
||||||
|
return raw, None
|
||||||
|
for x in ('utf8', 'utf-16-le', 'utf-16-be'):
|
||||||
|
bom = getattr(codecs, 'BOM_'+x.upper().replace('-16', '16').replace(
|
||||||
|
'-', '_'))
|
||||||
|
if raw.startswith(bom):
|
||||||
|
return raw[len(bom):], x
|
||||||
|
encoding = None
|
||||||
|
for pat in ENCODING_PATS:
|
||||||
|
match = pat.search(raw)
|
||||||
|
if match:
|
||||||
|
encoding = match.group(1)
|
||||||
|
break
|
||||||
|
if encoding is None:
|
||||||
|
encoding = force_encoding(raw, verbose, assume_utf8=assume_utf8)
|
||||||
|
if encoding.lower().strip() == 'macintosh':
|
||||||
|
encoding = 'mac-roman'
|
||||||
|
if encoding.lower().replace('_', '-').strip() in (
|
||||||
|
'gb2312', 'chinese', 'csiso58gb231280', 'euc-cn', 'euccn',
|
||||||
|
'eucgb2312-cn', 'gb2312-1980', 'gb2312-80', 'iso-ir-58'):
|
||||||
|
# Microsoft Word exports to HTML with encoding incorrectly set to
|
||||||
|
# gb2312 instead of gbk. gbk is a superset of gb2312, anyway.
|
||||||
|
encoding = 'gbk'
|
||||||
|
try:
|
||||||
|
codecs.lookup(encoding)
|
||||||
|
except LookupError:
|
||||||
|
encoding = 'utf-8'
|
||||||
|
|
||||||
|
return raw, encoding
|
||||||
|
|
||||||
def xml_to_unicode(raw, verbose=False, strip_encoding_pats=False,
|
def xml_to_unicode(raw, verbose=False, strip_encoding_pats=False,
|
||||||
resolve_entities=False, assume_utf8=False):
|
resolve_entities=False, assume_utf8=False):
|
||||||
@ -83,36 +112,11 @@ def xml_to_unicode(raw, verbose=False, strip_encoding_pats=False,
|
|||||||
prints a warning if detection confidence is < 100%
|
prints a warning if detection confidence is < 100%
|
||||||
@return: (unicode, encoding used)
|
@return: (unicode, encoding used)
|
||||||
'''
|
'''
|
||||||
encoding = None
|
|
||||||
if not raw:
|
if not raw:
|
||||||
return u'', encoding
|
return u'', None
|
||||||
|
raw, encoding = detect_xml_encoding(raw, verbose=verbose,
|
||||||
|
assume_utf8=assume_utf8)
|
||||||
if not isinstance(raw, unicode):
|
if not isinstance(raw, unicode):
|
||||||
if raw.startswith(codecs.BOM_UTF8):
|
|
||||||
raw, encoding = raw.decode('utf-8')[1:], 'utf-8'
|
|
||||||
elif raw.startswith(codecs.BOM_UTF16_LE):
|
|
||||||
raw, encoding = raw.decode('utf-16-le')[1:], 'utf-16-le'
|
|
||||||
elif raw.startswith(codecs.BOM_UTF16_BE):
|
|
||||||
raw, encoding = raw.decode('utf-16-be')[1:], 'utf-16-be'
|
|
||||||
if not isinstance(raw, unicode):
|
|
||||||
for pat in ENCODING_PATS:
|
|
||||||
match = pat.search(raw)
|
|
||||||
if match:
|
|
||||||
encoding = match.group(1)
|
|
||||||
break
|
|
||||||
if encoding is None:
|
|
||||||
encoding = force_encoding(raw, verbose, assume_utf8=assume_utf8)
|
|
||||||
try:
|
|
||||||
if encoding.lower().strip() == 'macintosh':
|
|
||||||
encoding = 'mac-roman'
|
|
||||||
if encoding.lower().replace('_', '-').strip() in (
|
|
||||||
'gb2312', 'chinese', 'csiso58gb231280', 'euc-cn', 'euccn',
|
|
||||||
'eucgb2312-cn', 'gb2312-1980', 'gb2312-80', 'iso-ir-58'):
|
|
||||||
# Microsoft Word exports to HTML with encoding incorrectly set to
|
|
||||||
# gb2312 instead of gbk. gbk is a superset of gb2312, anyway.
|
|
||||||
encoding = 'gbk'
|
|
||||||
raw = raw.decode(encoding, 'replace')
|
|
||||||
except LookupError:
|
|
||||||
encoding = 'utf-8'
|
|
||||||
raw = raw.decode(encoding, 'replace')
|
raw = raw.decode(encoding, 'replace')
|
||||||
|
|
||||||
if strip_encoding_pats:
|
if strip_encoding_pats:
|
||||||
@ -120,6 +124,4 @@ def xml_to_unicode(raw, verbose=False, strip_encoding_pats=False,
|
|||||||
if resolve_entities:
|
if resolve_entities:
|
||||||
raw = substitute_entites(raw)
|
raw = substitute_entites(raw)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return raw, encoding
|
return raw, encoding
|
||||||
|
@ -18,7 +18,7 @@ from functools import partial
|
|||||||
from itertools import izip
|
from itertools import izip
|
||||||
|
|
||||||
from calibre.customize.conversion import InputFormatPlugin
|
from calibre.customize.conversion import InputFormatPlugin
|
||||||
from calibre.ebooks.chardet import xml_to_unicode
|
from calibre.ebooks.chardet import detect_xml_encoding
|
||||||
from calibre.customize.conversion import OptionRecommendation
|
from calibre.customize.conversion import OptionRecommendation
|
||||||
from calibre.constants import islinux, isbsd, iswindows
|
from calibre.constants import islinux, isbsd, iswindows
|
||||||
from calibre import unicode_path, as_unicode
|
from calibre import unicode_path, as_unicode
|
||||||
@ -121,7 +121,7 @@ class HTMLFile(object):
|
|||||||
|
|
||||||
if not self.is_binary:
|
if not self.is_binary:
|
||||||
if not encoding:
|
if not encoding:
|
||||||
encoding = xml_to_unicode(src[:4096], verbose=verbose)[-1]
|
encoding = detect_xml_encoding(src[:4096], verbose=verbose)[1]
|
||||||
self.encoding = encoding
|
self.encoding = encoding
|
||||||
else:
|
else:
|
||||||
self.encoding = encoding
|
self.encoding = encoding
|
||||||
|
@ -30,6 +30,8 @@ CONTENT_TAGS = set(['img', 'hr', 'br'])
|
|||||||
|
|
||||||
NOT_VTAGS = HEADER_TAGS | NESTABLE_TAGS | TABLE_TAGS | SPECIAL_TAGS | \
|
NOT_VTAGS = HEADER_TAGS | NESTABLE_TAGS | TABLE_TAGS | SPECIAL_TAGS | \
|
||||||
CONTENT_TAGS
|
CONTENT_TAGS
|
||||||
|
LEAF_TAGS = set(['base', 'basefont', 'frame', 'link', 'meta', 'area', 'br',
|
||||||
|
'col', 'hr', 'img', 'input', 'param'])
|
||||||
PAGE_BREAKS = set(['always', 'left', 'right'])
|
PAGE_BREAKS = set(['always', 'left', 'right'])
|
||||||
|
|
||||||
COLLAPSE = re.compile(r'[ \t\r\n\v]+')
|
COLLAPSE = re.compile(r'[ \t\r\n\v]+')
|
||||||
@ -246,7 +248,17 @@ class MobiMLizer(object):
|
|||||||
last.text = None
|
last.text = None
|
||||||
else:
|
else:
|
||||||
last = bstate.body[-1]
|
last = bstate.body[-1]
|
||||||
|
# We use append instead of addprevious so that inline
|
||||||
|
# anchors in large blocks point to the correct place. See
|
||||||
|
# https://bugs.launchpad.net/calibre/+bug/899831
|
||||||
|
# This could potentially break if inserting an anchor at
|
||||||
|
# this point in the markup is illegal, but I cannot think
|
||||||
|
# of such a case offhand.
|
||||||
|
if barename(last.tag) in LEAF_TAGS:
|
||||||
last.addprevious(anchor)
|
last.addprevious(anchor)
|
||||||
|
else:
|
||||||
|
last.append(anchor)
|
||||||
|
|
||||||
istate.ids.clear()
|
istate.ids.clear()
|
||||||
if not text:
|
if not text:
|
||||||
return
|
return
|
||||||
@ -528,7 +540,11 @@ class MobiMLizer(object):
|
|||||||
old_mim = self.opts.mobi_ignore_margins
|
old_mim = self.opts.mobi_ignore_margins
|
||||||
self.opts.mobi_ignore_margins = False
|
self.opts.mobi_ignore_margins = False
|
||||||
|
|
||||||
if text or tag in CONTENT_TAGS or tag in NESTABLE_TAGS:
|
if (text or tag in CONTENT_TAGS or tag in NESTABLE_TAGS or (
|
||||||
|
# We have an id but no text and no children, the id should still
|
||||||
|
# be added.
|
||||||
|
istate.ids and tag in ('a', 'span', 'i', 'b', 'u') and
|
||||||
|
len(elem)==0)):
|
||||||
self.mobimlize_content(tag, text, bstate, istates)
|
self.mobimlize_content(tag, text, bstate, istates)
|
||||||
for child in elem:
|
for child in elem:
|
||||||
self.mobimlize_elem(child, stylizer, bstate, istates)
|
self.mobimlize_elem(child, stylizer, bstate, istates)
|
||||||
|
@ -18,7 +18,8 @@ from calibre.ebooks.chardet import xml_to_unicode
|
|||||||
from calibre.utils.zipfile import safe_replace
|
from calibre.utils.zipfile import safe_replace
|
||||||
from calibre.utils.config import DynamicConfig
|
from calibre.utils.config import DynamicConfig
|
||||||
from calibre.utils.logging import Log
|
from calibre.utils.logging import Log
|
||||||
from calibre import guess_type, prints, prepare_string_for_xml
|
from calibre import (guess_type, prints, prepare_string_for_xml,
|
||||||
|
xml_replace_entities)
|
||||||
from calibre.ebooks.oeb.transforms.cover import CoverManager
|
from calibre.ebooks.oeb.transforms.cover import CoverManager
|
||||||
from calibre.constants import filesystem_encoding
|
from calibre.constants import filesystem_encoding
|
||||||
|
|
||||||
@ -96,13 +97,19 @@ class EbookIterator(object):
|
|||||||
self.ebook_ext = ext.replace('original_', '')
|
self.ebook_ext = ext.replace('original_', '')
|
||||||
|
|
||||||
def search(self, text, index, backwards=False):
|
def search(self, text, index, backwards=False):
|
||||||
text = text.lower()
|
text = prepare_string_for_xml(text.lower())
|
||||||
pmap = [(i, path) for i, path in enumerate(self.spine)]
|
pmap = [(i, path) for i, path in enumerate(self.spine)]
|
||||||
if backwards:
|
if backwards:
|
||||||
pmap.reverse()
|
pmap.reverse()
|
||||||
for i, path in pmap:
|
for i, path in pmap:
|
||||||
if (backwards and i < index) or (not backwards and i > index):
|
if (backwards and i < index) or (not backwards and i > index):
|
||||||
if text in open(path, 'rb').read().decode(path.encoding).lower():
|
with open(path, 'rb') as f:
|
||||||
|
raw = f.read().decode(path.encoding)
|
||||||
|
try:
|
||||||
|
raw = xml_replace_entities(raw)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
if text in raw.lower():
|
||||||
return i
|
return i
|
||||||
|
|
||||||
def find_missing_css_files(self):
|
def find_missing_css_files(self):
|
||||||
|
@ -9,7 +9,8 @@ import os
|
|||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
from PyQt4.Qt import (QMenu, Qt, QInputDialog, QToolButton, QDialog,
|
from PyQt4.Qt import (QMenu, Qt, QInputDialog, QToolButton, QDialog,
|
||||||
QDialogButtonBox, QGridLayout, QLabel, QLineEdit, QIcon, QSize)
|
QDialogButtonBox, QGridLayout, QLabel, QLineEdit, QIcon, QSize,
|
||||||
|
QCoreApplication)
|
||||||
|
|
||||||
from calibre import isbytestring
|
from calibre import isbytestring
|
||||||
from calibre.constants import filesystem_encoding, iswindows
|
from calibre.constants import filesystem_encoding, iswindows
|
||||||
@ -384,11 +385,18 @@ class ChooseLibraryAction(InterfaceAction):
|
|||||||
_('Database integrity check failed, click Show details'
|
_('Database integrity check failed, click Show details'
|
||||||
' for details.'), show=True, det_msg=d.error[1])
|
' for details.'), show=True, det_msg=d.error[1])
|
||||||
|
|
||||||
|
self.gui.status_bar.show_message(
|
||||||
|
_('Starting library scan, this may take a while'))
|
||||||
|
try:
|
||||||
|
QCoreApplication.processEvents()
|
||||||
d = CheckLibraryDialog(self.gui, m.db)
|
d = CheckLibraryDialog(self.gui, m.db)
|
||||||
|
|
||||||
if not d.do_exec():
|
if not d.do_exec():
|
||||||
info_dialog(self.gui, _('No problems found'),
|
info_dialog(self.gui, _('No problems found'),
|
||||||
_('The files in your library match the information '
|
_('The files in your library match the information '
|
||||||
'in the database.'), show=True)
|
'in the database.'), show=True)
|
||||||
|
finally:
|
||||||
|
self.gui.status_bar.clear_message()
|
||||||
|
|
||||||
def switch_requested(self, location):
|
def switch_requested(self, location):
|
||||||
if not self.change_library_allowed():
|
if not self.change_library_allowed():
|
||||||
|
@ -9,7 +9,7 @@ from PyQt4.Qt import QThread, QObject, Qt, QProgressDialog, pyqtSignal, QTimer
|
|||||||
|
|
||||||
from calibre.gui2.dialogs.progress import ProgressDialog
|
from calibre.gui2.dialogs.progress import ProgressDialog
|
||||||
from calibre.gui2 import (question_dialog, error_dialog, info_dialog, gprefs,
|
from calibre.gui2 import (question_dialog, error_dialog, info_dialog, gprefs,
|
||||||
warning_dialog)
|
warning_dialog, available_width)
|
||||||
from calibre.ebooks.metadata.opf2 import OPF
|
from calibre.ebooks.metadata.opf2 import OPF
|
||||||
from calibre.ebooks.metadata import MetaInformation
|
from calibre.ebooks.metadata import MetaInformation
|
||||||
from calibre.constants import preferred_encoding, filesystem_encoding, DEBUG
|
from calibre.constants import preferred_encoding, filesystem_encoding, DEBUG
|
||||||
@ -244,6 +244,7 @@ class Adder(QObject): # {{{
|
|||||||
def __init__(self, parent, db, callback, spare_server=None):
|
def __init__(self, parent, db, callback, spare_server=None):
|
||||||
QObject.__init__(self, parent)
|
QObject.__init__(self, parent)
|
||||||
self.pd = ProgressDialog(_('Adding...'), parent=parent)
|
self.pd = ProgressDialog(_('Adding...'), parent=parent)
|
||||||
|
self.pd.setMaximumWidth(min(600, int(available_width()*0.75)))
|
||||||
self.spare_server = spare_server
|
self.spare_server = spare_server
|
||||||
self.db = db
|
self.db = db
|
||||||
self.pd.setModal(True)
|
self.pd.setModal(True)
|
||||||
|
@ -25,7 +25,8 @@ from calibre.utils.logging import Log
|
|||||||
|
|
||||||
class BulkConfig(Config):
|
class BulkConfig(Config):
|
||||||
|
|
||||||
def __init__(self, parent, db, preferred_output_format=None):
|
def __init__(self, parent, db, preferred_output_format=None,
|
||||||
|
has_saved_settings=True):
|
||||||
ResizableDialog.__init__(self, parent)
|
ResizableDialog.__init__(self, parent)
|
||||||
|
|
||||||
self.setup_output_formats(db, preferred_output_format)
|
self.setup_output_formats(db, preferred_output_format)
|
||||||
@ -54,6 +55,12 @@ class BulkConfig(Config):
|
|||||||
rb = self.buttonBox.button(self.buttonBox.RestoreDefaults)
|
rb = self.buttonBox.button(self.buttonBox.RestoreDefaults)
|
||||||
rb.setVisible(False)
|
rb.setVisible(False)
|
||||||
self.groups.setMouseTracking(True)
|
self.groups.setMouseTracking(True)
|
||||||
|
if not has_saved_settings:
|
||||||
|
o = self.opt_individual_saved_settings
|
||||||
|
o.setEnabled(False)
|
||||||
|
o.setToolTip(_('None of the selected books have saved conversion '
|
||||||
|
'settings.'))
|
||||||
|
o.setChecked(False)
|
||||||
|
|
||||||
|
|
||||||
def setup_pipeline(self, *args):
|
def setup_pipeline(self, *args):
|
||||||
|
@ -230,8 +230,6 @@ class Text(Base):
|
|||||||
|
|
||||||
def setup_ui(self, parent):
|
def setup_ui(self, parent):
|
||||||
self.sep = self.col_metadata['multiple_seps']
|
self.sep = self.col_metadata['multiple_seps']
|
||||||
values = self.all_values = list(self.db.all_custom(num=self.col_id))
|
|
||||||
values.sort(key=sort_key)
|
|
||||||
|
|
||||||
if self.col_metadata['is_multiple']:
|
if self.col_metadata['is_multiple']:
|
||||||
w = MultiCompleteLineEdit(parent)
|
w = MultiCompleteLineEdit(parent)
|
||||||
@ -239,7 +237,6 @@ class Text(Base):
|
|||||||
if self.sep['ui_to_list'] == '&':
|
if self.sep['ui_to_list'] == '&':
|
||||||
w.set_space_before_sep(True)
|
w.set_space_before_sep(True)
|
||||||
w.set_add_separator(tweaks['authors_completer_append_separator'])
|
w.set_add_separator(tweaks['authors_completer_append_separator'])
|
||||||
w.update_items_cache(values)
|
|
||||||
w.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
|
w.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
|
||||||
else:
|
else:
|
||||||
w = MultiCompleteComboBox(parent)
|
w = MultiCompleteComboBox(parent)
|
||||||
@ -249,16 +246,19 @@ class Text(Base):
|
|||||||
self.widgets = [QLabel('&'+self.col_metadata['name']+':', parent), w]
|
self.widgets = [QLabel('&'+self.col_metadata['name']+':', parent), w]
|
||||||
|
|
||||||
def initialize(self, book_id):
|
def initialize(self, book_id):
|
||||||
|
values = list(self.db.all_custom(num=self.col_id))
|
||||||
|
values.sort(key=sort_key)
|
||||||
|
self.widgets[1].clear()
|
||||||
|
self.widgets[1].update_items_cache(values)
|
||||||
val = self.db.get_custom(book_id, num=self.col_id, index_is_id=True)
|
val = self.db.get_custom(book_id, num=self.col_id, index_is_id=True)
|
||||||
self.initial_val = val
|
self.initial_val = val
|
||||||
val = self.normalize_db_val(val)
|
val = self.normalize_db_val(val)
|
||||||
self.widgets[1].update_items_cache(self.all_values)
|
|
||||||
|
|
||||||
if self.col_metadata['is_multiple']:
|
if self.col_metadata['is_multiple']:
|
||||||
self.setter(val)
|
self.setter(val)
|
||||||
else:
|
else:
|
||||||
idx = None
|
idx = None
|
||||||
for i, c in enumerate(self.all_values):
|
for i, c in enumerate(values):
|
||||||
if c == val:
|
if c == val:
|
||||||
idx = i
|
idx = i
|
||||||
self.widgets[1].addItem(c)
|
self.widgets[1].addItem(c)
|
||||||
@ -287,8 +287,6 @@ class Text(Base):
|
|||||||
class Series(Base):
|
class Series(Base):
|
||||||
|
|
||||||
def setup_ui(self, parent):
|
def setup_ui(self, parent):
|
||||||
values = self.all_values = list(self.db.all_custom(num=self.col_id))
|
|
||||||
values.sort(key=sort_key)
|
|
||||||
w = MultiCompleteComboBox(parent)
|
w = MultiCompleteComboBox(parent)
|
||||||
w.set_separator(None)
|
w.set_separator(None)
|
||||||
w.setSizeAdjustPolicy(w.AdjustToMinimumContentsLengthWithIcon)
|
w.setSizeAdjustPolicy(w.AdjustToMinimumContentsLengthWithIcon)
|
||||||
@ -305,6 +303,8 @@ class Series(Base):
|
|||||||
self.widgets.append(w)
|
self.widgets.append(w)
|
||||||
|
|
||||||
def initialize(self, book_id):
|
def initialize(self, book_id):
|
||||||
|
values = list(self.db.all_custom(num=self.col_id))
|
||||||
|
values.sort(key=sort_key)
|
||||||
val = self.db.get_custom(book_id, num=self.col_id, index_is_id=True)
|
val = self.db.get_custom(book_id, num=self.col_id, index_is_id=True)
|
||||||
s_index = self.db.get_custom_extra(book_id, num=self.col_id, index_is_id=True)
|
s_index = self.db.get_custom_extra(book_id, num=self.col_id, index_is_id=True)
|
||||||
if s_index is None:
|
if s_index is None:
|
||||||
@ -314,11 +314,12 @@ class Series(Base):
|
|||||||
self.initial_val = val
|
self.initial_val = val
|
||||||
val = self.normalize_db_val(val)
|
val = self.normalize_db_val(val)
|
||||||
idx = None
|
idx = None
|
||||||
for i, c in enumerate(self.all_values):
|
self.name_widget.clear()
|
||||||
|
for i, c in enumerate(values):
|
||||||
if c == val:
|
if c == val:
|
||||||
idx = i
|
idx = i
|
||||||
self.name_widget.addItem(c)
|
self.name_widget.addItem(c)
|
||||||
self.name_widget.update_items_cache(self.all_values)
|
self.name_widget.update_items_cache(values)
|
||||||
self.name_widget.setEditText('')
|
self.name_widget.setEditText('')
|
||||||
if idx is not None:
|
if idx is not None:
|
||||||
self.widgets[1].setCurrentIndex(idx)
|
self.widgets[1].setCurrentIndex(idx)
|
||||||
|
@ -5,8 +5,9 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import os
|
import os, itertools, operator
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
from future_builtins import map
|
||||||
|
|
||||||
from PyQt4.Qt import (QTableView, Qt, QAbstractItemView, QMenu, pyqtSignal,
|
from PyQt4.Qt import (QTableView, Qt, QAbstractItemView, QMenu, pyqtSignal,
|
||||||
QModelIndex, QIcon, QItemSelection, QMimeData, QDrag, QApplication,
|
QModelIndex, QIcon, QItemSelection, QMimeData, QDrag, QApplication,
|
||||||
@ -793,8 +794,13 @@ class BooksView(QTableView): # {{{
|
|||||||
sel = QItemSelection()
|
sel = QItemSelection()
|
||||||
m = self.model()
|
m = self.model()
|
||||||
max_col = m.columnCount(QModelIndex()) - 1
|
max_col = m.columnCount(QModelIndex()) - 1
|
||||||
for row in rows:
|
# Create a range based selector for each set of contiguous rows
|
||||||
sel.select(m.index(row, 0), m.index(row, max_col))
|
# as supplying selectors for each individual row causes very poor
|
||||||
|
# performance if a large number of rows has to be selected.
|
||||||
|
for k, g in itertools.groupby(enumerate(rows), lambda (i,x):i-x):
|
||||||
|
group = list(map(operator.itemgetter(1), g))
|
||||||
|
sel.merge(QItemSelection(m.index(min(group), 0),
|
||||||
|
m.index(max(group), max_col)), sm.Select)
|
||||||
sm.select(sel, sm.ClearAndSelect)
|
sm.select(sel, sm.ClearAndSelect)
|
||||||
|
|
||||||
def get_selected_ids(self):
|
def get_selected_ids(self):
|
||||||
|
@ -112,7 +112,10 @@ def convert_bulk_ebook(parent, queue, db, book_ids, out_format=None, args=[]):
|
|||||||
if total == 0:
|
if total == 0:
|
||||||
return None, None, None
|
return None, None, None
|
||||||
|
|
||||||
d = BulkConfig(parent, db, out_format)
|
has_saved_settings = db.has_conversion_options(book_ids)
|
||||||
|
|
||||||
|
d = BulkConfig(parent, db, out_format,
|
||||||
|
has_saved_settings=has_saved_settings)
|
||||||
if d.exec_() != QDialog.Accepted:
|
if d.exec_() != QDialog.Accepted:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -359,7 +359,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
|||||||
'log will be displayed automatically.')%self.gui_debug, show=True)
|
'log will be displayed automatically.')%self.gui_debug, show=True)
|
||||||
|
|
||||||
def esc(self, *args):
|
def esc(self, *args):
|
||||||
self.search.clear()
|
self.clear_button.click()
|
||||||
|
|
||||||
def start_content_server(self, check_started=True):
|
def start_content_server(self, check_started=True):
|
||||||
from calibre.library.server.main import start_threaded_server
|
from calibre.library.server.main import start_threaded_server
|
||||||
|
@ -172,7 +172,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
|||||||
|
|
||||||
STATE_VERSION = 1
|
STATE_VERSION = 1
|
||||||
|
|
||||||
def __init__(self, pathtoebook=None, debug_javascript=False):
|
def __init__(self, pathtoebook=None, debug_javascript=False, open_at=None):
|
||||||
MainWindow.__init__(self, None)
|
MainWindow.__init__(self, None)
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
self.view.magnification_changed.connect(self.magnification_changed)
|
self.view.magnification_changed.connect(self.magnification_changed)
|
||||||
@ -280,7 +280,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
|||||||
|
|
||||||
|
|
||||||
if pathtoebook is not None:
|
if pathtoebook is not None:
|
||||||
f = functools.partial(self.load_ebook, pathtoebook)
|
f = functools.partial(self.load_ebook, pathtoebook, open_at=open_at)
|
||||||
QTimer.singleShot(50, f)
|
QTimer.singleShot(50, f)
|
||||||
self.view.setMinimumSize(100, 100)
|
self.view.setMinimumSize(100, 100)
|
||||||
self.toc.setCursor(Qt.PointingHandCursor)
|
self.toc.setCursor(Qt.PointingHandCursor)
|
||||||
@ -457,8 +457,8 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
|||||||
def goto_end(self):
|
def goto_end(self):
|
||||||
self.goto_page(self.pos.maximum())
|
self.goto_page(self.pos.maximum())
|
||||||
|
|
||||||
def goto_page(self, new_page):
|
def goto_page(self, new_page, loaded_check=True):
|
||||||
if self.current_page is not None:
|
if self.current_page is not None or not loaded_check:
|
||||||
for page in self.iterator.spine:
|
for page in self.iterator.spine:
|
||||||
if new_page >= page.start_page and new_page <= page.max_page:
|
if new_page >= page.start_page and new_page <= page.max_page:
|
||||||
try:
|
try:
|
||||||
@ -672,7 +672,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
|||||||
except:
|
except:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|
||||||
def load_ebook(self, pathtoebook):
|
def load_ebook(self, pathtoebook, open_at=None):
|
||||||
if self.iterator is not None:
|
if self.iterator is not None:
|
||||||
self.save_current_position()
|
self.save_current_position()
|
||||||
self.iterator.__exit__()
|
self.iterator.__exit__()
|
||||||
@ -731,10 +731,17 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
|||||||
self.current_index = -1
|
self.current_index = -1
|
||||||
QApplication.instance().alert(self, 5000)
|
QApplication.instance().alert(self, 5000)
|
||||||
previous = self.set_bookmarks(self.iterator.bookmarks)
|
previous = self.set_bookmarks(self.iterator.bookmarks)
|
||||||
if previous is not None:
|
if open_at is None and previous is not None:
|
||||||
self.goto_bookmark(previous)
|
self.goto_bookmark(previous)
|
||||||
else:
|
else:
|
||||||
|
if open_at is None:
|
||||||
self.next_document()
|
self.next_document()
|
||||||
|
else:
|
||||||
|
if open_at > self.pos.maximum():
|
||||||
|
open_at = self.pos.maximum()
|
||||||
|
if open_at < self.pos.minimum():
|
||||||
|
open_at = self.pos.minimum()
|
||||||
|
self.goto_page(open_at, loaded_check=False)
|
||||||
|
|
||||||
def set_vscrollbar_value(self, pagenum):
|
def set_vscrollbar_value(self, pagenum):
|
||||||
self.vertical_scrollbar.blockSignals(True)
|
self.vertical_scrollbar.blockSignals(True)
|
||||||
@ -804,6 +811,9 @@ def config(defaults=None):
|
|||||||
help=_('Remember last used window size'))
|
help=_('Remember last used window size'))
|
||||||
c.add_opt('debug_javascript', ['--debug-javascript'], default=False,
|
c.add_opt('debug_javascript', ['--debug-javascript'], default=False,
|
||||||
help=_('Print javascript alert and console messages to the console'))
|
help=_('Print javascript alert and console messages to the console'))
|
||||||
|
c.add_opt('open_at', ['--open-at'], default=None,
|
||||||
|
help=_('The position at which to open the specified book. The position is '
|
||||||
|
'a location as displayed in the top left corner of the viewer.'))
|
||||||
|
|
||||||
return c
|
return c
|
||||||
|
|
||||||
@ -823,13 +833,17 @@ def main(args=sys.argv):
|
|||||||
parser = option_parser()
|
parser = option_parser()
|
||||||
opts, args = parser.parse_args(args)
|
opts, args = parser.parse_args(args)
|
||||||
pid = os.fork() if False and (islinux or isbsd) else -1
|
pid = os.fork() if False and (islinux or isbsd) else -1
|
||||||
|
try:
|
||||||
|
open_at = float(opts.open_at)
|
||||||
|
except:
|
||||||
|
open_at = None
|
||||||
if pid <= 0:
|
if pid <= 0:
|
||||||
app = Application(args)
|
app = Application(args)
|
||||||
app.setWindowIcon(QIcon(I('viewer.png')))
|
app.setWindowIcon(QIcon(I('viewer.png')))
|
||||||
QApplication.setOrganizationName(ORG_NAME)
|
QApplication.setOrganizationName(ORG_NAME)
|
||||||
QApplication.setApplicationName(APP_UID)
|
QApplication.setApplicationName(APP_UID)
|
||||||
main = EbookViewer(args[1] if len(args) > 1 else None,
|
main = EbookViewer(args[1] if len(args) > 1 else None,
|
||||||
debug_javascript=opts.debug_javascript)
|
debug_javascript=opts.debug_javascript, open_at=open_at)
|
||||||
sys.excepthook = main.unhandled_exception
|
sys.excepthook = main.unhandled_exception
|
||||||
main.show()
|
main.show()
|
||||||
if opts.raise_window:
|
if opts.raise_window:
|
||||||
|
@ -1085,6 +1085,14 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE;
|
|||||||
return cPickle.loads(str(data))
|
return cPickle.loads(str(data))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def has_conversion_options(self, ids, format='PIPE'):
|
||||||
|
ids = tuple(ids)
|
||||||
|
if len(ids) > 50000:
|
||||||
|
return True
|
||||||
|
return self.conn.get('''
|
||||||
|
SELECT data FROM conversion_options WHERE book IN %r AND
|
||||||
|
format=? LIMIT 1'''%(ids,), (format,), all=False) is not None
|
||||||
|
|
||||||
def delete_conversion_options(self, id, format, commit=True):
|
def delete_conversion_options(self, id, format, commit=True):
|
||||||
self.conn.execute('DELETE FROM conversion_options WHERE book=? AND format=?',
|
self.conn.execute('DELETE FROM conversion_options WHERE book=? AND format=?',
|
||||||
(id, format.upper()))
|
(id, format.upper()))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user