Sync to trunk.

This commit is contained in:
John Schember 2009-09-04 21:48:19 -04:00
commit c5fbaf44f7
60 changed files with 19729 additions and 16729 deletions

View File

@ -60,6 +60,9 @@ def freeze():
'/usr/lib/libgcrypt.so.11',
'/usr/lib/libgpg-error.so.0',
'/usr/lib/libphonon.so.4',
'/usr/lib/libssl.so.0.9.8',
'/usr/lib/libcrypto.so.0.9.8',
'/lib/libreadline.so.6',
]
binary_includes += [os.path.join(QTDIR, 'lib%s.so.4'%x) for x in QTDLLS]

View File

@ -4,6 +4,7 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
''' Create an OSX installer '''
import sys, re, os, shutil, subprocess, stat, glob, zipfile, plistlib
sys.path = sys.path[1:]
l = {}
exec open('setup.py').read() in l
VERSION = l['VERSION']
@ -210,7 +211,6 @@ os.execv(python, args)
|stat.S_IWUSR|stat.S_IROTH|stat.S_IRGRP)
self.add_plugins()
print 'Adding fontconfig'
for f in glob.glob(os.path.expanduser('~/fontconfig-bundled/*')):
dest = os.path.join(frameworks_dir, os.path.basename(f))
@ -222,6 +222,8 @@ os.execv(python, args)
shutil.rmtree(dst)
shutil.copytree('/usr/local/etc/fonts', dst, symlinks=False)
self.add_plugins()
print
print 'Adding IPython'
dst = os.path.join(resource_dir, 'lib', 'python2.6', 'IPython')

View File

@ -0,0 +1,138 @@
#include <stdlib.h>
#include <strings.h>
#include <CoreFoundation/CoreFoundation.h>
#include <mach-o/dyld.h>
#include <Python.h>
static const char *ERR_UNKNOWNPYTHONEXCEPTION = "An uncaught exception was raised during execution of the main script, but its class or name could not be determined";
static int
report_error(const char *msg) {
fprintf(stderr, msg);
fprintf(stderr, "\n");
fflush(stderr);
return -1;
}
// These variable must be filled in before compiling
static const char *ENV_VARS[] = { /*ENV_VARS*/ NULL };
static const char *ENV_VAR_VALS[] = { /*ENV_VAR_VALS*/ NULL};
static char PROGRAM[] = "**PROGRAM**";
static const char MODULE[] = "**MODULE**";
#define EXE "@executable_path/.."
static void
set_env_vars(const char* exe_path, const char* rpath) {
int i = 0;
char buf[3*PATH_MAX];
const char *env_var, *val;
while(1) {
env_var = ENV_VARS[i];
if (env_var == NULL) break;
val = ENV_VAR_VALS[i++];
if (strstr(val, EXE) == val && strlen(val) >= strlen(EXE)+1) {
strncpy(buf, exe_path, 3*PATH_MAX-150);
strncpy(buf+strlen(exe_path), val+strlen(EXE), 150);
setenv(env_var, buf, 1);
} else
setenv(env_var, val, 1);
}
setenv("CALIBRE_LAUNCH_MODULE", MODULE, 1);
setenv("RESOURCEPATH", rpath, 1);
return;
}
int
main(int argc, char * const *argv, char * const *envp) {
char *pathPtr = NULL;
char buf[3*PATH_MAX];
int ret, i;
uint32_t buf_size = PATH_MAX+1;
char *ebuf = calloc(buf_size, sizeof(char));
ret = _NSGetExecutablePath(ebuf, &buf_size);
if (ret == -1) {
free(ebuf);
ebuf = calloc(buf_size, sizeof(char));
if (_NSGetExecutablePath(ebuf, &buf_size) != 0)
return report_error("Failed to find real path of executable.");
}
pathPtr = realpath(ebuf, buf);
if (pathPtr == NULL) {
return report_error(strerror(errno));
}
char *t;
for (i = 0; i < 3; i++) {
t = rindex(pathPtr, '/');
if (t == NULL) return report_error("Failed to determine bundle path.");
*t = '\0';
}
char rpath[PATH_MAX+1];
strncpy(rpath, pathPtr, strlen(pathPtr));
strncat(rpath, "/Contents/Resources", 50);
char exe_path[PATH_MAX+1];
strncpy(exe_path, pathPtr, strlen(pathPtr));
strncat(exe_path, "/Contents", 50);
set_env_vars(exe_path, rpath);
char main_script[PATH_MAX+1];
strncpy(main_script, rpath, strlen(rpath));
strncat(main_script, "/launcher.py", 20);
Py_SetProgramName(PROGRAM);
Py_Initialize();
char **argv_new = calloc(argc+1, sizeof(char *));
argv_new[argc] = NULL;
argv_new[0] = main_script;
memcpy(&argv_new[1], &argv[1], (argc - 1) * sizeof(char *));
PySys_SetArgv(argc, argv_new);
FILE *main_script_file = fopen(main_script, "r");
int rval = PyRun_SimpleFileEx(main_script_file, main_script, 1);
while (rval != 0) {
PyObject *exc, *exceptionClassName, *v, *exceptionName;
exc = PySys_GetObject("last_type");
if ( !exc ) {
rval = report_error(ERR_UNKNOWNPYTHONEXCEPTION);
break;
}
exceptionClassName = PyObject_GetAttrString(exc, "__name__");
if (!exceptionClassName) {
rval = report_error(ERR_UNKNOWNPYTHONEXCEPTION);
break;
}
v = PySys_GetObject("last_value");
exceptionName = (v ? PyObject_Str(v) : NULL);
char *class = PyString_AsString(exceptionClassName);
char *exception = "";
Py_DecRef(exceptionClassName);
if (exceptionName) {
exception = PyString_AsString(exceptionName);
Py_DecRef(exceptionName);
}
char msg[2000];
strncpy(msg, "An unexpected error occurred: ", 100);
strncpy(msg, class, 500);
strncpy(msg, " : ", 3);
strncpy(msg, exception, 500);
rval = report_error(msg);
break;
}
Py_Finalize();
return rval;
}

View File

@ -35,22 +35,24 @@ _recipes_pil_prescript(['Hdf5StubImagePlugin', 'FitsStubImagePlugin', 'SunImageP
def _run():
global __file__
import os, sys, site
sys.frozen = 'macosx_app'
import os, sys
base = os.environ['RESOURCEPATH']
sys.frozen = 'macosx_app'
sys.frameworks_dir = os.path.join(os.path.dirname(base), 'Frameworks')
sys.new_app_bundle = True
site.addsitedir(base)
site.addsitedir(os.path.join(base, 'Python', 'site-packages'))
sys.site_packages = os.path.join(base, 'Python', 'site-packages')
sys.binaries_path = os.path.join(os.path.dirname(base), 'MacOS')
sys.console_binaries_path = os.path.join(os.path.dirname(base),
'console.app', 'Contents', 'MacOS')
exe = os.environ.get('CALIBRE_LAUNCH_MODULE', 'calibre.gui2.main')
exe = os.path.join(base, 'Python', 'site-packages', *exe.split('.'))
exe += '.py'
sys.argv[0] = __file__ = exe
argv = os.environ.get('CALIBRE_LAUNCH_ARGV', None)
if argv is not None:
import cPickle
argv = cPickle.loads(argv)
sys.argv[1:] = argv
for arg in list(sys.argv[1:]):
if arg.startswith('-psn'):
sys.argv.remove(arg)
execfile(exe, globals(), globals())
_run()

View File

@ -1,33 +0,0 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import os, sys, cPickle
ENV = {}##ENV##
MODULE = ''##MODULE##
path = os.path.abspath(os.path.realpath(__file__))
dirpath = os.path.dirname(path)
name = os.path.basename(path)
base_dir = os.path.dirname(os.path.dirname(dirpath))
resources_dir = os.path.join(base_dir, 'Resources')
frameworks_dir = os.path.join(base_dir, 'Frameworks')
exe_dir = os.path.join(base_dir, 'MacOS')
base_name = os.path.splitext(name)[0]
python = os.path.join(base_dir, 'MacOS', 'Python')
for key, val in ENV.items():
if val.startswith('@exec'):
ENV[key] = os.path.normpath(val.replace('@executable_path', exe_dir))
ENV['CALIBRE_LAUNCH_MODULE'] = MODULE
ENV['CALIBRE_LAUNCH_ARGV'] = cPickle.dumps(sys.argv[1:], -1)
ENV['RESOURCEPATH'] = resources_dir
os.environ.update(ENV)
launcher = os.path.join(resources_dir, 'launcher.py')
args = ['-OO', launcher]
os.execv(python, args)

File diff suppressed because it is too large Load Diff

View File

@ -20,17 +20,50 @@ main_functions = l['main_functions']
main_modules = l['main_modules']
LICENSE = open('LICENSE', 'rb').read()
ENV = dict(
FC_CONFIG_DIR='@executable_path/../Resources/fonts',
MAGICK_HOME='@executable_path/../Frameworks/ImageMagick',
PYTHONDONTWRITEBYTECODE='1',
PYTHONIOENCODING='utf-8:replace',
PYTHONPATH='@executable_path/../Resources/Python/site-packages',
PYTHONHOME='@executable_path/../Resources/Python',
FC_CONFIG_DIR='@executable_path/../Resources/fonts',
MAGICK_HOME='@executable_path/../Frameworks/ImageMagick',
QT_PLUGIN_PATH='@executable_path/../MacOS',
PYTHONDONTWRITEBYTECODE='1',
PYTHONIOENCODING='utf-8:replace',
PYTHONOPTIMIZE='2',
QT_PLUGIN_PATH='@executable_path'
)
SW = os.environ.get('SW')
SW = os.environ.get('SW', '/sw')
def compile_launchers(contents_dir, xprograms):
gcc = os.environ.get('CC', 'gcc')
base = os.path.dirname(__file__)
src = open(join(base, 'launcher.c'), 'rb').read()
env, env_vals = [], []
for key, val in ENV.items():
env.append('"%s"'% key)
env_vals.append('"%s"'% val)
env = ', '.join(env)+', '
env_vals = ', '.join(env_vals)+', '
src = src.replace('/*ENV_VARS*/', env)
src = src.replace('/*ENV_VAR_VALS*/', env_vals)
programs = []
for program, module in xprograms.items():
print '\tCompiling', program
out = join(contents_dir, 'MacOS', program)
programs.append(out)
psrc = src.replace('**PROGRAM**', program)
psrc = psrc.replace('**MODULE**', module)
fsrc = '/tmp/%s.c'%program
with open(fsrc, 'wb') as f:
f.write(psrc)
cmd = [gcc, '-Wall', '-arch', 'x86_64',
'-I%s/python/Python.framework/Headers'%SW,
fsrc, '-o', out, '-F%s/python'%SW,
'-framework', 'Python', '-framework', 'CoreFoundation',
'-headerpad_max_install_names']
print ' '.join(cmd)
sys.stdout.flush()
subprocess.check_call(cmd)
return programs
def flipwritable(fn, mode=None):
"""
@ -43,6 +76,15 @@ def flipwritable(fn, mode=None):
os.chmod(fn, stat.S_IWRITE | old_mode)
return old_mode
def thin(path):
try:
subprocess.check_call(['lipo', path, '-verify_arch', 'ppc64'])
print '\tThinning', path
except:
return
else:
subprocess.check_call(['lipo', path, '-thin', 'x86_64', '-output', path])
STRIPCMD = ['/usr/bin/strip', '-x', '-S', '-']
def strip_files(files, argv_max=(256 * 1024)):
"""
@ -120,8 +162,8 @@ class Py2App(object):
self.copy_launcher_and_site()
self.create_exe()
self.thin_to_x86_64()
self.strip_files()
self.create_launchers()
ret = self.makedmg(self.build_dir, APPNAME+'-'+VERSION+'-x86_64')
sys.stdout.flush()
@ -134,23 +176,17 @@ class Py2App(object):
return ret
@flush
def create_launchers(self):
print '\nCreating launchers'
all_names = basenames['console'] + basenames['gui']
all_modules = main_modules['console'] + main_modules['gui']
launcher = join(os.path.dirname(__file__), 'loader.py')
launcher = open(launcher, 'rb').read()
launcher = launcher.replace('{}##ENV##', repr(ENV))
os.mkdir(join(self.resources_dir, 'loaders'))
for basename, module in zip(all_names, all_modules):
py_file = join('src', *module.split('.'))+'.py'
shutil.copy2(py_file, join(self.resources_dir, 'Python',
'site-packages', *module.split('.'))+'.py')
raw = launcher.replace("''##MODULE##", repr(module))
path = join(self.resources_dir, 'loaders', basename)
open(path, 'wb').write(raw)
os.chmod(path, stat.S_IXUSR|stat.S_IXGRP|stat.S_IXOTH|stat.S_IREAD\
|stat.S_IWUSR|stat.S_IROTH|stat.S_IRGRP)
def thin_to_x86_64(self):
print '\nThinning to x86_64'
for y in (self.frameworks_dir, join(self.resources_dir, 'Python')):
for x in os.walk(y):
for f in x[-1]:
f = join(x[0], f)
if not os.path.isfile(f): continue
for t in ('.so', '.dylib', '/Python'):
if f.endswith(t):
thin(f)
break
@flush
def strip_files(self):
@ -159,13 +195,19 @@ class Py2App(object):
@flush
def create_exe(self):
print '\nCreating executable'
gcc = os.environ.get('CC', 'gcc')
base = os.path.dirname(__file__)
out = join(self.contents_dir, 'MacOS', 'calibre')
subprocess.check_call([gcc, '-Wall', '-arch', 'x86_64', join(base,
'main.c'), '-o', out])
self.to_strip.append(out)
print '\nCreating launchers'
programs = {}
for program, module in zip(basenames['console'],
main_modules['console'])+zip(basenames['gui'],
main_modules['gui']):
programs[program] = module
programs = compile_launchers(self.contents_dir, programs)
for out in programs:
self.fix_dependencies_in_lib(out)
for module in main_modules['console'] + main_modules['gui']:
base = join(*module.split('.'))+'.py'
shutil.copy2(join('src', base),
join(self.resources_dir, 'Python', 'site-packages', base))
@flush
def set_id(self, path_to_lib, new_id):
@ -226,10 +268,6 @@ class Py2App(object):
shutil.copy2(join(curr, 'Python'), currd)
self.set_id(join(currd, 'Python'),
self.FID+'/Python.framework/Versions/%s/Python'%basename(curr))
python = '%s/python/Python.framework/Versions/%s/Resources/Python.app/Contents/MacOS/Python'\
% (SW, self.version_info)
shutil.copy2(python, join(self.contents_dir, 'MacOS'))
self.fix_dependencies_in_lib(join(self.contents_dir, 'MacOS', 'Python'))
@flush
def add_qt_frameworks(self):
@ -272,10 +310,16 @@ class Py2App(object):
for f in glob.glob('src/calibre/plugins/*.so'):
shutil.copy2(f, dest)
self.fix_dependencies_in_lib(join(dest, basename(f)))
if 'podofo' in f:
self.change_dep('libpodofo.0.6.99.dylib',
self.FID+'/'+'libpodofo.0.6.99.dylib', join(dest, basename(f)))
@flush
def create_plist(self):
env = dict(**ENV)
env['CALIBRE_LAUNCHED_FROM_BUNDLE']='1';
pl = dict(
CFBundleDevelopmentRegion='English',
CFBundleDisplayName=APPNAME,
@ -286,7 +330,6 @@ class Py2App(object):
CFBundleSignature='????',
CFBundleExecutable='calibre',
LSMinimumSystemVersion='10.5.2',
PyRuntimeLocations=[self.FID+'/Python.framework/Versions/%s/Python'%self.version_info],
LSRequiresNativeExecution=True,
NSAppleScriptEnabled=False,
NSHumanReadableCopyright='Copyright 2008, Kovid Goyal',
@ -294,7 +337,7 @@ class Py2App(object):
'application. Visit http://calibre.kovidgoyal.net for details.'),
CFBundleIconFile='library.icns',
LSMultipleInstancesProhibited=True,
LSEnvironment=ENV
LSEnvironment=env
)
plistlib.writePlist(pl, join(self.contents_dir, 'Info.plist'))
@ -374,7 +417,7 @@ class Py2App(object):
@flush
def add_misc_libraries(self):
for x in ('usb', 'unrar'):
for x in ('usb', 'unrar', 'readline.6.0'):
print '\nAdding', x
x = 'lib%s.dylib'%x
shutil.copy2(join(SW, 'lib', x), self.frameworks_dir)
@ -551,9 +594,16 @@ class Py2App(object):
print '\nInstaller size: %.2fMB\n'%size
return dmg
def test_exe():
build_dir = abspath(join('build', APPNAME+'.app'))
py2app = Py2App(build_dir)
py2app.create_exe()
return 0
def main():
if 'test_exe' in sys.argv:
return test_exe()
build_dir = abspath(join('build', APPNAME+'.app'))
if os.path.exists(build_dir):
shutil.rmtree(build_dir)

View File

@ -73,22 +73,29 @@ if __name__ == '__main__':
upload_to_pypi, stage3, stage2, stage1, upload, \
upload_rss, betas, build_linux32, build_linux64, \
build_osx64
resources.SCRIPTS = list(basenames['console']+basenames['gui'])
resources.SCRIPTS = {}
for x in ('console', 'gui'):
for name in basenames[x]:
resources.SCRIPTS[name] = x
list(basenames['console']+basenames['gui'])
entry_points['console_scripts'].append(
'calibre_postinstall = calibre.linux:post_install')
optional = []
def qmake_query(arg=''):
return subprocess.Popen([QMAKE, '-query', arg],
stdout=subprocess.PIPE).stdout.read()
cmd = [QMAKE, '-query']
if arg:
cmd += [arg]
return subprocess.Popen(cmd, stdout=subprocess.PIPE).stdout.read()
qt_inc = qt_lib = None
qt_inc = qmake_query('QT_INSTALL_HEADERS').splitlines()[0]
qt_inc = qt_inc if qt_inc not in ('', '**Unknown**') and os.path.isdir(qt_inc) else None
qt_lib = qmake_query('QT_INSTALL_LIBS').splitlines()[0]
qt_lib = qt_lib if qt_lib not in ('', '**Unknown**') and os.path.isdir(qt_lib) else None
if qt_lib is None or qt_inc is None:
print 'WARNING: Could not find QT librariers and headers.',
print 'Is qmake in your PATH?'
print '\n\nWARNING: Could not find QT librariers and headers.',
print 'Is qmake in your PATH?\n\n'
if iswindows:
@ -121,9 +128,9 @@ if __name__ == '__main__':
poppler_lib), qt_lib],
include_dirs=[poppler_inc, qt_inc]))
else:
print 'WARNING: Poppler not found on your system. Various PDF related',
print '\n\nWARNING: Poppler not found on your system. Various PDF related',
print 'functionality will not work. Use the POPPLER_INC_DIR and',
print 'POPPLER_LIB_DIR environment variables.'
print 'POPPLER_LIB_DIR environment variables.\n\n'
podofo_inc = '/usr/include/podofo' if islinux else \
'C:\\podofo\\include\\podofo' if iswindows else \
@ -138,9 +145,9 @@ if __name__ == '__main__':
library_dirs=[os.environ.get('PODOFO_LIB_DIR', podofo_lib)],
include_dirs=[podofo_inc]))
else:
print 'WARNING: PoDoFo not found on your system. Various PDF related',
print '\n\nWARNING: PoDoFo not found on your system. Various PDF related',
print 'functionality will not work. Use the PODOFO_INC_DIR and',
print 'PODOFO_LIB_DIR environment variables.'
print 'PODOFO_LIB_DIR environment variables.\n\n'
fc_inc = '/usr/include/fontconfig' if islinux else \
r'C:\cygwin\home\kovid\fontconfig\include\fontconfig' if iswindows else \
@ -152,8 +159,8 @@ if __name__ == '__main__':
fc_inc = os.environ.get('FC_INC_DIR', fc_inc)
fc_lib = os.environ.get('FC_LIB_DIR', fc_lib)
if not os.path.exists(os.path.join(fc_inc, 'fontconfig.h')):
print 'ERROR: fontconfig not found on your system.',
print 'Use the FC_INC_DIR and FC_LIB_DIR environment variables.'
print '\n\nERROR: fontconfig not found on your system.',
print 'Use the FC_INC_DIR and FC_LIB_DIR environment variables.\n\n'
raise SystemExit(1)
ext_modules = optional + [

View File

@ -2,7 +2,7 @@ __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en'
__appname__ = 'calibre'
__version__ = '0.6.10'
__version__ = '0.6.11'
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
import re

View File

@ -6,7 +6,7 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
Embedded console for debugging.
'''
import sys, os, re
import sys, os, re, shutil
from calibre.utils.config import OptionParser
from calibre.constants import iswindows, isosx
from calibre.libunzip import update
@ -45,6 +45,9 @@ def update_zipfile(zipfile, mod, path):
name = mod.replace('.', '/') + os.path.splitext(path)[-1]
update(zipfile, [pat], [path], [name])
def update_site_packages(sp, mod, path):
dest = os.path.join(sp, *mod.split('.'))+'.py'
shutil.copy2(path, dest)
def update_module(mod, path):
if not hasattr(sys, 'frozen'):
@ -52,6 +55,8 @@ def update_module(mod, path):
zp = None
if iswindows:
zp = os.path.join(os.path.dirname(sys.executable), 'library.zip')
elif getattr(sys, 'new_app_bundle', False):
update_site_packages(sys.site_packages, mod, path)
elif isosx:
zp = os.path.join(os.path.dirname(getattr(sys, 'frameworks_dir')),
'Resources', 'lib',

View File

@ -38,7 +38,7 @@ class PRS505(CLI, Device):
WINDOWS_CARD_A_MEM = re.compile(r'PRS-505/\S+:MS')
WINDOWS_CARD_B_MEM = re.compile(r'PRS-505/\S+:SD')
OSX_MAIN_MEM = re.compile(r'Sony PRS-(505|300)/[^:]+ Media')
OSX_MAIN_MEM = re.compile(r'Sony PRS-(((505|300)/[^:]+)|(300)) Media')
OSX_CARD_A_MEM = re.compile(r'Sony PRS-505/[^:]+:MS Media')
OSX_CARD_B_MEM = re.compile(r'Sony PRS-505/[^:]+:SD Media')
@ -55,6 +55,7 @@ class PRS505(CLI, Device):
EBOOK_DIR_MAIN = 'database/media/books'
def open(self):
self.report_progress = lambda x, y: x
Device.open(self)
def write_cache(prefix):

View File

@ -21,12 +21,12 @@ class PRS700(PRS505):
BCD = [0x31a]
WINDOWS_MAIN_MEM = re.compile('PRS-[67]00')
WINDOWS_CARD_A_MEM = re.compile(r'PRS-[67]00/\S+:MS')
WINDOWS_CARD_B_MEM = re.compile(r'PRS-[67]00/\S+:SD')
WINDOWS_MAIN_MEM = re.compile('PRS-((700/)|(600&))')
WINDOWS_CARD_A_MEM = re.compile(r'PRS-((700/\S+:)|(600_))MS')
WINDOWS_CARD_B_MEM = re.compile(r'PRS-((700/\S+:)|(600_))SD')
OSX_MAIN_MEM = re.compile(r'Sony PRS-[67]00/[^:]+ Media')
OSX_CARD_A_MEM = re.compile(r'Sony PRS-[67]00/[^:]+:MS Media')
OSX_CARD_B_MEM = re.compile(r'Sony PRS-[67]00/[^:]+:SD Media')
OSX_MAIN_MEM = re.compile(r'Sony PRS-((700/[^:]+)|(600)) Media')
OSX_CARD_A_MEM = re.compile(r'Sony PRS-((700/[^:]+:)|(600 ))MS Media')
OSX_CARD_B_MEM = re.compile(r'Sony PRS-((700/[^:]+:)|(600 ))SD Media')

View File

@ -814,7 +814,6 @@ OptionRecommendation(name='language',
out_dir = os.path.join(self.opts.debug_pipeline, 'processed')
self.dump_oeb(self.oeb, out_dir)
self.log('Processed HTML written to:', out_dir)
return
self.log.info('Creating %s...'%self.output_plugin.name)
our = CompositeProgressReporter(0.67, 1., self.ui_reporter)

View File

@ -127,6 +127,7 @@ class Stylizer(object):
if elem.tag == XHTML('style') and elem.text \
and elem.get('type', CSS_MIME) in OEB_STYLES:
text = XHTML_CSS_NAMESPACE + elem.text
text = oeb.css_preprocessor(text)
stylesheet = parser.parseString(text, href=cssname)
stylesheet.namespaces['h'] = XHTML_NS
stylesheets.append(stylesheet)

View File

@ -23,7 +23,8 @@ class TXTInput(InputFormatPlugin):
'With this option it will assume that every line represents '
'a paragraph instead.')),
OptionRecommendation(name='markdown', recommended_value=False,
help=_('Run the text input though the markdown processor.')),
help=_('Run the text input through the markdown pre-processor. To '
'learn more about markdown see')+' http://daringfireball.net/projects/markdown/'),
])
def convert(self, stream, options, file_ext, log,

View File

@ -464,9 +464,16 @@ class ConfigDialog(QDialog, Ui_Dialog):
def create_symlinks(self):
from calibre.utils.osx_symlinks import create_symlinks
loc, paths = create_symlinks()
info_dialog(self, _('Command line tools installed'),
_('Command line tools installed in')+' '+loc,
if loc is None:
error_dialog(self, _('Error'),
_('Failed to install command line tools.'),
det_msg=paths, show=True)
else:
info_dialog(self, _('Command line tools installed'),
'<p>'+_('Command line tools installed in')+' '+loc+
'<br>'+ _('If you move calibre.app, you have to re-install '
'the command line tools.'),
det_msg='\n'.join(paths), show=True)
def setup_conversion_options(self):
self.conversion_options = ConfigTabs(self)

View File

@ -699,10 +699,7 @@
<item>
<layout class="QVBoxLayout" name="verticalLayout_12">
<item>
<widget class="QToolButton" name="compact_button">
<property name="toolTip">
<string>Free unused diskspace from the database</string>
</property>
<widget class="QPushButton" name="compact_button">
<property name="text">
<string>&amp;Check database integrity</string>
</property>

View File

@ -122,21 +122,49 @@ class TagsModel(QStandardItemModel):
self._data[category], self.cmap[r], self.bold_font, self.icon_map))
#self.reset()
def reset_all_states(self):
changed_indices = []
for category in self._data.values():
Category = self.find_category(category)
for tag in category:
if tag.state != 0:
tag.state = 0
if Category is not None:
Tag = self.find_tag(tag, Category)
if Tag is not None:
changed_indices.append(Tag.index())
for idx in changed_indices:
if idx.isValid():
self.emit(SIGNAL('dataChanged(QModelIndex,QModelIndex)'),
idx, idx)
def clear_state(self):
for category in self._data.values():
for tag in category:
tag.state = 0
self.refresh()
self.reset_all_states()
def find_category(self, name):
root = self.invisibleRootItem()
for i in range(root.rowCount()):
child = root.child(i)
if getattr(child, 'category', None) == name:
return child
def find_tag(self, tag, category):
for i in range(category.rowCount()):
child = category.child(i)
if getattr(child, 'tag', None) == tag:
return child
def reinit(self, *args, **kwargs):
if self.ignore_next_search == 0:
for category in self._data.values():
for tag in category:
tag.state = 0
self.reset()
self.reset_all_states()
else:
self.ignore_next_search -= 1
def toggle(self, index):
if index.parent().isValid():
category = self.row_map[index.parent().row()]

View File

@ -42,8 +42,8 @@ def convert_single_ebook(parent, db, book_ids, auto_conversion=False, out_format
result = d.exec_()
if result == QDialog.Accepted:
if not convert_existing(parent, db, [book_id], d.output_format):
continue
#if not convert_existing(parent, db, [book_id], d.output_format):
# continue
mi = db.get_metadata(book_id, True)
in_file = db.format_abspath(book_id, d.input_format, True)
@ -198,7 +198,10 @@ def convert_existing(parent, db, book_ids, output_format):
already_converted_ids.append(book_id)
already_converted_titles.append(db.get_metadata(book_id, True).title)
if not question_dialog(parent, _('Convert existing'), _('The following books have already been converted to %s format. Do you wish to reconvert them?' % output_format), '\n'.join(already_converted_titles)):
if not question_dialog(parent, _('Convert existing'),
_('The following books have already been converted to %s format. '
'Do you wish to reconvert them?') % output_format,
'\n'.join(already_converted_titles)):
book_ids = [x for x in book_ids if x not in already_converted_ids]
return book_ids

View File

@ -114,6 +114,24 @@ class LibraryServer(object):
</entry>
'''))
STANZA_TAG_ENTRY=MarkupTemplate(textwrap.dedent('''\
<entry xmlns:py="http://genshi.edgewall.org/">
<title>${tags}</title>
<id>urn:calibre:${record[FM['id']]}</id>
<updated>${timestamp}</updated>
<link type="application/atom+xml" href="/stanza/?tagid=${record[FM['id']]}" />
</entry>
'''))
STANZA_SERIES_ENTRY=MarkupTemplate(textwrap.dedent('''\
<entry xmlns:py="http://genshi.edgewall.org/">
<title>${series}</title>
<id>urn:calibre:${record[FM['id']]}</id>
<updated>${timestamp}</updated>
<link type="application/atom+xml" href="/stanza/?seriesid=${record[FM['id']]}" />
</entry>
'''))
STANZA = MarkupTemplate(textwrap.dedent('''\
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:py="http://genshi.edgewall.org/">
@ -166,6 +184,18 @@ class LibraryServer(object):
<updated>${updated.strftime('%Y-%m-%dT%H:%M:%S+00:00')}</updated>
<link type="application/atom+xml" href="/stanza/?sortby=bynewest" />
</entry>
<entry>
<title>By Tag</title>
<id>urn:uuid:824921e8-db8a-4e61-7d38-f1ce41502853</id>
<updated>${updated.strftime('%Y-%m-%dT%H:%M:%S+00:00')}</updated>
<link type="application/atom+xml" href="/stanza/?sortby=bytag" />
</entry>
<entry>
<title>By Series</title>
<id>urn:uuid:512a5e50-a88f-f6b8-82aa-8f129c719f61</id>
<updated>${updated.strftime('%Y-%m-%dT%H:%M:%S+00:00')}</updated>
<link type="application/atom+xml" href="/stanza/?sortby=byseries" />
</entry>
</feed>
'''))
@ -339,30 +369,46 @@ class LibraryServer(object):
@expose
def stanza(self, search=None, sortby=None, authorid=None):
def stanza(self, search=None, sortby=None, authorid=None, tagid=None, seriesid=None):
'Feeds to read calibre books on a ipod with stanza.'
books = []
updated = self.db.last_modified()
cherrypy.response.headers['Last-Modified'] = self.last_modified(updated)
cherrypy.response.headers['Content-Type'] = 'text/xml'
if not sortby and not search and not authorid:
if not sortby and not search and not authorid and not tagid and not seriesid:
return self.STANZA_MAIN.generate(subtitle='', data=books, FM=FIELD_MAP,
updated=updated, id='urn:calibre:main').render('xml')
if authorid:
authorid=int(authorid)
au = self.db.authors(authorid, index_is_id=True)
ids = self.db.data.get_matches('authors', au)
elif tagid:
tagid=int(tagid)
ta = self.db.tags(tagid, index_is_id=True)
ids = self.db.data.get_matches('tags', ta)
elif seriesid:
seriesid=int(seriesid)
se = self.db.series(seriesid, index_is_id=True)
ids = self.db.data.get_matches('series', se)
else:
ids = self.db.data.parse(search) if search and search.strip() else self.db.data.universal_set()
record_list = list(iter(self.db))
if sortby == "byauthor":
record_list.sort(lambda x, y: cmp(x[FIELD_MAP['author_sort']], y[FIELD_MAP['author_sort']]))
elif sortby == "bytitle" or authorid:
elif sortby == "bytag":
record_list.sort(lambda x, y: cmp(x[FIELD_MAP['tags']], y[FIELD_MAP['tags']]))
elif sortby == "byseries":
record_list.sort(lambda x, y: cmp(x[FIELD_MAP['series']], y[FIELD_MAP['series']]))
elif sortby == "bytitle" or authorid or tagid:
record_list.sort(lambda x, y: cmp(title_sort(x[FIELD_MAP['title']]),
title_sort(y[FIELD_MAP['title']])))
elif seriesid:
record_list.sort(lambda x, y: cmp(x[FIELD_MAP['series_index']], y[FIELD_MAP['series_index']]))
else:
record_list = reversed(record_list)
author_list=[]
tag_list=[]
series_list=[]
for record in record_list:
if record[0] not in ids: continue
r = record[FIELD_MAP['formats']]
@ -401,6 +447,30 @@ class LibraryServer(object):
fmt=fmt,
timestamp=strftime('%Y-%m-%dT%H:%M:%S+00:00', record[5]),
).render('xml').decode('utf8'))
elif sortby == "bytag":
if tags and tags not in tag_list:
tag_list.append(tags)
books.append(self.STANZA_TAG_ENTRY.generate(
tags=tags,
record=record, FM=FIELD_MAP,
port=self.opts.port,
extra=''.join(extra),
mimetype=mimetype,
fmt=fmt,
timestamp=strftime('%Y-%m-%dT%H:%M:%S+00:00', record[5]),
).render('xml').decode('utf8'))
elif sortby == "byseries":
if series and series not in series_list:
series_list.append(series)
books.append(self.STANZA_SERIES_ENTRY.generate(
series=series,
record=record, FM=FIELD_MAP,
port=self.opts.port,
extra=''.join(extra),
mimetype=mimetype,
fmt=fmt,
timestamp=strftime('%Y-%m-%dT%H:%M:%S+00:00', record[5]),
).render('xml').decode('utf8'))
else:
books.append(self.STANZA_ENTRY.generate(
authors=authors,
@ -415,6 +485,7 @@ class LibraryServer(object):
return self.STANZA.generate(subtitle='', data=books, FM=FIELD_MAP,
updated=updated, id='urn:calibre:main').render('xml')
@expose
def library(self, start='0', num='50', sort=None, search=None,
_=None, order='ascending'):
@ -465,7 +536,8 @@ class LibraryServer(object):
cherrypy.request.headers.get('Stanza-Device-Name', 919) != 919 or \
cherrypy.request.headers.get('Want-OPDS-Catalog', 919) != 919 or \
ua.startswith('Stanza')
return self.stanza(search=kwargs.get('search', None), sortby=kwargs.get('sortby',None), authorid=kwargs.get('authorid',None)) if want_opds else self.static('index.html')
return self.stanza(search=kwargs.get('search', None), sortby=kwargs.get('sortby',None), authorid=kwargs.get('authorid',None),
tagid=kwargs.get('tagid',None), seriesid=kwargs.get('seriesid',None)) if want_opds else self.static('index.html')
@expose

View File

@ -22,7 +22,7 @@
</div>
<div id="count_bar">
<span id="left"><img src="/static/first.png" alt="Show first set of books" title="Show first set of books"/>&nbsp;<img src="/static/previous.png" alt="Show previous set of books" title="Show previous set of books"/>&nbsp;</span><span id="count">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>&nbsp;<span id="right"><img src="/static/next.png" alt="Show first set of books" title="Show first set of books"/>&nbsp;<img src="/static/last.png" alt="Show previous set of books" title="Show previous set of books" /></span>
<span id="left"><img src="/static/first.png" alt="Show first set of books" title="Show first set of books"/>&nbsp;<img src="/static/previous.png" alt="Show previous set of books" title="Show previous set of books"/>&nbsp;</span><span id="count">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>&nbsp;<span id="right"><img src="/static/next.png" alt="Show next set of books" title="Show next set of books"/>&nbsp;<img src="/static/last.png" alt="Show last set of books" title="Show last set of books" /></span>
</div>
<div id="main">

View File

@ -78,7 +78,7 @@ Device Integration
What devices does |app| support?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
At the moment |app| has full support for the SONY PRS 500/505/700, Cybook Gen 3/Opus, Amazon Kindle 1/2/DX, Netronix EB600, Ectaco Jetbook, BeBook/BeBook Mini, Irex Illiad/DR1000, Adroid phones and the iPhone. In addition, using the :guilabel:`Save to disk` function you can use it with any ebook reader that exports itself as a USB disk.
At the moment |app| has full support for the SONY PRS 300/500/505/600/700, Cybook Gen 3/Opus, Amazon Kindle 1/2/DX, Netronix EB600, Ectaco Jetbook, BeBook/BeBook Mini, Irex Illiad/DR1000, Adroid phones and the iPhone. In addition, using the :guilabel:`Save to disk` function you can use it with any ebook reader that exports itself as a USB disk.
I used |app| to transfer some books to my reader, and now the SONY software hangs every time I connect the reader?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -180,7 +180,7 @@ else:
data = dict(version = version, name='osx',
installer_name='OS X universal dmg',
title='Download %s for OS X'%(__appname__),
compatibility='%s works on OS X Tiger and Leopard, but not Snow Leopard.'%(__appname__,),
compatibility='%s works on OS X Tiger and Leopard, but not Snow Leopard (It might work on Snow Leopard if run with Rosetta).'%(__appname__,),
path=MOBILEREAD+file, app=__appname__,
note=Markup(\
u'''

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -4,9 +4,9 @@
#
msgid ""
msgstr ""
"Project-Id-Version: calibre 0.6.10\n"
"POT-Creation-Date: 2009-09-01 21:13+MDT\n"
"PO-Revision-Date: 2009-09-01 21:13+MDT\n"
"Project-Id-Version: calibre 0.6.11\n"
"POT-Creation-Date: 2009-09-04 15:35+MDT\n"
"PO-Revision-Date: 2009-09-04 15:35+MDT\n"
"Last-Translator: Automatically generated\n"
"Language-Team: LANGUAGE\n"
"MIME-Version: 1.0\n"
@ -75,8 +75,8 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/pdb/input.py:33
#: /home/kovid/work/calibre/src/calibre/ebooks/pdb/palmdoc/writer.py:29
#: /home/kovid/work/calibre/src/calibre/ebooks/pdb/ztxt/writer.py:27
#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/manipulate/crop.py:81
#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/manipulate/crop.py:82
#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/manipulate/crop.py:83
#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/manipulate/decrypt.py:75
#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/manipulate/decrypt.py:76
#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/manipulate/encrypt.py:61
@ -127,8 +127,8 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1423
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1425
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1534
#: /home/kovid/work/calibre/src/calibre/library/server.py:373
#: /home/kovid/work/calibre/src/calibre/library/server.py:446
#: /home/kovid/work/calibre/src/calibre/library/server.py:419
#: /home/kovid/work/calibre/src/calibre/library/server.py:517
#: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:45
#: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:63
#: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:77
@ -392,8 +392,8 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/cybookg3/driver.py:76
#: /home/kovid/work/calibre/src/calibre/devices/jetbook/driver.py:76
#: /home/kovid/work/calibre/src/calibre/devices/jetbook/driver.py:78
#: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:129
#: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:131
#: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:130
#: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:132
#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:99
#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:101
msgid "Transferring books to device..."
@ -441,10 +441,10 @@ msgid "Communicate with the Sony PRS-500 eBook reader."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/prs505/books.py:150
#: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:92
#: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:95
#: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:98
#: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:109
#: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:93
#: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:96
#: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:99
#: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:110
#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:49
#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:52
#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:55
@ -463,14 +463,14 @@ msgstr ""
msgid "Kovid Goyal and John Schember"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:162
#: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:169
#: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:163
#: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:170
#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:119
#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:134
msgid "Removing books from device..."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:197
#: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:198
#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:149
msgid "Sending metadata to device..."
msgstr ""
@ -920,7 +920,7 @@ msgstr ""
msgid "Running transforms on ebook..."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:822
#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:821
msgid "Creating"
msgstr ""
@ -940,10 +940,10 @@ msgstr ""
msgid "Normally, if the input file has no cover and you don't specify one, a default cover is generated with the title, authors, etc. This option disables the generation of this cover."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/fb2/fb2ml.py:123
#: /home/kovid/work/calibre/src/calibre/ebooks/fb2/fb2ml.py:122
#: /home/kovid/work/calibre/src/calibre/ebooks/pml/pmlml.py:111
#: /home/kovid/work/calibre/src/calibre/ebooks/rb/rbml.py:98
#: /home/kovid/work/calibre/src/calibre/ebooks/txt/txtml.py:70
#: /home/kovid/work/calibre/src/calibre/ebooks/txt/txtml.py:72
msgid "Table of Contents:"
msgstr ""
@ -1623,14 +1623,14 @@ msgid ""
"Manipulate a PDF.\n"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/manipulate/crop.py:28
#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/manipulate/crop.py:29
msgid ""
"[options] file.pdf\n"
"\n"
"Crop a PDF file.\n"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/manipulate/crop.py:37
#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/manipulate/crop.py:38
#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/manipulate/decrypt.py:34
#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/manipulate/encrypt.py:32
#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/manipulate/merge.py:36
@ -1640,31 +1640,31 @@ msgstr ""
msgid "Path to output file. By default a file is created in the current directory."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/manipulate/crop.py:40
#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/manipulate/crop.py:41
msgid "Number of pixels to crop from the left most x (default is %s)"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/manipulate/crop.py:43
#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/manipulate/crop.py:44
msgid "Number of pixels to crop from the left most y (default is %s)"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/manipulate/crop.py:46
#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/manipulate/crop.py:47
msgid "Number of pixels to crop from the right most x (default is %s)"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/manipulate/crop.py:49
#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/manipulate/crop.py:50
msgid "Number of pixels to crop from the right most y (default is %s)"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/manipulate/crop.py:52
#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/manipulate/crop.py:53
msgid "A file generated by ghostscript which allows each page to be individually cropped `gs -dSAFER -dNOPAUSE -dBATCH -sDEVICE=bbox file.pdf 2> bounding`"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/manipulate/crop.py:72
#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/manipulate/crop.py:73
msgid "Crop Options:"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/manipulate/crop.py:72
#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/manipulate/crop.py:73
#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/manipulate/decrypt.py:62
#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/manipulate/encrypt.py:52
#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/manipulate/merge.py:56
@ -1810,7 +1810,7 @@ msgid "This RTF file has a feature calibre does not support. Convert it to HTML
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/txt/input.py:26
msgid "Run the text input though the markdown processor."
msgid "Run the text input through the markdown pre-processor. To learn more about markdown see"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/txt/output.py:24
@ -1821,6 +1821,15 @@ msgstr ""
msgid "Specify the character encoding of the output document. The default is utf-8. Note: This option is not honored by all formats."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/txt/output.py:38
#: /home/kovid/work/calibre/src/calibre/gui2/convert/txt_output_ui.py:44
msgid "Do not add a blank line between paragraphs."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/txt/output.py:41
msgid "Add a tab at the beginning of each paragraph."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:28
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:505
msgid "Frequently used directories"
@ -2021,8 +2030,8 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/rb_output_ui.py:28
#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection_ui.py:67
#: /home/kovid/work/calibre/src/calibre/gui2/convert/toc_ui.py:61
#: /home/kovid/work/calibre/src/calibre/gui2/convert/txt_input_ui.py:28
#: /home/kovid/work/calibre/src/calibre/gui2/convert/txt_output_ui.py:35
#: /home/kovid/work/calibre/src/calibre/gui2/convert/txt_input_ui.py:35
#: /home/kovid/work/calibre/src/calibre/gui2/convert/txt_output_ui.py:41
#: /home/kovid/work/calibre/src/calibre/gui2/convert/xpath_edit_ui.py:41
#: /home/kovid/work/calibre/src/calibre/gui2/convert/xpath_wizard_ui.py:67
#: /home/kovid/work/calibre/src/calibre/gui2/device_drivers/configwidget_ui.py:59
@ -2124,7 +2133,7 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:509
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:525
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:526
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:557
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:556
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:343
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:348
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:362
@ -2192,7 +2201,7 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/fb2_output_ui.py:29
#: /home/kovid/work/calibre/src/calibre/gui2/convert/pdb_output_ui.py:37
#: /home/kovid/work/calibre/src/calibre/gui2/convert/rb_output_ui.py:29
#: /home/kovid/work/calibre/src/calibre/gui2/convert/txt_output_ui.py:37
#: /home/kovid/work/calibre/src/calibre/gui2/convert/txt_output_ui.py:43
msgid "&Inline TOC"
msgstr ""
@ -2498,7 +2507,7 @@ msgid "PDB Input"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/pdb_input_ui.py:29
#: /home/kovid/work/calibre/src/calibre/gui2/convert/txt_input_ui.py:29
#: /home/kovid/work/calibre/src/calibre/gui2/convert/txt_input_ui.py:36
msgid "Treat each &line as a paragraph"
msgstr ""
@ -2686,14 +2695,26 @@ msgstr ""
msgid "TXT Input"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/txt_input_ui.py:37
msgid "Process using markdown"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/txt_input_ui.py:38
msgid "<p>Markdown is a simple markup language for text files, that allows for advanced formatting. To learn more visit <a href=\"http://daringfireball.net/projects/markdown\">markdown</a>."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/txt_output.py:16
msgid "TXT Output"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/txt_output_ui.py:36
#: /home/kovid/work/calibre/src/calibre/gui2/convert/txt_output_ui.py:42
msgid "&Line ending style:"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/txt_output_ui.py:45
msgid "Add a tab at the beginning of each paragraph"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/xpath_edit_ui.py:42
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info_ui.py:62
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info_ui.py:63
@ -3099,117 +3120,126 @@ msgstr ""
msgid "new email address"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:467
msgid "Command line tools installed"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:468
msgid "Command line tools installed in"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:518
msgid "No valid plugin path"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:519
msgid "%s is not a valid plugin path"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:522
msgid "Choose plugin"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:534
msgid "Plugin cannot be disabled"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:535
msgid "The plugin: %s cannot be disabled"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:544
msgid "Plugin not customizable"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:545
msgid "Plugin: %s does not need customization"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:569
msgid "Customize %s"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:579
msgid "Cannot remove builtin plugin"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:580
msgid " cannot be removed. It is a builtin plugin. Try disabling it instead."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:613
msgid "Error log:"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:620
msgid "Access log:"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:645
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:574
msgid "Failed to start content server"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:669
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:471
msgid "Select location for books"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:686
msgid "Invalid size"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:687
msgid "The size %s is invalid. must be of the form widthxheight"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:731
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:736
msgid "Invalid database location"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:732
msgid "Invalid database location "
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:733
msgid "<br>Must be a directory."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:737
msgid "Invalid database location.<br>Cannot write to "
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:775
msgid "Checking database integrity"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:794
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:801
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:142
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1005
#: /home/kovid/work/calibre/src/calibre/utils/ipc/job.py:52
msgid "Error"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:795
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:469
msgid "Failed to install command line tools."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:472
msgid "Command line tools installed"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:473
msgid "Command line tools installed in"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:474
msgid "If you move calibre.app, you have to re-install the command line tools."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:525
msgid "No valid plugin path"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:526
msgid "%s is not a valid plugin path"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:529
msgid "Choose plugin"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:541
msgid "Plugin cannot be disabled"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:542
msgid "The plugin: %s cannot be disabled"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:551
msgid "Plugin not customizable"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:552
msgid "Plugin: %s does not need customization"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:576
msgid "Customize %s"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:586
msgid "Cannot remove builtin plugin"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:587
msgid " cannot be removed. It is a builtin plugin. Try disabling it instead."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:620
msgid "Error log:"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:627
msgid "Access log:"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:652
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:574
msgid "Failed to start content server"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:676
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:471
msgid "Select location for books"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:693
msgid "Invalid size"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:694
msgid "The size %s is invalid. must be of the form widthxheight"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:738
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:743
msgid "Invalid database location"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:739
msgid "Invalid database location "
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:740
msgid "<br>Must be a directory."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:744
msgid "Invalid database location.<br>Cannot write to "
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:782
msgid "Checking database integrity"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:802
msgid "Failed to check database integrity"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:800
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:807
msgid "Some inconsistencies found"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:801
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:808
msgid "The following books had formats listed in the database that are not actually available. The entries for the formats have been removed. You should check them manually. This can happen if you manipulate the files in the library folder directly."
msgstr ""
@ -3444,109 +3474,105 @@ msgid "calibre can send your books to you (or your reader) by email"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:533
msgid "Free unused diskspace from the database"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:534
msgid "&Check database integrity"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:535
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:534
msgid "&Install command line tools"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:536
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:535
msgid "calibre contains a network server that allows you to access your book collection using a browser from anywhere in the world. Any changes to the settings will only take effect after a server restart."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:537
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:536
msgid "Server &port:"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:538
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:537
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/password_ui.py:58
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:178
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/send_email_ui.py:117
msgid "&Username:"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:539
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:538
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/password_ui.py:59
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:179
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/send_email_ui.py:119
msgid "&Password:"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:540
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:539
msgid "If you leave the password blank, anyone will be able to access your book collection using the web interface."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:541
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:540
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/password_ui.py:60
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:180
msgid "&Show password"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:542
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:541
msgid "The maximum size (widthxheight) for displayed covers. Larger covers are resized. "
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:543
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:542
msgid "Max. &cover size:"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:544
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:543
msgid "&Start Server"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:545
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:544
msgid "St&op Server"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:546
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:545
msgid "&Test Server"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:547
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:546
msgid "Run server &automatically on startup"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:548
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:547
msgid "View &server logs"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:549
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:548
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/stanza_ui.py:46
msgid ""
"<p>Remember to leave calibre running as the server only runs as long as calibre is running.\n"
"<p>Stanza should see your calibre collection automatically. If not, try adding the URL http://myhostname:8080 as a new catalog in the Stanza reader on your iPhone. Here myhostname should be the fully qualified hostname or the IP address of the computer calibre is running on."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:551
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:550
msgid "Here you can customize the behavior of Calibre by controlling what plugins it uses."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:552
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:551
msgid "Enable/&Disable plugin"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:553
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:552
msgid "&Customize plugin"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:554
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:553
msgid "&Remove plugin"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:555
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:554
msgid "Add new plugin"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:556
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:555
msgid "Plugin &file:"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:558
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:557
msgid "&Add"
msgstr ""
@ -5135,33 +5161,41 @@ msgid "Publishers"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:30
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:94
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:97
msgid "Starting conversion of %d books"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:53
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:133
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:56
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:137
msgid "Convert book %d of %d (%s)"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:79
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:150
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:82
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:154
msgid "Could not convert some books"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:80
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:151
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:83
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:155
msgid "Could not convert %d of %d books, because no suitable source format was found."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:182
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:186
msgid "You must set a username and password for %s"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:187
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:191
msgid "Fetch news from "
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:201
msgid "Convert existing"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:202
msgid "The following books have already been converted to %s format. Do you wish to reconvert them?"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/bookmarkmanager.py:43
msgid "Edit bookmark"
msgstr ""
@ -6126,11 +6160,11 @@ msgstr ""
msgid "Requested formats not available"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server.py:203
#: /home/kovid/work/calibre/src/calibre/library/server.py:233
msgid "Password to access your calibre library. Username is "
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server.py:526
#: /home/kovid/work/calibre/src/calibre/library/server.py:598
msgid ""
"[options]\n"
"\n"
@ -6308,19 +6342,19 @@ msgstr ""
msgid "Downloading cover from %s"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:930
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:932
msgid "Untitled Article"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1001
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1003
msgid "Article downloaded: %s"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1012
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1014
msgid "Article download failed: %s"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1027
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1029
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_borba.py:80
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_glas_srpske.py:76
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_instapaper.py:59
@ -6509,10 +6543,10 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_stackoverflow.py:18
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_starbulletin.py:19
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_straitstimes.py:22
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_telegraph_uk.py:18
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_telegraph_uk.py:17
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_teleread.py:17
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_the_age.py:19
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_the_budget_fashionista.py:25
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_the_budget_fashionista.py:23
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_the_nation.py:17
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_the_oz.py:16
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_the_register.py:6

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -63,6 +63,8 @@ def shorten_components_to(length, components):
if not r:
r = x.strip()[0] if x.strip() else 'x'
ans.append(r)
if len(os.sep.join(ans)) > length:
return shorten_components_to(length, ans)
return ans
def find_executable_in_path(name, path=None):

View File

@ -16,6 +16,8 @@ if iswindows:
import win32process
_windows_null_file = open(os.devnull, 'wb')
isnewosx = isosx and getattr(sys, 'new_app_bundle', False)
class Worker(object):
'''
Platform independent object for launching child processes. All processes
@ -45,6 +47,9 @@ class Worker(object):
return os.path.join(os.path.dirname(sys.executable),
'calibre-parallel.exe' if isfrozen else \
'Scripts\\calibre-parallel.exe')
if isnewosx:
return os.path.join(sys.console_binaries_path, 'calibre-parallel')
if isosx:
if not isfrozen: return 'calibre-parallel'
contents = os.path.join(self.osx_contents_dir,
@ -56,6 +61,9 @@ class Worker(object):
@property
def gui_executable(self):
if isnewosx:
return os.path.join(sys.binaries_path, 'calibre-parallel')
if isfrozen and isosx:
return os.path.join(self.osx_contents_dir,
'MacOS', self.osx_interpreter)
@ -98,7 +106,7 @@ class Worker(object):
def __init__(self, env, gui=False):
self._env = {}
self.gui = gui
if isosx and isfrozen:
if isosx and isfrozen and not isnewosx:
contents = os.path.join(self.osx_contents_dir, 'console.app', 'Contents')
resources = os.path.join(contents, 'Resources')
fd = os.path.join(contents, 'Frameworks')
@ -133,7 +141,7 @@ class Worker(object):
if priority is None:
priority = prefs['worker_process_priority']
cmd = [exe]
if isosx:
if isosx and not isnewosx:
cmd += ['-c', self.osx_prefix + 'from calibre.utils.ipc.worker import main; main()']
args = {
'env' : env,
@ -155,7 +163,7 @@ class Worker(object):
ret = self._file.name
if iswindows and 'stdin' not in args:
# On windows when usingthepythonw interpreter,
# On windows when using the pythonw interpreter,
# stdout, stderr and stdin may not be valid
args['stdin'] = subprocess.PIPE
args['stdout'] = _windows_null_file

View File

@ -6,14 +6,18 @@ __license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
AUTHTOOL="""#!%s
import sys, os
AUTHTOOL="""#!/usr/bin/python
import os
scripts = %s
links = %s
os.setuid(0)
for s, l in zip(scripts, links):
if os.path.lexists(l):
try:
os.remove(l)
except:
pass
print 'Creating link:', l, '->', s
omask = os.umask(022)
os.symlink(s, l)
@ -23,15 +27,31 @@ for s, l in zip(scripts, links):
DEST_PATH = '/usr/bin'
def create_symlinks():
import os, tempfile, traceback, sys
from Authorization import Authorization, kAuthorizationFlagDestroyRights
return create_symlinks_new() if getattr(sys, 'new_app_bundle', False) else create_symlinks_old()
def create_symlinks_new():
from calibre.resources import scripts
links = [os.path.join(DEST_PATH, i) for i in scripts]
scripts = [os.path.join(
sys.binaries_path if scripts[i] == 'gui' else sys.console_binaries_path, i) for i in scripts]
return do_it(scripts, links)
def create_symlinks_old():
from calibre.resources import scripts
resources_path = os.environ['RESOURCEPATH']
links = [os.path.join(DEST_PATH, i) for i in scripts]
scripts = [os.path.join(resources_path, 'loaders', i) for i in scripts]
return do_it(scripts, links)
def do_it(scripts, links):
import os, tempfile, traceback
from Authorization import Authorization, kAuthorizationFlagDestroyRights
r1, r2 = DEST_PATH, links
bad = False
for s, l in zip(scripts, links):
if os.path.exists(l) and os.path.exists(os.path.realpath(l)):
@ -39,19 +59,28 @@ def create_symlinks():
bad = True
break
if bad:
ph, pp = os.environ.get('PYTHONHOME', None), os.environ.get('PYTHONPATH', None)
auth = Authorization(destroyflags=(kAuthorizationFlagDestroyRights,))
fd, name = tempfile.mkstemp('.py')
os.write(fd, AUTHTOOL % (sys.executable, repr(scripts), repr(links)))
os.write(fd, AUTHTOOL % (repr(scripts), repr(links)))
os.close(fd)
os.chmod(name, 0700)
try:
pipe = auth.executeWithPrivileges(sys.executable, name)
if pp:
del os.environ['PYTHONPATH']
if ph:
del os.environ['PYTHONHOME']
pipe = auth.executeWithPrivileges(name)
sys.stdout.write(pipe.read())
pipe.close()
except:
traceback.print_exc()
r1, r2 = None, traceback.format_exc()
finally:
os.unlink(name)
if pp:
os.environ['PYTHONPATH'] = pp
if ph:
os.environ['PYTHONHOME'] = ph
return DEST_PATH, links
return r1, r2

View File

@ -61,7 +61,9 @@ class Economist(BasicNewsRecipe):
continue
a = tag.find('a', href=True)
if a is not None:
url=a['href'].replace('displaystory', 'PrinterFriendly')
url=a['href'].replace('displaystory', 'PrinterFriendly').strip()
if url.startswith('Printer'):
url = '/'+url
if url.startswith('/'):
url = 'http://www.economist.com' + url
try:

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2008, Darko Miletic <darko.miletic at gmail.com>'
__copyright__ = '2008-2009, Darko Miletic <darko.miletic at gmail.com>'
'''
latimes.com
'''
@ -10,20 +10,56 @@ from calibre.web.feeds.news import BasicNewsRecipe
class LATimes(BasicNewsRecipe):
title = u'The Los Angeles Times'
__author__ = u'Darko Miletic'
__author__ = u'Darko Miletic and Sujata Raman'
description = u'News from Los Angeles'
oldest_article = 7
max_articles_per_feed = 100
language = _('English')
no_stylesheets = True
use_embedded_content = False
encoding = 'utf-8'
lang = 'en-US'
keep_only_tags = [ dict(name='div', attrs={'id':'center' }) ]
remove_tags_after = [ dict(name='div', attrs={'id':'socialnet'}) ]
remove_tags = [
dict(name='div' , attrs={'id':'wrapper_vid' })
,dict(name='div' , attrs={'id':'article_related'})
,dict(name='div' , attrs={'id':'socialnet' })
conversion_options = {
'comment' : description
, 'language' : lang
}
extra_css = '''
h1{font-family :Georgia,"Times New Roman",Times,serif; font-size:large; }
h2{font-family :Georgia,"Times New Roman",Times,serif; font-size:x-small;}
.story{font-family :Georgia,"Times New Roman",Times,serif; font-size: x-small;}
.entry-body{font-family :Georgia,"Times New Roman",Times,serif; font-size: x-small;}
.entry-more{font-family :Georgia,"Times New Roman",Times,serif; font-size: x-small;}
.credit{color:#666666; font-family :Georgia,"Times New Roman",Times,serif; font-size: xx-small;}
.small{color:#666666; font-family :Georgia,"Times New Roman",Times,serif; font-size: xx-small;}
.byline{font-family :Georgia,"Times New Roman",Times,serif; font-size: xx-small;}
.date{font-family :Georgia,"Times New Roman",Times,serif; font-size: xx-small;color:#930000; font-style:italic;}
.time{font-family :Georgia,"Times New Roman",Times,serif; font-size: xx-small;color:#930000; font-style:italic;}
.copyright{font-family :Georgia,"Times New Roman",Times,serif; font-size: xx-small;color:#930000; }
.subhead{font-family :Georgia,"Times New Roman",Times,serif; font-size:x-small;}
'''
keep_only_tags = [dict(name='div', attrs={'class':["story" ,"entry"] })]
remove_tags = [ dict(name='div', attrs={'class':['articlerail',"sphereTools","tools","toppaginate","entry-footer-left","entry-footer-right"]}),
dict(name='div', attrs={'id':["moduleArticleToolsContainer",]}),
dict(name='ul', attrs={'class':["article-nav clearfix",]}),
dict(name='p', attrs={'class':["entry-footer",]}),
dict(name=['iframe'])
]
feeds = [(u'News', u'http://feeds.latimes.com/latimes/news')]
feeds = [(u'News', u'http://feeds.latimes.com/latimes/news')
,(u'Local','http://feeds.latimes.com/latimes/news/local')
,(u'Most Emailed','http://feeds.latimes.com/MostEmailed')
,(u'California Politics','http://feeds.latimes.com/latimes/news/local/politics/cal/')
,('OrangeCounty','http://feeds.latimes.com/latimes/news/local/orange/')
,('National','http://feeds.latimes.com/latimes/news/nationworld/nation')
,('Politics','http://feeds.latimes.com/latimes/news/politics/')
,('Business','http://feeds.latimes.com/latimes/business')
,('Sports','http://feeds.latimes.com/latimes/sports/')
,('Entertainment','http://feeds.latimes.com/latimes/entertainment/')
]
def get_article_url(self, article):
return article.get('feedburner_origlink')

View File

@ -89,6 +89,17 @@ class Newsweek(BasicNewsRecipe):
return cmp(tx, ty)
return sorted(ans, cmp=fcmp)
def ensure_html(self, soup):
root = soup.find(name=True)
if root.name == 'html': return soup
nsoup = BeautifulSoup('<html><head></head><body/></html>')
nroot = nsoup.find(name='body')
for x in soup.contents:
if getattr(x, 'name', False):
x.extract()
nroot.insert(len(nroot), x)
return nsoup
def postprocess_html(self, soup, first_fetch):
if not first_fetch:
h1 = soup.find(id='headline')
@ -99,7 +110,7 @@ class Newsweek(BasicNewsRecipe):
div.extract()
divs = list(soup.findAll('div', 'pagination'))
if not divs:
return soup
return self.ensure_html(soup)
for div in divs[1:]: div.extract()
all_a = divs[0].findAll('a', href=True)
divs[0]['style']="display:none"
@ -109,7 +120,7 @@ class Newsweek(BasicNewsRecipe):
for a in soup.findAll('a', href=test):
if a not in all_a:
del a['href']
return soup
return self.ensure_html(soup)
def get_current_issue(self):
soup = self.index_to_soup('http://www.newsweek.com')

View File

@ -1,5 +1,4 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2008, Darko Miletic <darko.miletic at gmail.com>'
'''
@ -18,11 +17,21 @@ class TelegraphUK(BasicNewsRecipe):
language = _('English')
use_embedded_content = False
extra_css = '''
h1{font-family :Arial,Helvetica,sans-serif; font-size:large; }
h2{font-family :Arial,Helvetica,sans-serif; font-size:x-small; color:#444444}
.story{font-family :Arial,Helvetica,sans-serif; font-size: x-small;}
.byline{color:#666666; font-family :Arial,Helvetica,sans-serif; font-size: xx-small;}
a{color:#234B7B; }
.imageExtras{color:#666666; font-family :Arial,Helvetica,sans-serif; font-size: xx-small;}
'''
keep_only_tags = [
dict(name='div', attrs={'class':'storyHead'})
,dict(name='div', attrs={'class':'story' })
#,dict(name='div', attrs={'class':['slideshowHD gutterUnder',"twoThirds gutter","caption" ] })
]
remove_tags = [dict(name='div', attrs={'class':'slideshow'})]
remove_tags = [dict(name='div', attrs={'class':['related_links_inline',"imgindex","next","prev","gutterUnder"]})]
feeds = [
(u'UK News' , u'http://www.telegraph.co.uk/news/uknews/rss' )
@ -36,3 +45,14 @@ class TelegraphUK(BasicNewsRecipe):
,(u'Comment' , u'http://www.telegraph.co.uk/comment/rss' )
,(u'How about that?', u'http://www.telegraph.co.uk/news/newstopics/howaboutthat/rss' )
]
def get_article_url(self, article):
url = article.get('guid', None)
if 'picture-galleries' in url or 'pictures' in url or 'picturegalleries' in url :
url = None
return url

View File

@ -6,9 +6,7 @@ __copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>'
www.thebudgetfashionista.com
'''
import re
from calibre.web.feeds.recipes import BasicNewsRecipe
from calibre.ebooks.BeautifulSoup import Tag
class TheBudgetFashionista(BasicNewsRecipe):
title = 'The Budget Fashionista'
@ -24,40 +22,22 @@ class TheBudgetFashionista(BasicNewsRecipe):
lang = 'en-US'
language = _('English')
preprocess_regexps = [(re.compile(r"</head>{0,1}", re.DOTALL|re.IGNORECASE),lambda match: '')]
conversion_options = {
'comment' : description
, 'tags' : category
, 'publisher' : publisher
, 'language' : lang
}
html2lrf_options = [
'--comment', description
, '--category', category
, '--publisher', publisher
]
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"'
keep_only_tags = [dict(name='div', attrs={'id':'singlepost'})]
remove_tags_after = dict(name='div', attrs={'id':'postnav'})
remove_tags = [
dict(name=['object','link','script','iframe','form'])
,dict(name='div', attrs={'id':'postnav'})
]
keep_only_tags = [dict(name='div', attrs={'class':'columnLeft'})]
remove_tags_after = dict(name='div', attrs={'class':'postDetails'})
remove_tags = [dict(name=['object','link','script','iframe','form','login-button'])]
feeds = [(u'Articles', u'http://www.thebudgetfashionista.com/feeds/atom/')]
def preprocess_html(self, soup):
for item in soup.findAll(style=True):
del item['style']
return soup
for it in soup.findAll('img'):
if it.parent.name == 'a':
it.parent.name = 'div'
return soup;
def postprocess_html(self, soup, x):
body = soup.find('body')
post = soup.find('div', attrs={'id':'singlepost'})
if post and body:
post.extract()
body.extract()
soup.html.append(body)
body.insert(1,post)
mlang = Tag(soup,'meta',[("http-equiv","Content-Language"),("content",self.lang)])
mcharset = Tag(soup,'meta',[("http-equiv","Content-Type"),("content","text/html; charset=utf-8")])
soup.head.insert(0,mlang)
soup.head.insert(1,mcharset)
return self.adeify_images(soup)