mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Merge from custcol trunk
This commit is contained in:
commit
41755c20ef
@ -5,7 +5,7 @@
|
|||||||
# Also, each release can have new and improved recipes.
|
# Also, each release can have new and improved recipes.
|
||||||
|
|
||||||
- version: 0.6.52
|
- version: 0.6.52
|
||||||
date: 2010-07-30
|
date: 2010-05-07
|
||||||
|
|
||||||
new features:
|
new features:
|
||||||
- title: "Support for the Kobo Reader and the HTC Desire"
|
- title: "Support for the Kobo Reader and the HTC Desire"
|
||||||
|
@ -7,13 +7,14 @@ __docformat__ = 'restructuredtext en'
|
|||||||
discovermagazine.com
|
discovermagazine.com
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
import re
|
||||||
from calibre.web.feeds.news import BasicNewsRecipe
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
class DiscoverMagazine(BasicNewsRecipe):
|
class DiscoverMagazine(BasicNewsRecipe):
|
||||||
|
|
||||||
title = u'Discover Magazine'
|
title = u'Discover Magazine'
|
||||||
description = u'Science, Technology and the Future'
|
description = u'Science, Technology and the Future'
|
||||||
__author__ = 'Mike Diaz'
|
__author__ = 'Starson17'
|
||||||
language = 'en'
|
language = 'en'
|
||||||
|
|
||||||
oldest_article = 33
|
oldest_article = 33
|
||||||
@ -24,10 +25,46 @@ class DiscoverMagazine(BasicNewsRecipe):
|
|||||||
encoding = 'utf-8'
|
encoding = 'utf-8'
|
||||||
extra_css = '.headline {font-size: x-large;} \n .fact {padding-top: 10pt}'
|
extra_css = '.headline {font-size: x-large;} \n .fact {padding-top: 10pt}'
|
||||||
|
|
||||||
remove_tags = [dict(name='div', attrs={'id':['searchModule', 'mainMenu', 'tool-box']}),
|
remove_tags = [
|
||||||
|
dict(name='div', attrs={'id':['searchModule', 'mainMenu', 'tool-box']}),
|
||||||
|
dict(name='div', attrs={'id':['footer','teaser','already-subscriber','teaser-suite','related-articles']}),
|
||||||
|
dict(name='div', attrs={'class':['column']}),
|
||||||
dict(name='img', attrs={'src':'http://discovermagazine.com/onebyone.gif'})]
|
dict(name='img', attrs={'src':'http://discovermagazine.com/onebyone.gif'})]
|
||||||
|
|
||||||
remove_tags_after = [dict(name='div', attrs={'class':'articlebody'})]
|
remove_tags_after = [dict(name='div', attrs={'class':'listingBar'})]
|
||||||
|
|
||||||
|
def append_page(self, soup, appendtag, position):
|
||||||
|
pager = soup.find('span',attrs={'class':'next'})
|
||||||
|
if pager:
|
||||||
|
nexturl = pager.a['href']
|
||||||
|
soup2 = self.index_to_soup(nexturl)
|
||||||
|
texttag = soup2.find('div', attrs={'class':'articlebody'})
|
||||||
|
newpos = len(texttag.contents)
|
||||||
|
self.append_page(soup2,texttag,newpos)
|
||||||
|
texttag.extract()
|
||||||
|
appendtag.insert(position,texttag)
|
||||||
|
|
||||||
|
def preprocess_html(self, soup):
|
||||||
|
mtag = '<meta http-equiv="Content-Language" content="en-US"/>\n<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>'
|
||||||
|
soup.head.insert(0,mtag)
|
||||||
|
self.append_page(soup, soup.body, 3)
|
||||||
|
pager = soup.find('div',attrs={'class':'listingBar'})
|
||||||
|
if pager:
|
||||||
|
pager.extract()
|
||||||
|
return soup
|
||||||
|
|
||||||
|
def postprocess_html(self, soup, first_fetch):
|
||||||
|
for tag in soup.findAll(text=re.compile('^This article is a sample')):
|
||||||
|
tag.parent.extract()
|
||||||
|
for tag in soup.findAll(['table', 'tr', 'td']):
|
||||||
|
tag.name = 'div'
|
||||||
|
for tag in soup.findAll('div', attrs={'class':'discreet advert'}):
|
||||||
|
tag.extract()
|
||||||
|
for tag in soup.findAll('hr', attrs={'size':'1'}):
|
||||||
|
tag.extract()
|
||||||
|
for tag in soup.findAll('br'):
|
||||||
|
tag.extract()
|
||||||
|
return soup
|
||||||
|
|
||||||
feeds = [
|
feeds = [
|
||||||
(u'Technology', u'http://discovermagazine.com/topics/technology/rss.xml'),
|
(u'Technology', u'http://discovermagazine.com/topics/technology/rss.xml'),
|
||||||
@ -38,10 +75,12 @@ class DiscoverMagazine(BasicNewsRecipe):
|
|||||||
(u'Living World', u'http://discovermagazine.com/topics/living-world/rss.xml'),
|
(u'Living World', u'http://discovermagazine.com/topics/living-world/rss.xml'),
|
||||||
(u'Environment', u'http://discovermagazine.com/topics/environment/rss.xml'),
|
(u'Environment', u'http://discovermagazine.com/topics/environment/rss.xml'),
|
||||||
(u'Physics & Math', u'http://discovermagazine.com/topics/physics-math/rss.xml'),
|
(u'Physics & Math', u'http://discovermagazine.com/topics/physics-math/rss.xml'),
|
||||||
(u'Vital Signs', u'http://discovermagazine.com/columns/vital-signs/rss.xml'),
|
|
||||||
(u"20 Things you didn't know about...", u'http://discovermagazine.com/columns/20-things-you-didnt-know/rss.xml'),
|
(u"20 Things you didn't know about...", u'http://discovermagazine.com/columns/20-things-you-didnt-know/rss.xml'),
|
||||||
(u'Fuzzy Math', u'http://discovermagazine.com/columns/fuzzy-math/rss.xml'),
|
(u'Fuzzy Math', u'http://discovermagazine.com/columns/fuzzy-math/rss.xml'),
|
||||||
(u'The Brain', u'http://discovermagazine.com/columns/the-brain/rss.xml'),
|
(u'The Brain', u'http://discovermagazine.com/columns/the-brain/rss.xml'),
|
||||||
(u'Stupid Science Word of the Month', u'http://discovermagazine.com/columns/stupid-science-word-of-the-month/rss.xml'),
|
(u'What is This', u'http://discovermagazine.com/columns/what-is-this/rss.xml'),
|
||||||
(u'Science Not Fiction', u'http://blogs.discovermagazine.com/sciencenotfiction/wp-rss.php')
|
(u'Vital Signs', u'http://discovermagazine.com/columns/vital-signs/rss.xml'),
|
||||||
|
(u'Think Tech', u'http://discovermagazine.com/columns/think-tech/rss.xml'),
|
||||||
|
(u'Future Tech', u'http://discovermagazine.com/columns/future-tech/rss.xml'),
|
||||||
|
(u'Discover Interview', u'http://discovermagazine.com/columns/discover-interview/rss.xml'),
|
||||||
]
|
]
|
@ -4,6 +4,7 @@ __copyright__ = '2008, Kovid Goyal <kovid@kovidgoyal.net>'
|
|||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
import sys, os, re, logging, time, mimetypes, \
|
import sys, os, re, logging, time, mimetypes, \
|
||||||
__builtin__, warnings, multiprocessing
|
__builtin__, warnings, multiprocessing
|
||||||
|
from urllib import getproxies
|
||||||
__builtin__.__dict__['dynamic_property'] = lambda(func): func(None)
|
__builtin__.__dict__['dynamic_property'] = lambda(func): func(None)
|
||||||
from htmlentitydefs import name2codepoint
|
from htmlentitydefs import name2codepoint
|
||||||
from math import floor
|
from math import floor
|
||||||
@ -199,46 +200,29 @@ def extract(path, dir):
|
|||||||
extractor(path, dir)
|
extractor(path, dir)
|
||||||
|
|
||||||
def get_proxies(debug=True):
|
def get_proxies(debug=True):
|
||||||
proxies = {}
|
proxies = getproxies()
|
||||||
|
for key, proxy in list(proxies.items()):
|
||||||
for q in ('http', 'ftp'):
|
if not proxy or '..' in proxy:
|
||||||
proxy = os.environ.get(q+'_proxy', None)
|
del proxies[key]
|
||||||
if not proxy: continue
|
continue
|
||||||
if proxy.startswith(q+'://'):
|
if proxy.startswith(key+'://'):
|
||||||
proxy = proxy[7:]
|
proxy = proxy[len(key)+3:]
|
||||||
proxies[q] = proxy
|
if proxy.endswith('/'):
|
||||||
|
proxy = proxy[:-1]
|
||||||
if iswindows:
|
if len(proxy) > 4:
|
||||||
try:
|
proxies[key] = proxy
|
||||||
winreg = __import__('_winreg')
|
|
||||||
settings = winreg.OpenKey(winreg.HKEY_CURRENT_USER,
|
|
||||||
'Software\\Microsoft\\Windows'
|
|
||||||
'\\CurrentVersion\\Internet Settings')
|
|
||||||
proxy = winreg.QueryValueEx(settings, "ProxyEnable")[0]
|
|
||||||
if proxy:
|
|
||||||
server = str(winreg.QueryValueEx(settings, 'ProxyServer')[0])
|
|
||||||
if ';' in server:
|
|
||||||
for p in server.split(';'):
|
|
||||||
protocol, address = p.split('=')
|
|
||||||
proxies[protocol] = address
|
|
||||||
else:
|
else:
|
||||||
proxies['http'] = server
|
prints('Removing invalid', key, 'proxy:', proxy)
|
||||||
proxies['ftp'] = server
|
del proxies[key]
|
||||||
settings.Close()
|
|
||||||
except Exception, e:
|
|
||||||
prints('Unable to detect proxy settings: %s' % str(e))
|
|
||||||
for x in list(proxies):
|
|
||||||
if len(proxies[x]) < 5:
|
|
||||||
prints('Removing invalid', x, 'proxy:', proxies[x])
|
|
||||||
del proxies[x]
|
|
||||||
if proxies and debug:
|
if proxies and debug:
|
||||||
prints('Using proxies:', proxies)
|
prints('Using proxies:', proxies)
|
||||||
return proxies
|
return proxies
|
||||||
|
|
||||||
def get_parsed_proxy(typ='http', debug=True):
|
def get_parsed_proxy(typ='http', debug=True):
|
||||||
proxies = get_proxies(debug)
|
proxies = get_proxies(debug)
|
||||||
if typ not in proxies:
|
proxy = proxies.get(typ, None)
|
||||||
return
|
if proxy:
|
||||||
pattern = re.compile((
|
pattern = re.compile((
|
||||||
'(?:ptype://)?' \
|
'(?:ptype://)?' \
|
||||||
'(?:(?P<user>\w+):(?P<pass>.*)@)?' \
|
'(?:(?P<user>\w+):(?P<pass>.*)@)?' \
|
||||||
@ -246,7 +230,7 @@ def get_parsed_proxy(typ='http', debug=True):
|
|||||||
'(?::(?P<port>\d+))?').replace('ptype', typ)
|
'(?::(?P<port>\d+))?').replace('ptype', typ)
|
||||||
)
|
)
|
||||||
|
|
||||||
match = pattern.match(proxies['typ'])
|
match = pattern.match(proxies[typ])
|
||||||
if match:
|
if match:
|
||||||
try:
|
try:
|
||||||
ans = {
|
ans = {
|
||||||
@ -260,9 +244,9 @@ def get_parsed_proxy(typ='http', debug=True):
|
|||||||
except:
|
except:
|
||||||
if debug:
|
if debug:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return
|
else:
|
||||||
if debug:
|
if debug:
|
||||||
prints('Using http proxy', ans)
|
prints('Using http proxy', str(ans))
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
|
||||||
|
@ -34,9 +34,51 @@ Run an embedded python interpreter.
|
|||||||
help='Add a simple plugin (i.e. a plugin that consists of only a '
|
help='Add a simple plugin (i.e. a plugin that consists of only a '
|
||||||
'.py file), by specifying the path to the py file containing the '
|
'.py file), by specifying the path to the py file containing the '
|
||||||
'plugin code.')
|
'plugin code.')
|
||||||
|
parser.add_option('--reinitialize-db', default=None,
|
||||||
|
help='Re-initialize the sqlite calibre database at the '
|
||||||
|
'specified path. Useful to recover from db corruption.')
|
||||||
|
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
def reinit_db(dbpath, callback=None):
|
||||||
|
if not os.path.exists(dbpath):
|
||||||
|
raise ValueError(dbpath + ' does not exist')
|
||||||
|
from calibre.library.sqlite import connect
|
||||||
|
from contextlib import closing
|
||||||
|
import shutil
|
||||||
|
conn = connect(dbpath, False)
|
||||||
|
uv = conn.get('PRAGMA user_version;', all=False)
|
||||||
|
conn.execute('PRAGMA writable_schema=ON')
|
||||||
|
conn.commit()
|
||||||
|
sql_lines = conn.dump()
|
||||||
|
conn.close()
|
||||||
|
dest = dbpath + '.tmp'
|
||||||
|
try:
|
||||||
|
with closing(connect(dest, False)) as nconn:
|
||||||
|
nconn.execute('create temporary table temp_sequence(id INTEGER PRIMARY KEY AUTOINCREMENT)')
|
||||||
|
nconn.commit()
|
||||||
|
if callable(callback):
|
||||||
|
callback(len(sql_lines), True)
|
||||||
|
for i, line in enumerate(sql_lines):
|
||||||
|
try:
|
||||||
|
nconn.execute(line)
|
||||||
|
except:
|
||||||
|
import traceback
|
||||||
|
prints('SQL line %r failed with error:'%line)
|
||||||
|
prints(traceback.format_exc())
|
||||||
|
continue
|
||||||
|
finally:
|
||||||
|
if callable(callback):
|
||||||
|
callback(i, False)
|
||||||
|
nconn.execute('pragma user_version=%d'%int(uv))
|
||||||
|
nconn.commit()
|
||||||
|
os.remove(dbpath)
|
||||||
|
shutil.copyfile(dest, dbpath)
|
||||||
|
finally:
|
||||||
|
if os.path.exists(dest):
|
||||||
|
os.remove(dest)
|
||||||
|
prints('Database successfully re-initialized')
|
||||||
|
|
||||||
def migrate(old, new):
|
def migrate(old, new):
|
||||||
from calibre.utils.config import prefs
|
from calibre.utils.config import prefs
|
||||||
from calibre.library.database import LibraryDatabase
|
from calibre.library.database import LibraryDatabase
|
||||||
@ -122,6 +164,8 @@ def main(args=sys.argv):
|
|||||||
prints('CALIBRE_RESOURCES_PATH='+sys.resources_location)
|
prints('CALIBRE_RESOURCES_PATH='+sys.resources_location)
|
||||||
prints('CALIBRE_EXTENSIONS_PATH='+sys.extensions_location)
|
prints('CALIBRE_EXTENSIONS_PATH='+sys.extensions_location)
|
||||||
prints('CALIBRE_PYTHON_PATH='+os.pathsep.join(sys.path))
|
prints('CALIBRE_PYTHON_PATH='+os.pathsep.join(sys.path))
|
||||||
|
elif opts.reinitialize_db is not None:
|
||||||
|
reinit_db(opts.reinitialize_db)
|
||||||
else:
|
else:
|
||||||
from calibre import ipython
|
from calibre import ipython
|
||||||
ipython()
|
ipython()
|
||||||
|
@ -47,5 +47,5 @@ class KOBO(USBMS):
|
|||||||
VENDOR_NAME = 'KOBO_INC'
|
VENDOR_NAME = 'KOBO_INC'
|
||||||
WINDOWS_MAIN_MEM = '.KOBOEREADER'
|
WINDOWS_MAIN_MEM = '.KOBOEREADER'
|
||||||
|
|
||||||
EBOOK_DIR_MAIN = 'e-books'
|
EBOOK_DIR_MAIN = ''
|
||||||
|
|
||||||
|
@ -284,7 +284,12 @@ class BookList(_BookList):
|
|||||||
plitems = []
|
plitems = []
|
||||||
for pl in self.playlists():
|
for pl in self.playlists():
|
||||||
for c in pl.childNodes:
|
for c in pl.childNodes:
|
||||||
if hasattr(c, 'tagName') and c.tagName.endswith('item'):
|
if hasattr(c, 'tagName') and c.tagName.endswith('item') and \
|
||||||
|
hasattr(c, 'getAttribute'):
|
||||||
|
try:
|
||||||
|
c.getAttribute('id')
|
||||||
|
except: # Unlinked node
|
||||||
|
continue
|
||||||
plitems.append(c)
|
plitems.append(c)
|
||||||
return plitems
|
return plitems
|
||||||
|
|
||||||
@ -385,9 +390,9 @@ class BookList(_BookList):
|
|||||||
continue
|
continue
|
||||||
db_ids = [i.getAttribute('id') for i in pl.childNodes if hasattr(i, 'getAttribute')]
|
db_ids = [i.getAttribute('id') for i in pl.childNodes if hasattr(i, 'getAttribute')]
|
||||||
pl_book_ids = [getattr(self.book_by_id(i), 'db_id', None) for i in db_ids]
|
pl_book_ids = [getattr(self.book_by_id(i), 'db_id', None) for i in db_ids]
|
||||||
map = {}
|
imap = {}
|
||||||
for i, j in zip(pl_book_ids, db_ids):
|
for i, j in zip(pl_book_ids, db_ids):
|
||||||
map[i] = j
|
imap[i] = j
|
||||||
pl_book_ids = [i for i in pl_book_ids if i is not None]
|
pl_book_ids = [i for i in pl_book_ids if i is not None]
|
||||||
ordered_ids = [i for i in self.tag_order[title] if i in pl_book_ids]
|
ordered_ids = [i for i in self.tag_order[title] if i in pl_book_ids]
|
||||||
|
|
||||||
@ -399,7 +404,7 @@ class BookList(_BookList):
|
|||||||
child.unlink()
|
child.unlink()
|
||||||
for id in ordered_ids:
|
for id in ordered_ids:
|
||||||
item = self.document.createElement(self.prefix+'item')
|
item = self.document.createElement(self.prefix+'item')
|
||||||
item.setAttribute('id', str(map[id]))
|
item.setAttribute('id', str(imap[id]))
|
||||||
pl.appendChild(item)
|
pl.appendChild(item)
|
||||||
|
|
||||||
def fix_ids(main, carda, cardb):
|
def fix_ids(main, carda, cardb):
|
||||||
|
@ -121,6 +121,14 @@ class PRS505(CLI, Device):
|
|||||||
self.report_progress(1.0, _('Getting list of books on device...'))
|
self.report_progress(1.0, _('Getting list of books on device...'))
|
||||||
return bl
|
return bl
|
||||||
|
|
||||||
|
def filename_callback(self, fname, mi):
|
||||||
|
if getattr(mi, 'application_id', None) is not None:
|
||||||
|
base = fname.rpartition('.')[0]
|
||||||
|
suffix = '_%s'%mi.application_id
|
||||||
|
if not base.endswith(suffix):
|
||||||
|
fname = base + suffix + '.' + fname.rpartition('.')[-1]
|
||||||
|
return fname
|
||||||
|
|
||||||
def upload_books(self, files, names, on_card=None, end_session=True,
|
def upload_books(self, files, names, on_card=None, end_session=True,
|
||||||
metadata=None):
|
metadata=None):
|
||||||
|
|
||||||
|
@ -825,7 +825,10 @@ class Device(DeviceConfig, DevicePlugin):
|
|||||||
from calibre.library.save_to_disk import get_components
|
from calibre.library.save_to_disk import get_components
|
||||||
if not isinstance(template, unicode):
|
if not isinstance(template, unicode):
|
||||||
template = template.decode('utf-8')
|
template = template.decode('utf-8')
|
||||||
extra_components = get_components(template, mdata, fname)
|
app_id = str(getattr(mdata, 'application_id', ''))
|
||||||
|
# The SONY readers need to have the db id in the created filename
|
||||||
|
extra_components = get_components(template, mdata, fname,
|
||||||
|
length=250-len(app_id)-1)
|
||||||
if not extra_components:
|
if not extra_components:
|
||||||
extra_components.append(sanitize(self.filename_callback(fname,
|
extra_components.append(sanitize(self.filename_callback(fname,
|
||||||
mdata)))
|
mdata)))
|
||||||
|
@ -341,8 +341,15 @@ class ComicInput(InputFormatPlugin):
|
|||||||
if not os.path.exists('comics.txt'):
|
if not os.path.exists('comics.txt'):
|
||||||
raise ValueError('%s is not a valid comic collection'
|
raise ValueError('%s is not a valid comic collection'
|
||||||
%stream.name)
|
%stream.name)
|
||||||
raw = open('comics.txt', 'rb').read().decode('utf-8')
|
raw = open('comics.txt', 'rb').read()
|
||||||
raw.lstrip(unicode(codecs.BOM_UTF8, "utf8" ))
|
if raw.startswith(codecs.BOM_UTF16_BE):
|
||||||
|
raw = raw.decode('utf-16-be')[1:]
|
||||||
|
elif raw.startswith(codecs.BOM_UTF16_LE):
|
||||||
|
raw = raw.decode('utf-16-le')[1:]
|
||||||
|
elif raw.startswith(codecs.BOM_UTF8):
|
||||||
|
raw = raw.decode('utf-8')[1:]
|
||||||
|
else:
|
||||||
|
raw = raw.decode('utf-8')
|
||||||
for line in raw.splitlines():
|
for line in raw.splitlines():
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
if not line:
|
if not line:
|
||||||
|
@ -292,7 +292,7 @@ class HTMLInput(InputFormatPlugin):
|
|||||||
encoding=opts.input_encoding)
|
encoding=opts.input_encoding)
|
||||||
|
|
||||||
def is_case_sensitive(self, path):
|
def is_case_sensitive(self, path):
|
||||||
if self._is_case_sensitive is not None:
|
if getattr(self, '_is_case_sensitive', None) is not None:
|
||||||
return self._is_case_sensitive
|
return self._is_case_sensitive
|
||||||
if not path or not os.path.exists(path):
|
if not path or not os.path.exists(path):
|
||||||
return islinux or isfreebsd
|
return islinux or isfreebsd
|
||||||
|
@ -8,6 +8,7 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2009, John Schember <john@nachtimwald.com>'
|
__copyright__ = '2009, John Schember <john@nachtimwald.com>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
import re
|
||||||
import struct
|
import struct
|
||||||
|
|
||||||
from calibre.ebooks.metadata import MetaInformation
|
from calibre.ebooks.metadata import MetaInformation
|
||||||
@ -46,10 +47,10 @@ def get_metadata(stream, extract_cover=True):
|
|||||||
mdata = pheader.section_data(hr.metadata_offset)
|
mdata = pheader.section_data(hr.metadata_offset)
|
||||||
|
|
||||||
mdata = mdata.split('\x00')
|
mdata = mdata.split('\x00')
|
||||||
mi.title = mdata[0]
|
mi.title = re.sub(r'[^a-zA-Z0-9 \._=\+\-!\?,\'\"]', '', mdata[0])
|
||||||
mi.authors = [mdata[1]]
|
mi.authors = [re.sub(r'[^a-zA-Z0-9 \._=\+\-!\?,\'\"]', '', mdata[1])]
|
||||||
mi.publisher = mdata[3]
|
mi.publisher = re.sub(r'[^a-zA-Z0-9 \._=\+\-!\?,\'\"]', '', mdata[3])
|
||||||
mi.isbn = mdata[4]
|
mi.isbn = re.sub(r'[^a-zA-Z0-9 \._=\+\-!\?,\'\"]', '', mdata[4])
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -420,6 +420,7 @@ class FileDialog(QObject):
|
|||||||
modal = True,
|
modal = True,
|
||||||
name = '',
|
name = '',
|
||||||
mode = QFileDialog.ExistingFiles,
|
mode = QFileDialog.ExistingFiles,
|
||||||
|
default_dir='~'
|
||||||
):
|
):
|
||||||
QObject.__init__(self)
|
QObject.__init__(self)
|
||||||
ftext = ''
|
ftext = ''
|
||||||
@ -438,9 +439,10 @@ class FileDialog(QObject):
|
|||||||
self.selected_files = None
|
self.selected_files = None
|
||||||
self.fd = None
|
self.fd = None
|
||||||
|
|
||||||
initial_dir = dynamic.get(self.dialog_name, os.path.expanduser('~'))
|
initial_dir = dynamic.get(self.dialog_name,
|
||||||
|
os.path.expanduser(default_dir))
|
||||||
if not isinstance(initial_dir, basestring):
|
if not isinstance(initial_dir, basestring):
|
||||||
initial_dir = os.path.expanduser('~')
|
initial_dir = os.path.expanduser(default_dir)
|
||||||
self.selected_files = []
|
self.selected_files = []
|
||||||
if mode == QFileDialog.AnyFile:
|
if mode == QFileDialog.AnyFile:
|
||||||
f = unicode(QFileDialog.getSaveFileName(parent, title, initial_dir, ftext, ""))
|
f = unicode(QFileDialog.getSaveFileName(parent, title, initial_dir, ftext, ""))
|
||||||
@ -475,9 +477,10 @@ class FileDialog(QObject):
|
|||||||
return tuple(self.selected_files)
|
return tuple(self.selected_files)
|
||||||
|
|
||||||
|
|
||||||
def choose_dir(window, name, title):
|
def choose_dir(window, name, title, default_dir='~'):
|
||||||
fd = FileDialog(title, [], False, window, name=name,
|
fd = FileDialog(title=title, filters=[], add_all_files_filter=False,
|
||||||
mode=QFileDialog.DirectoryOnly)
|
parent=window, name=name, mode=QFileDialog.DirectoryOnly,
|
||||||
|
default_dir=default_dir)
|
||||||
dir = fd.get_files()
|
dir = fd.get_files()
|
||||||
if dir:
|
if dir:
|
||||||
return dir[0]
|
return dir[0]
|
||||||
|
@ -4,15 +4,18 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
|||||||
import sys, os, time, socket, traceback
|
import sys, os, time, socket, traceback
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
from PyQt4.Qt import QCoreApplication, QIcon, QMessageBox
|
from PyQt4.Qt import QCoreApplication, QIcon, QMessageBox, QObject, QTimer, \
|
||||||
|
QThread, pyqtSignal, Qt, QProgressDialog, QString
|
||||||
|
|
||||||
from calibre import prints
|
from calibre import prints, plugins
|
||||||
from calibre.constants import iswindows, __appname__, isosx
|
from calibre.constants import iswindows, __appname__, isosx, filesystem_encoding
|
||||||
from calibre.utils.ipc import ADDRESS, RC
|
from calibre.utils.ipc import ADDRESS, RC
|
||||||
from calibre.gui2 import ORG_NAME, APP_UID, initialize_file_icon_provider, \
|
from calibre.gui2 import ORG_NAME, APP_UID, initialize_file_icon_provider, \
|
||||||
Application
|
Application, choose_dir, error_dialog, question_dialog
|
||||||
from calibre.gui2.main_window import option_parser as _option_parser
|
from calibre.gui2.main_window import option_parser as _option_parser
|
||||||
from calibre.utils.config import prefs, dynamic
|
from calibre.utils.config import prefs, dynamic
|
||||||
|
from calibre.library.database2 import LibraryDatabase2
|
||||||
|
from calibre.library.sqlite import sqlite, DatabaseException
|
||||||
|
|
||||||
def option_parser():
|
def option_parser():
|
||||||
parser = _option_parser('''\
|
parser = _option_parser('''\
|
||||||
@ -48,25 +51,186 @@ def init_qt(args):
|
|||||||
app.setWindowIcon(QIcon(I('library.png')))
|
app.setWindowIcon(QIcon(I('library.png')))
|
||||||
return app, opts, args, actions
|
return app, opts, args, actions
|
||||||
|
|
||||||
def run_gui(opts, args, actions, listener, app):
|
def get_library_path():
|
||||||
|
library_path = prefs['library_path']
|
||||||
|
if library_path is None: # Need to migrate to new database layout
|
||||||
|
base = os.path.expanduser('~')
|
||||||
|
if iswindows:
|
||||||
|
base = plugins['winutil'][0].special_folder_path(
|
||||||
|
plugins['winutil'][0].CSIDL_PERSONAL)
|
||||||
|
if not base or not os.path.exists(base):
|
||||||
|
from PyQt4.Qt import QDir
|
||||||
|
base = unicode(QDir.homePath()).replace('/', os.sep)
|
||||||
|
candidate = choose_dir(None, 'choose calibre library',
|
||||||
|
_('Choose a location for your calibre e-book library'),
|
||||||
|
default_dir=base)
|
||||||
|
if not candidate:
|
||||||
|
candidate = os.path.join(base, 'Calibre Library')
|
||||||
|
library_path = os.path.abspath(candidate)
|
||||||
|
if not os.path.exists(library_path):
|
||||||
|
try:
|
||||||
|
os.makedirs(library_path)
|
||||||
|
except:
|
||||||
|
error_dialog(None, _('Failed to create library'),
|
||||||
|
_('Failed to create calibre library at: %r. Aborting.')%library_path,
|
||||||
|
det_msg = traceback.print_exc(), show=True)
|
||||||
|
library_path = None
|
||||||
|
return library_path
|
||||||
|
|
||||||
|
class DBRepair(QThread):
|
||||||
|
|
||||||
|
repair_done = pyqtSignal(object, object)
|
||||||
|
progress = pyqtSignal(object, object)
|
||||||
|
|
||||||
|
def __init__(self, library_path, parent, pd):
|
||||||
|
QThread.__init__(self, parent)
|
||||||
|
self.library_path = library_path
|
||||||
|
self.pd = pd
|
||||||
|
self.progress.connect(self._callback, type=Qt.QueuedConnection)
|
||||||
|
|
||||||
|
def _callback(self, num, is_length):
|
||||||
|
if is_length:
|
||||||
|
self.pd.setRange(0, num-1)
|
||||||
|
num = 0
|
||||||
|
self.pd.setValue(num)
|
||||||
|
|
||||||
|
def callback(self, num, is_length):
|
||||||
|
self.progress.emit(num, is_length)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
from calibre.debug import reinit_db
|
||||||
|
try:
|
||||||
|
reinit_db(os.path.join(self.library_path, 'metadata.db'),
|
||||||
|
self.callback)
|
||||||
|
db = LibraryDatabase2(self.library_path)
|
||||||
|
tb = None
|
||||||
|
except:
|
||||||
|
db, tb = None, traceback.format_exc()
|
||||||
|
self.repair_done.emit(db, tb)
|
||||||
|
|
||||||
|
class GuiRunner(QObject):
|
||||||
|
'''Make sure an event loop is running before starting the main work of
|
||||||
|
initialization'''
|
||||||
|
|
||||||
|
def __init__(self, opts, args, actions, listener, app):
|
||||||
|
self.opts, self.args, self.listener, self.app = opts, args, listener, app
|
||||||
|
self.actions = actions
|
||||||
|
self.main = None
|
||||||
|
QObject.__init__(self)
|
||||||
|
self.timer = QTimer.singleShot(1, self.initialize)
|
||||||
|
|
||||||
|
def start_gui(self):
|
||||||
from calibre.gui2.ui import Main
|
from calibre.gui2.ui import Main
|
||||||
|
main = Main(self.library_path, self.db, self.listener, self.opts, self.actions)
|
||||||
|
add_filesystem_book = partial(main.add_filesystem_book, allow_device=False)
|
||||||
|
sys.excepthook = main.unhandled_exception
|
||||||
|
if len(self.args) > 1:
|
||||||
|
p = os.path.abspath(self.args[1])
|
||||||
|
add_filesystem_book(p)
|
||||||
|
self.app.file_event_hook = add_filesystem_book
|
||||||
|
self.main = main
|
||||||
|
|
||||||
|
def initialization_failed(self):
|
||||||
|
print 'Catastrophic failure initializing GUI, bailing out...'
|
||||||
|
QCoreApplication.exit(1)
|
||||||
|
raise SystemExit(1)
|
||||||
|
|
||||||
|
def initialize_db_stage2(self, db, tb):
|
||||||
|
repair_pd = getattr(self, 'repair_pd', None)
|
||||||
|
if repair_pd is not None:
|
||||||
|
repair_pd.cancel()
|
||||||
|
|
||||||
|
if db is None and tb is not None:
|
||||||
|
# DB Repair failed
|
||||||
|
error_dialog(None, _('Repairing failed'),
|
||||||
|
_('The database repair failed. Starting with '
|
||||||
|
'a new empty library.'),
|
||||||
|
det_msg=tb, show=True)
|
||||||
|
if db is None:
|
||||||
|
fname = _('Calibre Library')
|
||||||
|
if isinstance(fname, unicode):
|
||||||
|
try:
|
||||||
|
fname = fname.encode(filesystem_encoding)
|
||||||
|
except:
|
||||||
|
fname = 'Calibre Library'
|
||||||
|
x = os.path.expanduser('~'+os.sep+fname)
|
||||||
|
if not os.path.exists(x):
|
||||||
|
try:
|
||||||
|
os.makedirs(x)
|
||||||
|
except:
|
||||||
|
x = os.path.expanduser('~')
|
||||||
|
candidate = choose_dir(None, 'choose calibre library',
|
||||||
|
_('Choose a location for your new calibre e-book library'),
|
||||||
|
default_dir=x)
|
||||||
|
|
||||||
|
if not candidate:
|
||||||
|
self.initialization_failed()
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.library_path = candidate
|
||||||
|
db = LibraryDatabase2(candidate)
|
||||||
|
except:
|
||||||
|
error_dialog(None, _('Bad database location'),
|
||||||
|
_('Bad database location %r. calibre will now quit.'
|
||||||
|
)%self.library_path,
|
||||||
|
det_msg=traceback.format_exc(), show=True)
|
||||||
|
self.initialization_failed()
|
||||||
|
|
||||||
|
self.db = db
|
||||||
|
self.start_gui()
|
||||||
|
|
||||||
|
def initialize_db(self):
|
||||||
|
db = None
|
||||||
|
try:
|
||||||
|
db = LibraryDatabase2(self.library_path)
|
||||||
|
except (sqlite.Error, DatabaseException):
|
||||||
|
repair = question_dialog(None, _('Corrupted database'),
|
||||||
|
_('Your calibre database appears to be corrupted. Do '
|
||||||
|
'you want calibre to try and repair it automatically? '
|
||||||
|
'If you say No, a new empty calibre library will be created.'),
|
||||||
|
det_msg=traceback.format_exc()
|
||||||
|
)
|
||||||
|
if repair:
|
||||||
|
self.repair_pd = QProgressDialog(_('Repairing database. This '
|
||||||
|
'can take a very long time for a large collection'), QString(),
|
||||||
|
0, 0)
|
||||||
|
self.repair_pd.setWindowModality(Qt.WindowModal)
|
||||||
|
self.repair_pd.show()
|
||||||
|
|
||||||
|
self.repair = DBRepair(self.library_path, self, self.repair_pd)
|
||||||
|
self.repair.repair_done.connect(self.initialize_db_stage2,
|
||||||
|
type=Qt.QueuedConnection)
|
||||||
|
self.repair.start()
|
||||||
|
return
|
||||||
|
except:
|
||||||
|
error_dialog(None, _('Bad database location'),
|
||||||
|
_('Bad database location %r. Will start with '
|
||||||
|
' a new, empty calibre library')%self.library_path,
|
||||||
|
det_msg=traceback.format_exc(), show=True)
|
||||||
|
|
||||||
|
self.initialize_db_stage2(db, None)
|
||||||
|
|
||||||
|
def initialize(self, *args):
|
||||||
|
self.library_path = get_library_path()
|
||||||
|
if self.library_path is None:
|
||||||
|
self.initialization_failed()
|
||||||
|
|
||||||
|
self.initialize_db()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def run_gui(opts, args, actions, listener, app):
|
||||||
initialize_file_icon_provider()
|
initialize_file_icon_provider()
|
||||||
if not dynamic.get('welcome_wizard_was_run', False):
|
if not dynamic.get('welcome_wizard_was_run', False):
|
||||||
from calibre.gui2.wizard import wizard
|
from calibre.gui2.wizard import wizard
|
||||||
wizard().exec_()
|
wizard().exec_()
|
||||||
dynamic.set('welcome_wizard_was_run', True)
|
dynamic.set('welcome_wizard_was_run', True)
|
||||||
main = Main(listener, opts, actions)
|
runner = GuiRunner(opts, args, actions, listener, app)
|
||||||
add_filesystem_book = partial(main.add_filesystem_book, allow_device=False)
|
|
||||||
sys.excepthook = main.unhandled_exception
|
|
||||||
if len(args) > 1:
|
|
||||||
args[1] = os.path.abspath(args[1])
|
|
||||||
add_filesystem_book(args[1])
|
|
||||||
app.file_event_hook = add_filesystem_book
|
|
||||||
ret = app.exec_()
|
ret = app.exec_()
|
||||||
if getattr(main, 'run_wizard_b4_shutdown', False):
|
if getattr(runner.main, 'run_wizard_b4_shutdown', False):
|
||||||
from calibre.gui2.wizard import wizard
|
from calibre.gui2.wizard import wizard
|
||||||
wizard().exec_()
|
wizard().exec_()
|
||||||
if getattr(main, 'restart_after_quit', False):
|
if getattr(runner.main, 'restart_after_quit', False):
|
||||||
e = sys.executable if getattr(sys, 'frozen', False) else sys.argv[0]
|
e = sys.executable if getattr(sys, 'frozen', False) else sys.argv[0]
|
||||||
print 'Restarting with:', e, sys.argv
|
print 'Restarting with:', e, sys.argv
|
||||||
if hasattr(sys, 'frameworks_dir'):
|
if hasattr(sys, 'frameworks_dir'):
|
||||||
@ -78,7 +242,7 @@ def run_gui(opts, args, actions, listener, app):
|
|||||||
else:
|
else:
|
||||||
if iswindows:
|
if iswindows:
|
||||||
try:
|
try:
|
||||||
main.system_tray_icon.hide()
|
runner.main.system_tray_icon.hide()
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
return ret
|
return ret
|
||||||
|
@ -4,7 +4,7 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
|||||||
import StringIO, traceback, sys
|
import StringIO, traceback, sys
|
||||||
|
|
||||||
from PyQt4.Qt import QMainWindow, QString, Qt, QFont, QCoreApplication, SIGNAL,\
|
from PyQt4.Qt import QMainWindow, QString, Qt, QFont, QCoreApplication, SIGNAL,\
|
||||||
QAction, QMenu, QMenuBar, QIcon
|
QAction, QMenu, QMenuBar, QIcon, pyqtSignal
|
||||||
from calibre.gui2.dialogs.conversion_error import ConversionErrorDialog
|
from calibre.gui2.dialogs.conversion_error import ConversionErrorDialog
|
||||||
from calibre.utils.config import OptionParser
|
from calibre.utils.config import OptionParser
|
||||||
from calibre.gui2 import error_dialog
|
from calibre.gui2 import error_dialog
|
||||||
@ -41,6 +41,8 @@ class MainWindow(QMainWindow):
|
|||||||
___menu = None
|
___menu = None
|
||||||
__actions = []
|
__actions = []
|
||||||
|
|
||||||
|
keyboard_interrupt = pyqtSignal()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_application_menubar(cls):
|
def create_application_menubar(cls):
|
||||||
mb = QMenuBar(None)
|
mb = QMenuBar(None)
|
||||||
@ -76,6 +78,9 @@ class MainWindow(QMainWindow):
|
|||||||
print 'Received signal:', repr(signal)
|
print 'Received signal:', repr(signal)
|
||||||
|
|
||||||
def unhandled_exception(self, type, value, tb):
|
def unhandled_exception(self, type, value, tb):
|
||||||
|
if type == KeyboardInterrupt:
|
||||||
|
self.keyboard_interrupt.emit()
|
||||||
|
return
|
||||||
try:
|
try:
|
||||||
sio = StringIO.StringIO()
|
sio = StringIO.StringIO()
|
||||||
traceback.print_exception(type, value, tb, file=sio)
|
traceback.print_exception(type, value, tb, file=sio)
|
||||||
|
@ -14,9 +14,9 @@ from xml.parsers.expat import ExpatError
|
|||||||
from Queue import Queue, Empty
|
from Queue import Queue, Empty
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from PyQt4.Qt import Qt, SIGNAL, QObject, QCoreApplication, QUrl, QTimer, \
|
from PyQt4.Qt import Qt, SIGNAL, QObject, QUrl, QTimer, \
|
||||||
QModelIndex, QPixmap, QColor, QPainter, QMenu, QIcon, \
|
QModelIndex, QPixmap, QColor, QPainter, QMenu, QIcon, \
|
||||||
QToolButton, QDialog, QDesktopServices, QFileDialog, \
|
QToolButton, QDialog, QDesktopServices, \
|
||||||
QSystemTrayIcon, QApplication, QKeySequence, QAction, \
|
QSystemTrayIcon, QApplication, QKeySequence, QAction, \
|
||||||
QMessageBox, QStackedLayout, QHelpEvent, QInputDialog,\
|
QMessageBox, QStackedLayout, QHelpEvent, QInputDialog,\
|
||||||
QThread, pyqtSignal
|
QThread, pyqtSignal
|
||||||
@ -125,9 +125,11 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
|||||||
self.default_thumbnail = (pixmap.width(), pixmap.height(),
|
self.default_thumbnail = (pixmap.width(), pixmap.height(),
|
||||||
pixmap_to_data(pixmap))
|
pixmap_to_data(pixmap))
|
||||||
|
|
||||||
def __init__(self, listener, opts, actions, parent=None):
|
self.last_time = datetime.datetime.now()
|
||||||
|
def __init__(self, library_path, db, listener, opts, actions, parent=None):
|
||||||
self.last_time = datetime.datetime.now()
|
self.last_time = datetime.datetime.now()
|
||||||
self.preferences_action, self.quit_action = actions
|
self.preferences_action, self.quit_action = actions
|
||||||
|
self.library_path = library_path
|
||||||
self.spare_servers = []
|
self.spare_servers = []
|
||||||
self.must_restart_before_config = False
|
self.must_restart_before_config = False
|
||||||
MainWindow.__init__(self, opts, parent)
|
MainWindow.__init__(self, opts, parent)
|
||||||
@ -518,31 +520,6 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
|||||||
if self.system_tray_icon.isVisible() and opts.start_in_tray:
|
if self.system_tray_icon.isVisible() and opts.start_in_tray:
|
||||||
self.hide_windows()
|
self.hide_windows()
|
||||||
self.stack.setCurrentIndex(0)
|
self.stack.setCurrentIndex(0)
|
||||||
try:
|
|
||||||
db = LibraryDatabase2(self.library_path)
|
|
||||||
except Exception:
|
|
||||||
import traceback
|
|
||||||
error_dialog(self, _('Bad database location'),
|
|
||||||
_('Bad database location')+':'+self.library_path,
|
|
||||||
det_msg=traceback.format_exc()).exec_()
|
|
||||||
fname = _('Calibre Library')
|
|
||||||
if isinstance(fname, unicode):
|
|
||||||
try:
|
|
||||||
fname = fname.encode(filesystem_encoding)
|
|
||||||
except:
|
|
||||||
fname = 'Calibre Library'
|
|
||||||
x = os.path.expanduser('~'+os.sep+fname)
|
|
||||||
if not os.path.exists(x):
|
|
||||||
os.makedirs(x)
|
|
||||||
dir = unicode(QFileDialog.getExistingDirectory(self,
|
|
||||||
_('Choose a location for your ebook library.'),
|
|
||||||
x))
|
|
||||||
if not dir:
|
|
||||||
QCoreApplication.exit(1)
|
|
||||||
raise SystemExit(1)
|
|
||||||
else:
|
|
||||||
self.library_path = dir
|
|
||||||
db = LibraryDatabase2(self.library_path)
|
|
||||||
db.set_book_on_device_func(self.book_on_device)
|
db.set_book_on_device_func(self.book_on_device)
|
||||||
self.library_view.set_database(db)
|
self.library_view.set_database(db)
|
||||||
self.library_view.model().set_book_on_device_func(self.book_on_device)
|
self.library_view.model().set_book_on_device_func(self.book_on_device)
|
||||||
@ -675,6 +652,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
|||||||
v.resizeRowToContents(0)
|
v.resizeRowToContents(0)
|
||||||
height = v.rowHeight(0)
|
height = v.rowHeight(0)
|
||||||
self.library_view.verticalHeader().setDefaultSectionSize(height)
|
self.library_view.verticalHeader().setDefaultSectionSize(height)
|
||||||
|
self.keyboard_interrupt.connect(self.quit, type=Qt.QueuedConnection)
|
||||||
|
|
||||||
def do_edit_categories(self):
|
def do_edit_categories(self):
|
||||||
d = TagCategories(self, self.library_view.model().db)
|
d = TagCategories(self, self.library_view.model().db)
|
||||||
@ -2399,38 +2377,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
|||||||
d.show()
|
d.show()
|
||||||
self._modeless_dialogs.append(d)
|
self._modeless_dialogs.append(d)
|
||||||
|
|
||||||
|
|
||||||
def initialize_database(self):
|
|
||||||
self.library_path = prefs['library_path']
|
|
||||||
if self.library_path is None: # Need to migrate to new database layout
|
|
||||||
base = os.path.expanduser('~')
|
|
||||||
if iswindows:
|
|
||||||
from calibre import plugins
|
|
||||||
from PyQt4.Qt import QDir
|
|
||||||
base = plugins['winutil'][0].special_folder_path(
|
|
||||||
plugins['winutil'][0].CSIDL_PERSONAL)
|
|
||||||
if not base or not os.path.exists(base):
|
|
||||||
base = unicode(QDir.homePath()).replace('/', os.sep)
|
|
||||||
dir = unicode(QFileDialog.getExistingDirectory(self,
|
|
||||||
_('Choose a location for your ebook library.'), base))
|
|
||||||
if not dir:
|
|
||||||
dir = os.path.expanduser('~/Library')
|
|
||||||
self.library_path = os.path.abspath(dir)
|
|
||||||
if not os.path.exists(self.library_path):
|
|
||||||
try:
|
|
||||||
os.makedirs(self.library_path)
|
|
||||||
except:
|
|
||||||
self.library_path = os.path.expanduser('~/CalibreLibrary')
|
|
||||||
error_dialog(self, _('Invalid library location'),
|
|
||||||
_('Could not access %s. Using %s as the library.')%
|
|
||||||
(repr(self.library_path), repr(self.library_path))
|
|
||||||
).exec_()
|
|
||||||
if not os.path.exists(self.library_path):
|
|
||||||
os.makedirs(self.library_path)
|
|
||||||
|
|
||||||
|
|
||||||
def read_settings(self):
|
def read_settings(self):
|
||||||
self.initialize_database()
|
|
||||||
geometry = config['main_window_geometry']
|
geometry = config['main_window_geometry']
|
||||||
if geometry is not None:
|
if geometry is not None:
|
||||||
self.restoreGeometry(geometry)
|
self.restoreGeometry(geometry)
|
||||||
|
@ -26,3 +26,8 @@ def server_config(defaults=None):
|
|||||||
help=_('The maximum number of matches to return per OPDS query. '
|
help=_('The maximum number of matches to return per OPDS query. '
|
||||||
'This affects Stanza, WordPlayer, etc. integration.'))
|
'This affects Stanza, WordPlayer, etc. integration.'))
|
||||||
return c
|
return c
|
||||||
|
|
||||||
|
def db():
|
||||||
|
from calibre.library.database2 import LibraryDatabase2
|
||||||
|
from calibre.utils.config import prefs
|
||||||
|
return LibraryDatabase2(prefs['library_path'])
|
||||||
|
@ -106,6 +106,9 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
self.conn = connect(self.dbpath, self.row_factory)
|
self.conn = connect(self.dbpath, self.row_factory)
|
||||||
if self.user_version == 0:
|
if self.user_version == 0:
|
||||||
self.initialize_database()
|
self.initialize_database()
|
||||||
|
# remember to add any filter to the connect method in sqlite.py as well
|
||||||
|
# so that various code taht connects directly will not complain about
|
||||||
|
# missing functions
|
||||||
self.books_list_filter = self.conn.create_dynamic_filter('books_list_filter')
|
self.books_list_filter = self.conn.create_dynamic_filter('books_list_filter')
|
||||||
|
|
||||||
def __init__(self, library_path, row_factory=False):
|
def __init__(self, library_path, row_factory=False):
|
||||||
@ -1480,7 +1483,7 @@ books_series_link feeds
|
|||||||
def check_integrity(self, callback):
|
def check_integrity(self, callback):
|
||||||
callback(0., _('Checking SQL integrity...'))
|
callback(0., _('Checking SQL integrity...'))
|
||||||
user_version = self.user_version
|
user_version = self.user_version
|
||||||
sql = self.conn.dump()
|
sql = '\n'.join(self.conn.dump())
|
||||||
self.conn.close()
|
self.conn.close()
|
||||||
dest = self.dbpath+'.tmp'
|
dest = self.dbpath+'.tmp'
|
||||||
if os.path.exists(dest):
|
if os.path.exists(dest):
|
||||||
@ -1492,7 +1495,6 @@ books_series_link feeds
|
|||||||
conn = ndb.conn
|
conn = ndb.conn
|
||||||
conn.execute('create table temp_sequence(id INTEGER PRIMARY KEY AUTOINCREMENT)')
|
conn.execute('create table temp_sequence(id INTEGER PRIMARY KEY AUTOINCREMENT)')
|
||||||
conn.commit()
|
conn.commit()
|
||||||
conn.create_function(self.books_list_filter.name, 1, lambda x: 1)
|
|
||||||
conn.executescript(sql)
|
conn.executescript(sql)
|
||||||
conn.commit()
|
conn.commit()
|
||||||
conn.execute('pragma user_version=%d'%user_version)
|
conn.execute('pragma user_version=%d'%user_version)
|
||||||
|
@ -117,6 +117,8 @@ class DBThread(Thread):
|
|||||||
self.conn.create_aggregate('sort_concat', 2, SafeSortedConcatenate)
|
self.conn.create_aggregate('sort_concat', 2, SafeSortedConcatenate)
|
||||||
self.conn.create_function('title_sort', 1, title_sort)
|
self.conn.create_function('title_sort', 1, title_sort)
|
||||||
self.conn.create_function('uuid4', 0, lambda : str(uuid.uuid4()))
|
self.conn.create_function('uuid4', 0, lambda : str(uuid.uuid4()))
|
||||||
|
# Dummy functions for dynamically created filters
|
||||||
|
self.conn.create_function('books_list_filter', 1, lambda x: 1)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
try:
|
try:
|
||||||
@ -128,7 +130,7 @@ class DBThread(Thread):
|
|||||||
break
|
break
|
||||||
if func == 'dump':
|
if func == 'dump':
|
||||||
try:
|
try:
|
||||||
ok, res = True, '\n'.join(self.conn.iterdump())
|
ok, res = True, tuple(self.conn.iterdump())
|
||||||
except Exception, err:
|
except Exception, err:
|
||||||
ok, res = False, (err, traceback.format_exc())
|
ok, res = False, (err, traceback.format_exc())
|
||||||
elif func == 'create_dynamic_filter':
|
elif func == 'create_dynamic_filter':
|
||||||
|
Loading…
x
Reference in New Issue
Block a user