mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Pull from trunk
This commit is contained in:
commit
b8a5d12234
@ -2,7 +2,7 @@
|
||||
<?eclipse-pydev version="1.0"?>
|
||||
|
||||
<pydev_project>
|
||||
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.6</pydev_property>
|
||||
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.5</pydev_property>
|
||||
<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
|
||||
<path>/calibre-pluginize/src</path>
|
||||
</pydev_pathproperty>
|
||||
|
@ -38,6 +38,10 @@ def freeze():
|
||||
'/usr/lib/libxml2.so.2',
|
||||
'/usr/lib/libxslt.so.1',
|
||||
'/usr/lib/libxslt.so.1',
|
||||
'/usr/lib/libgthread-2.0.so.0',
|
||||
'/usr/lib/libglib-2.0.so.0',
|
||||
'/usr/lib/gcc/i686-pc-linux-gnu/4.3.3/libstdc++.so.6',
|
||||
'/usr/lib/libpng12.so.0',
|
||||
'/usr/lib/libexslt.so.0',
|
||||
'/usr/lib/libMagickWand.so',
|
||||
'/usr/lib/libMagickCore.so',
|
||||
|
@ -28,33 +28,12 @@ for icon in ICONS:
|
||||
raise Exception('No icon at '+icon)
|
||||
|
||||
VERSION = re.sub('[a-z]\d+', '', VERSION)
|
||||
WINVER = VERSION+'.0'
|
||||
|
||||
PY2EXE_DIR = os.path.join(BASE_DIR, 'build','py2exe')
|
||||
|
||||
class BuildEXE(py2exe.build_exe.py2exe):
|
||||
manifest_resource_id = 0
|
||||
|
||||
MANIFEST_TEMPLATE = '''
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<assemblyIdentity version="%(version)s"
|
||||
processorArchitecture="x86"
|
||||
name="net.kovidgoyal.%(prog)s"
|
||||
type="win32"
|
||||
/>
|
||||
<description>Ebook management application</description>
|
||||
<!-- Identify the application security requirements. -->
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||
<security>
|
||||
<requestedPrivileges>
|
||||
<requestedExecutionLevel
|
||||
level="asInvoker"
|
||||
uiAccess="false"/>
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
</assembly>
|
||||
'''
|
||||
def run(self):
|
||||
py2exe.build_exe.py2exe.run(self)
|
||||
print 'Adding plugins...'
|
||||
@ -129,38 +108,34 @@ class BuildEXE(py2exe.build_exe.py2exe):
|
||||
shutil.copyfile(f, os.path.join(PY2EXE_DIR, os.path.basename(f)))
|
||||
|
||||
|
||||
@classmethod
|
||||
def manifest(cls, prog):
|
||||
cls.manifest_resource_id += 1
|
||||
return (24, cls.manifest_resource_id,
|
||||
cls.MANIFEST_TEMPLATE % dict(prog=prog, version=(VERSION+'.0')))
|
||||
|
||||
def exe_factory(dest_base, script, icon_resources=None):
|
||||
exe = {
|
||||
'dest_base' : dest_base,
|
||||
'script' : script,
|
||||
'name' : dest_base,
|
||||
'version' : WINVER,
|
||||
'description' : 'calibre - E-book library management',
|
||||
'author' : 'Kovid Goyal',
|
||||
'copyright' : '(c) Kovid Goyal, 2008',
|
||||
'company' : 'kovidgoyal.net',
|
||||
}
|
||||
if icon_resources is not None:
|
||||
exe['icon_resources'] = icon_resources
|
||||
return exe
|
||||
|
||||
def main(args=sys.argv):
|
||||
sys.argv[1:2] = ['py2exe']
|
||||
if os.path.exists(PY2EXE_DIR):
|
||||
shutil.rmtree(PY2EXE_DIR)
|
||||
|
||||
console = [dict(dest_base=basenames['console'][i], script=scripts['console'][i])
|
||||
console = [exe_factory(basenames['console'][i], scripts['console'][i])
|
||||
for i in range(len(scripts['console']))]
|
||||
setup(
|
||||
cmdclass = {'py2exe': BuildEXE},
|
||||
windows = [
|
||||
{'script' : scripts['gui'][0],
|
||||
'dest_base' : APPNAME,
|
||||
'icon_resources' : [(1, ICONS[0])],
|
||||
#'other_resources' : [BuildEXE.manifest(APPNAME)],
|
||||
},
|
||||
{'script' : scripts['gui'][1],
|
||||
'dest_base' : 'lrfviewer',
|
||||
'icon_resources' : [(1, ICONS[1])],
|
||||
#'other_resources' : [BuildEXE.manifest('lrfviewer')],
|
||||
},
|
||||
{'script' : scripts['gui'][2],
|
||||
'dest_base' : 'ebook-viewer',
|
||||
'icon_resources' : [(1, ICONS[1])],
|
||||
#'other_resources' : [BuildEXE.manifest('ebook-viewer')],
|
||||
},
|
||||
exe_factory(APPNAME, scripts['gui'][0], [(1, ICONS[0])]),
|
||||
exe_factory('lrfviewer', scripts['gui'][1], [(1, ICONS[1])]),
|
||||
exe_factory('ebook-viewer', scripts['gui'][2], [(1, ICONS[1])]),
|
||||
],
|
||||
console = console,
|
||||
options = { 'py2exe' : {'compressed': 1,
|
||||
|
372
setup.py
372
setup.py
@ -47,350 +47,15 @@ main_functions = {
|
||||
|
||||
if __name__ == '__main__':
|
||||
from setuptools import setup, find_packages
|
||||
from setuptools.command.build_py import build_py as _build_py, convert_path
|
||||
from distutils.command.build import build as _build
|
||||
from distutils.core import Command as _Command
|
||||
from pyqtdistutils import PyQtExtension, build_ext, Extension
|
||||
import subprocess, glob
|
||||
from upload import sdist, pot, build, build_py, manual, \
|
||||
resources, clean, gui, translations, update, \
|
||||
tag_release, upload_demo, build_linux, build_windows, \
|
||||
build_osx, upload_installers, upload_user_manual, \
|
||||
upload_to_pypi, stage3, stage2, stage1, upload
|
||||
|
||||
def newer(targets, sources):
|
||||
'''
|
||||
Return True is sources is newer that targets or if targets
|
||||
does not exist.
|
||||
'''
|
||||
for f in targets:
|
||||
if not os.path.exists(f):
|
||||
return True
|
||||
ttimes = map(lambda x: os.stat(x).st_mtime, targets)
|
||||
stimes = map(lambda x: os.stat(x).st_mtime, sources)
|
||||
newest_source, oldest_target = max(stimes), min(ttimes)
|
||||
return newest_source > oldest_target
|
||||
|
||||
class build_py(_build_py):
|
||||
|
||||
def find_data_files(self, package, src_dir):
|
||||
"""
|
||||
Return filenames for package's data files in 'src_dir'
|
||||
Modified to treat data file specs as paths not globs
|
||||
"""
|
||||
globs = (self.package_data.get('', [])
|
||||
+ self.package_data.get(package, []))
|
||||
files = self.manifest_files.get(package, [])[:]
|
||||
for pattern in globs:
|
||||
# Each pattern has to be converted to a platform-specific path
|
||||
pattern = os.path.join(src_dir, convert_path(pattern))
|
||||
next = glob.glob(pattern)
|
||||
files.extend(next if next else [pattern])
|
||||
|
||||
return self.exclude_data_files(package, src_dir, files)
|
||||
|
||||
|
||||
class Command(_Command):
|
||||
user_options = []
|
||||
def initialize_options(self): pass
|
||||
def finalize_options(self): pass
|
||||
|
||||
class sdist(Command):
|
||||
|
||||
description = "create a source distribution using bzr"
|
||||
|
||||
def run(self):
|
||||
name = 'dist/calibre-%s.tar.gz'%VERSION
|
||||
subprocess.check_call(('bzr export '+name).split())
|
||||
self.distribution.dist_files.append(('sdist', '', name))
|
||||
|
||||
class pot(Command):
|
||||
description = '''Create the .pot template for all translatable strings'''
|
||||
|
||||
PATH = os.path.join('src', APPNAME, 'translations')
|
||||
|
||||
def source_files(self):
|
||||
ans = []
|
||||
for root, dirs, files in os.walk(os.path.dirname(self.PATH)):
|
||||
for name in files:
|
||||
if name.endswith('.py'):
|
||||
ans.append(os.path.abspath(os.path.join(root, name)))
|
||||
return ans
|
||||
|
||||
|
||||
def run(self):
|
||||
sys.path.insert(0, os.path.abspath(self.PATH))
|
||||
try:
|
||||
from pygettext import main as pygettext
|
||||
files = self.source_files()
|
||||
buf = cStringIO.StringIO()
|
||||
print 'Creating translations template'
|
||||
tempdir = tempfile.mkdtemp()
|
||||
pygettext(buf, ['-k', '__', '-p', tempdir]+files)
|
||||
src = buf.getvalue()
|
||||
pot = os.path.join(tempdir, 'calibre.pot')
|
||||
f = open(pot, 'wb')
|
||||
f.write(src)
|
||||
f.close()
|
||||
print 'Translations template:', pot
|
||||
return pot
|
||||
finally:
|
||||
sys.path.remove(os.path.abspath(self.PATH))
|
||||
|
||||
class manual(Command):
|
||||
description='''Build the User Manual '''
|
||||
|
||||
def run(self):
|
||||
cwd = os.path.abspath(os.getcwd())
|
||||
os.chdir(os.path.join('src', 'calibre', 'manual'))
|
||||
try:
|
||||
for d in ('.build', 'cli'):
|
||||
if os.path.exists(d):
|
||||
shutil.rmtree(d)
|
||||
os.makedirs(d)
|
||||
if not os.path.exists('.build'+os.sep+'html'):
|
||||
os.makedirs('.build'+os.sep+'html')
|
||||
subprocess.check_call(['sphinx-build', '-b', 'custom', '-d',
|
||||
'.build/doctrees', '.', '.build/html'])
|
||||
finally:
|
||||
os.chdir(cwd)
|
||||
|
||||
@classmethod
|
||||
def clean(cls):
|
||||
path = os.path.join('src', 'calibre', 'manual', '.build')
|
||||
if os.path.exists(path):
|
||||
shutil.rmtree(path)
|
||||
|
||||
class resources(Command):
|
||||
description='''Compile various resource files used in calibre. '''
|
||||
|
||||
RESOURCES = dict(
|
||||
opf_template = 'ebooks/metadata/opf.xml',
|
||||
ncx_template = 'ebooks/metadata/ncx.xml',
|
||||
fb2_xsl = 'ebooks/lrf/fb2/fb2.xsl',
|
||||
metadata_sqlite = 'library/metadata_sqlite.sql',
|
||||
jquery = 'gui2/viewer/jquery.js',
|
||||
jquery_scrollTo = 'gui2/viewer/jquery_scrollTo.js',
|
||||
html_css = 'ebooks/oeb/html.css',
|
||||
)
|
||||
|
||||
DEST = os.path.join('src', APPNAME, 'resources.py')
|
||||
|
||||
def get_qt_translations(self):
|
||||
data = {}
|
||||
translations_found = False
|
||||
for TPATH in ('/usr/share/qt4/translations', '/usr/lib/qt4/translations'):
|
||||
if os.path.exists(TPATH):
|
||||
files = glob.glob(TPATH + '/qt_??.qm')
|
||||
for f in files:
|
||||
key = os.path.basename(f).partition('.')[0]
|
||||
data[key] = f
|
||||
translations_found = True
|
||||
break
|
||||
if not translations_found:
|
||||
print 'WARNING: Could not find Qt transations'
|
||||
return data
|
||||
|
||||
def get_static_resources(self):
|
||||
sdir = os.path.join('src', 'calibre', 'library', 'static')
|
||||
resources, max = {}, 0
|
||||
for f in os.listdir(sdir):
|
||||
resources[f] = open(os.path.join(sdir, f), 'rb').read()
|
||||
mtime = os.stat(os.path.join(sdir, f)).st_mtime
|
||||
max = mtime if mtime > max else max
|
||||
return resources, max
|
||||
|
||||
def get_recipes(self):
|
||||
sdir = os.path.join('src', 'calibre', 'web', 'feeds', 'recipes')
|
||||
resources, max = {}, 0
|
||||
for f in os.listdir(sdir):
|
||||
if f.endswith('.py') and f != '__init__.py':
|
||||
resources[f.replace('.py', '')] = open(os.path.join(sdir, f), 'rb').read()
|
||||
mtime = os.stat(os.path.join(sdir, f)).st_mtime
|
||||
max = mtime if mtime > max else max
|
||||
return resources, max
|
||||
|
||||
def run(self):
|
||||
data, dest, RESOURCES = {}, self.DEST, self.RESOURCES
|
||||
for key in RESOURCES:
|
||||
path = RESOURCES[key]
|
||||
if not os.path.isabs(path):
|
||||
RESOURCES[key] = os.path.join('src', APPNAME, path)
|
||||
translations = self.get_qt_translations()
|
||||
RESOURCES.update(translations)
|
||||
static, smax = self.get_static_resources()
|
||||
recipes, rmax = self.get_recipes()
|
||||
amax = max(rmax, smax)
|
||||
if newer([dest], RESOURCES.values()) or os.stat(dest).st_mtime < amax:
|
||||
print 'Compiling resources...'
|
||||
with open(dest, 'wb') as f:
|
||||
for key in RESOURCES:
|
||||
data = open(RESOURCES[key], 'rb').read()
|
||||
f.write(key + ' = ' + repr(data)+'\n\n')
|
||||
f.write('server_resources = %s\n\n'%repr(static))
|
||||
f.write('recipes = %s\n\n'%repr(recipes))
|
||||
f.write('build_time = "%s"\n\n'%time.strftime('%d %m %Y %H%M%S'))
|
||||
else:
|
||||
print 'Resources are up to date'
|
||||
|
||||
@classmethod
|
||||
def clean(cls):
|
||||
path = cls.DEST
|
||||
for path in glob.glob(path+'*'):
|
||||
if os.path.exists(path):
|
||||
os.remove(path)
|
||||
|
||||
class translations(Command):
|
||||
description='''Compile the translations'''
|
||||
PATH = os.path.join('src', APPNAME, 'translations')
|
||||
DEST = os.path.join(PATH, 'compiled.py')
|
||||
|
||||
def run(self):
|
||||
sys.path.insert(0, os.path.abspath(self.PATH))
|
||||
try:
|
||||
files = glob.glob(os.path.join(self.PATH, '*.po'))
|
||||
if newer([self.DEST], files):
|
||||
from msgfmt import main as msgfmt
|
||||
translations = {}
|
||||
print 'Compiling translations...'
|
||||
for po in files:
|
||||
lang = os.path.basename(po).partition('.')[0]
|
||||
buf = cStringIO.StringIO()
|
||||
print 'Compiling', lang
|
||||
msgfmt(buf, [po])
|
||||
translations[lang] = buf.getvalue()
|
||||
open(self.DEST, 'wb').write('translations = '+repr(translations))
|
||||
else:
|
||||
print 'Translations up to date'
|
||||
finally:
|
||||
sys.path.remove(os.path.abspath(self.PATH))
|
||||
|
||||
|
||||
@classmethod
|
||||
def clean(cls):
|
||||
path = cls.DEST
|
||||
if os.path.exists(path):
|
||||
os.remove(path)
|
||||
|
||||
|
||||
class gui(Command):
|
||||
description='''Compile all GUI forms and images'''
|
||||
PATH = os.path.join('src', APPNAME, 'gui2')
|
||||
IMAGES_DEST = os.path.join(PATH, 'images_rc.py')
|
||||
QRC = os.path.join(PATH, 'images.qrc')
|
||||
|
||||
@classmethod
|
||||
def find_forms(cls):
|
||||
forms = []
|
||||
for root, dirs, files in os.walk(cls.PATH):
|
||||
for name in files:
|
||||
if name.endswith('.ui'):
|
||||
forms.append(os.path.abspath(os.path.join(root, name)))
|
||||
|
||||
return forms
|
||||
|
||||
@classmethod
|
||||
def form_to_compiled_form(cls, form):
|
||||
return form.rpartition('.')[0]+'_ui.py'
|
||||
|
||||
def run(self):
|
||||
self.build_forms()
|
||||
self.build_images()
|
||||
|
||||
def build_images(self):
|
||||
cwd, images = os.getcwd(), os.path.basename(self.IMAGES_DEST)
|
||||
try:
|
||||
os.chdir(self.PATH)
|
||||
sources, files = [], []
|
||||
for root, dirs, files in os.walk('images'):
|
||||
for name in files:
|
||||
sources.append(os.path.join(root, name))
|
||||
if newer([images], sources):
|
||||
print 'Compiling images...'
|
||||
for s in sources:
|
||||
alias = ' alias="library"' if s.endswith('images'+os.sep+'library.png') else ''
|
||||
files.append('<file%s>%s</file>'%(alias, s))
|
||||
manifest = '<RCC>\n<qresource prefix="/">\n%s\n</qresource>\n</RCC>'%'\n'.join(files)
|
||||
with open('images.qrc', 'wb') as f:
|
||||
f.write(manifest)
|
||||
subprocess.check_call(['pyrcc4', '-o', images, 'images.qrc'])
|
||||
else:
|
||||
print 'Images are up to date'
|
||||
finally:
|
||||
os.chdir(cwd)
|
||||
|
||||
|
||||
def build_forms(self):
|
||||
from PyQt4.uic import compileUi
|
||||
forms = self.find_forms()
|
||||
for form in forms:
|
||||
compiled_form = self.form_to_compiled_form(form)
|
||||
if not os.path.exists(compiled_form) or os.stat(form).st_mtime > os.stat(compiled_form).st_mtime:
|
||||
print 'Compiling form', form
|
||||
buf = cStringIO.StringIO()
|
||||
compileUi(form, buf)
|
||||
dat = buf.getvalue()
|
||||
dat = dat.replace('__appname__', APPNAME)
|
||||
dat = dat.replace('import images_rc', 'from calibre.gui2 import images_rc')
|
||||
dat = dat.replace('from library import', 'from calibre.gui2.library import')
|
||||
dat = dat.replace('from widgets import', 'from calibre.gui2.widgets import')
|
||||
dat = re.compile(r'QtGui.QApplication.translate\(.+?,\s+"(.+?)(?<!\\)",.+?\)', re.DOTALL).sub(r'_("\1")', dat)
|
||||
|
||||
# Workaround bug in Qt 4.4 on Windows
|
||||
if form.endswith('dialogs%sconfig.ui'%os.sep) or form.endswith('dialogs%slrf_single.ui'%os.sep):
|
||||
print 'Implementing Workaround for buggy pyuic in form', form
|
||||
dat = re.sub(r'= QtGui\.QTextEdit\(self\..*?\)', '= QtGui.QTextEdit()', dat)
|
||||
dat = re.sub(r'= QtGui\.QListWidget\(self\..*?\)', '= QtGui.QListWidget()', dat)
|
||||
|
||||
if form.endswith('viewer%smain.ui'%os.sep):
|
||||
print 'Promoting WebView'
|
||||
dat = dat.replace('self.view = QtWebKit.QWebView(', 'self.view = DocumentView(')
|
||||
dat += '\n\nfrom calibre.gui2.viewer.documentview import DocumentView'
|
||||
|
||||
open(compiled_form, 'wb').write(dat)
|
||||
|
||||
|
||||
@classmethod
|
||||
def clean(cls):
|
||||
forms = cls.find_forms()
|
||||
for form in forms:
|
||||
c = cls.form_to_compiled_form(form)
|
||||
if os.path.exists(c):
|
||||
os.remove(c)
|
||||
for x in (cls.IMAGES_DEST, cls.QRC):
|
||||
if os.path.exists(x):
|
||||
os.remove(x)
|
||||
|
||||
class clean(Command):
|
||||
description='''Delete all computer generated files in the source tree'''
|
||||
|
||||
def run(self):
|
||||
print 'Cleaning...'
|
||||
manual.clean()
|
||||
gui.clean()
|
||||
translations.clean()
|
||||
resources.clean()
|
||||
|
||||
for f in glob.glob(os.path.join('src', 'calibre', 'plugins', '*')):
|
||||
os.remove(f)
|
||||
for root, dirs, files in os.walk('.'):
|
||||
for name in files:
|
||||
for t in ('.pyc', '.pyo', '~'):
|
||||
if name.endswith(t):
|
||||
os.remove(os.path.join(root, name))
|
||||
break
|
||||
|
||||
for dir in ('build', 'dist', os.path.join('src', 'calibre.egg-info')):
|
||||
shutil.rmtree(dir, ignore_errors=True)
|
||||
|
||||
class build(_build):
|
||||
|
||||
sub_commands = [
|
||||
('resources', lambda self : 'CALIBRE_BUILDBOT' not in os.environ.keys()),
|
||||
('translations', lambda self : 'CALIBRE_BUILDBOT' not in os.environ.keys()),
|
||||
('gui', lambda self : 'CALIBRE_BUILDBOT' not in os.environ.keys()),
|
||||
('build_ext', lambda self: True),
|
||||
('build_py', lambda self: True),
|
||||
('build_clib', _build.has_c_libraries),
|
||||
('build_scripts', _build.has_scripts),
|
||||
]
|
||||
|
||||
entry_points['console_scripts'].append('calibre_postinstall = calibre.linux:post_install')
|
||||
entry_points['console_scripts'].append(
|
||||
'calibre_postinstall = calibre.linux:post_install')
|
||||
ext_modules = [
|
||||
Extension('calibre.plugins.lzx',
|
||||
sources=['src/calibre/utils/lzx/lzxmodule.c',
|
||||
@ -430,7 +95,9 @@ if __name__ == '__main__':
|
||||
plugins = ['plugins/%s.so'%(x.name.rpartition('.')[-1]) for x in ext_modules]
|
||||
else:
|
||||
plugins = ['plugins/%s.pyd'%(x.name.rpartition('.')[-1]) for x in ext_modules] + \
|
||||
['plugins/%s.pyd.manifest'%(x.name.rpartition('.')[-1]) for x in ext_modules if 'pictureflow' not in x.name]
|
||||
['plugins/%s.pyd.manifest'%(x.name.rpartition('.')[-1]) \
|
||||
for x in ext_modules if 'pictureflow' not in x.name]
|
||||
|
||||
|
||||
setup(
|
||||
name = APPNAME,
|
||||
@ -451,7 +118,11 @@ if __name__ == '__main__':
|
||||
''',
|
||||
long_description =
|
||||
'''
|
||||
%s is an e-book library manager. It can view, convert and catalog e-books in most of the major e-book formats. It can also talk to a few e-book reader devices. It can go out to the internet and fetch metadata for your books. It can download newspapers and convert them into e-books for convenient reading. It is cross platform, running on Linux, Windows and OS X.
|
||||
%s is an e-book library manager. It can view, convert and catalog e-books \
|
||||
in most of the major e-book formats. It can also talk to e-book reader \
|
||||
devices. It can go out to the internet and fetch metadata for your books. \
|
||||
It can download newspapers and convert them into e-books for convenient \
|
||||
reading. It is cross platform, running on Linux, Windows and OS X.
|
||||
|
||||
For screenshots: https://%s.kovidgoyal.net/wiki/Screenshots
|
||||
|
||||
@ -490,6 +161,19 @@ if __name__ == '__main__':
|
||||
'gui' : gui,
|
||||
'clean' : clean,
|
||||
'sdist' : sdist,
|
||||
'update' : update,
|
||||
'tag_release' : tag_release,
|
||||
'upload_demo' : upload_demo,
|
||||
'build_linux' : build_linux,
|
||||
'build_windows' : build_windows,
|
||||
'build_osx' : build_osx,
|
||||
'upload_installers': upload_installers,
|
||||
'upload_user_manual': upload_user_manual,
|
||||
'upload_to_pypi': upload_to_pypi,
|
||||
'stage3' : stage3,
|
||||
'stage2' : stage2,
|
||||
'stage1' : stage1,
|
||||
'publish' : upload,
|
||||
},
|
||||
)
|
||||
|
||||
|
@ -29,7 +29,8 @@ mimetypes.add_type('application/adobe-page-template+xml', '.xpgt')
|
||||
mimetypes.add_type('application/x-font-opentype', '.otf')
|
||||
mimetypes.add_type('application/x-font-truetype', '.ttf')
|
||||
mimetypes.add_type('application/oebps-package+xml', '.opf')
|
||||
|
||||
import cssutils
|
||||
cssutils.log.setLevel(logging.WARN)
|
||||
|
||||
def to_unicode(raw, encoding='utf-8', errors='strict'):
|
||||
if isinstance(raw, unicode):
|
||||
@ -71,7 +72,7 @@ def sanitize_file_name(name, substitute='_', as_unicode=False):
|
||||
one = re.sub(r'^\.+$', '_', one)
|
||||
if as_unicode:
|
||||
one = one.decode(filesystem_encoding)
|
||||
return one
|
||||
return one.replace('..', '_')
|
||||
|
||||
|
||||
def prints(*args, **kwargs):
|
||||
@ -401,8 +402,10 @@ def walk(dir):
|
||||
for f in record[-1]:
|
||||
yield os.path.join(record[0], f)
|
||||
|
||||
def strftime(fmt, t=time.localtime()):
|
||||
def strftime(fmt, t=None):
|
||||
''' A version of strtime that returns unicode strings. '''
|
||||
if t is None:
|
||||
t = time.localtime()
|
||||
if iswindows:
|
||||
if isinstance(fmt, unicode):
|
||||
fmt = fmt.encode('mbcs')
|
||||
|
@ -2,7 +2,7 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
__appname__ = 'calibre'
|
||||
__version__ = '0.4.136'
|
||||
__version__ = '0.4.138'
|
||||
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
||||
'''
|
||||
Various run time constants.
|
||||
|
@ -132,7 +132,7 @@ class HTMLMetadataReader(MetadataReaderPlugin):
|
||||
class MOBIMetadataReader(MetadataReaderPlugin):
|
||||
|
||||
name = 'Read MOBI metadata'
|
||||
file_types = set(['mobi', 'prc'])
|
||||
file_types = set(['mobi', 'prc', '.azw'])
|
||||
description = _('Read metadata from %s files')%'MOBI'
|
||||
|
||||
def get_metadata(self, stream, ftype):
|
||||
|
@ -186,7 +186,10 @@ class BookList(_BookList):
|
||||
node = self.document.createElement(self.prefix + "text")
|
||||
mime = MIME_MAP[name.rpartition('.')[-1].lower()]
|
||||
cid = self.max_id()+1
|
||||
sourceid = str(self[0].sourceid) if len(self) else "1"
|
||||
try:
|
||||
sourceid = str(self[0].sourceid) if len(self) else '1'
|
||||
except:
|
||||
sourceid = '1'
|
||||
attrs = {
|
||||
"title" : info["title"],
|
||||
'titleSorter' : sortable_title(info['title']),
|
||||
|
@ -32,7 +32,7 @@ class PRS505(Device):
|
||||
BCD = [0x229] #: Needed to disambiguate 505 and 700 on linux
|
||||
PRODUCT_NAME = 'PRS-505'
|
||||
VENDOR_NAME = 'SONY'
|
||||
FORMATS = ['lrf', 'epub', 'lrx', 'rtf', 'pdf', 'txt']
|
||||
FORMATS = ['epub', 'lrf', 'lrx', 'rtf', 'pdf', 'txt']
|
||||
|
||||
MEDIA_XML = 'database/cache/media.xml'
|
||||
CACHE_XML = 'Sony Reader/database/cache.xml'
|
||||
|
@ -124,6 +124,7 @@ MAP = {
|
||||
'lit' : lit2opf,
|
||||
'mobi' : mobi2opf,
|
||||
'prc' : mobi2opf,
|
||||
'azw' : mobi2opf,
|
||||
'fb2' : fb22opf,
|
||||
'rtf' : rtf2opf,
|
||||
'txt' : txt2opf,
|
||||
@ -131,7 +132,8 @@ MAP = {
|
||||
'epub' : epub2opf,
|
||||
'odt' : odt2epub,
|
||||
}
|
||||
SOURCE_FORMATS = ['lit', 'mobi', 'prc', 'fb2', 'odt', 'rtf', 'txt', 'pdf', 'rar', 'zip', 'oebzip', 'htm', 'html', 'epub']
|
||||
SOURCE_FORMATS = ['lit', 'mobi', 'prc', 'azw', 'fb2', 'odt', 'rtf',
|
||||
'txt', 'pdf', 'rar', 'zip', 'oebzip', 'htm', 'html', 'epub']
|
||||
|
||||
def unarchive(path, tdir):
|
||||
extract(path, tdir)
|
||||
|
@ -310,6 +310,8 @@ def create_cover_image(src, dest, screen_size, rescale_cover=True):
|
||||
def process_title_page(mi, filelist, htmlfilemap, opts, tdir):
|
||||
old_title_page = None
|
||||
f = lambda x : os.path.normcase(os.path.normpath(x))
|
||||
if not isinstance(mi.cover, basestring):
|
||||
mi.cover = None
|
||||
if mi.cover:
|
||||
if f(filelist[0].path) == f(mi.cover):
|
||||
old_title_page = htmlfilemap[filelist[0].path]
|
||||
|
@ -332,6 +332,8 @@ class PreProcessor(object):
|
||||
(re.compile(r'&(\S+?);'), convert_entities),
|
||||
# Remove the <![if/endif tags inserted by everybody's darling, MS Word
|
||||
(re.compile(r'(?i)<{0,1}!\[(end){0,1}if[^>]*>'), lambda match: ''),
|
||||
# Strip all comments since Adobe DE is petrified of them
|
||||
(re.compile(r'<!--[^>]*>'), lambda match : ''),
|
||||
]
|
||||
|
||||
# Fix pdftohtml markup
|
||||
@ -491,9 +493,25 @@ class Parser(PreProcessor, LoggingInterface):
|
||||
self.root.insert(0, head)
|
||||
|
||||
self.head = head
|
||||
try:
|
||||
self.body = self.root.body
|
||||
except:
|
||||
import traceback
|
||||
err = traceback.format_exc()
|
||||
self.root = fromstring(u'<html><head/><body><p>This page was too '
|
||||
'severely malformed for calibre to handle. '
|
||||
'It has been replaced by this error message.'
|
||||
'</p><pre>%s</pre></body></html>'%err)
|
||||
self.head = self.root.xpath('./head')[0]
|
||||
self.body = self.root.body
|
||||
invalid_counter = 0
|
||||
for a in self.root.xpath('//a[@name]'):
|
||||
try:
|
||||
a.set('id', a.get('name'))
|
||||
except:
|
||||
invalid_counter += 1
|
||||
for x in ('id', 'name'):
|
||||
a.set(x, 'calibre_invalid_id_%d'%invalid_counter)
|
||||
if not self.head.xpath('./title'):
|
||||
title = etree.SubElement(self.head, 'title')
|
||||
title.text = _('Unknown')
|
||||
|
@ -780,7 +780,7 @@ class LitReader(object):
|
||||
if u != 0:
|
||||
raise LitError("Reset table entry greater than 32 bits")
|
||||
if size >= len(content):
|
||||
raise LitError("Reset table entry out of bounds")
|
||||
self._warn("LZX reset table entry out of bounds")
|
||||
if bytes_remaining >= window_bytes:
|
||||
lzx.reset()
|
||||
try:
|
||||
|
@ -29,6 +29,7 @@ preferred_source_formats = [
|
||||
'XHTM',
|
||||
'XHTML',
|
||||
'PRC',
|
||||
'AZW',
|
||||
'RTF',
|
||||
'PDF',
|
||||
'TXT',
|
||||
|
@ -154,7 +154,7 @@ def process_file(path, options, logger=None):
|
||||
convertor = txt2lrf
|
||||
elif 'epub' == ext:
|
||||
convertor = epub2lrf
|
||||
elif ext in ['mobi', 'prc']:
|
||||
elif ext in ['mobi', 'prc', 'azw']:
|
||||
convertor = mobi2lrf
|
||||
elif ext == 'fb2':
|
||||
convertor = fb22lrf
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,6 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?python
|
||||
from uuid import uuid4
|
||||
import re
|
||||
?>
|
||||
<ncx version="2005-1"
|
||||
xml:lang="en"
|
||||
@ -19,7 +20,7 @@ from uuid import uuid4
|
||||
<py:def function="navpoint(np, level)">
|
||||
${'%*s'%(4*level,'')}<navPoint id="${str(uuid4())}" playOrder="${str(np.play_order)}">
|
||||
${'%*s'%(4*level,'')}<navLabel>
|
||||
${'%*s'%(4*level,'')}<text>${np.text}</text>
|
||||
${'%*s'%(4*level,'')}<text>${re.sub(r'\s+', ' ', np.text)}</text>
|
||||
${'%*s'%(4*level,'')}</navLabel>
|
||||
${'%*s'%(4*level,'')}<content src="${unicode(np.href)+(('#' + unicode(np.fragment)) if np.fragment else '')}" />
|
||||
<py:for each="np2 in np">${navpoint(np2, level+1)}</py:for>
|
||||
|
@ -186,6 +186,8 @@ class MobiReader(object):
|
||||
self.processed_html = self.processed_html.decode(self.book_header.codec, 'ignore')
|
||||
for pat in ENCODING_PATS:
|
||||
self.processed_html = pat.sub('', self.processed_html)
|
||||
self.processed_html = re.sub(r'&(\S+?);', entity_to_unicode,
|
||||
self.processed_html)
|
||||
self.extract_images(processed_records, output_dir)
|
||||
self.replace_page_breaks()
|
||||
self.cleanup_html()
|
||||
@ -247,9 +249,6 @@ class MobiReader(object):
|
||||
self.processed_html = '<html><p>'+self.processed_html.replace('\n\n', '<p>')+'</html>'
|
||||
self.processed_html = self.processed_html.replace('\r\n', '\n')
|
||||
self.processed_html = self.processed_html.replace('> <', '>\n<')
|
||||
for t, c in [('b', 'bold'), ('i', 'italic')]:
|
||||
self.processed_html = re.sub(r'(?i)<%s>'%t, r'<span class="%s">'%c, self.processed_html)
|
||||
self.processed_html = re.sub(r'(?i)</%s>'%t, r'</span>', self.processed_html)
|
||||
|
||||
def upshift_markup(self, root):
|
||||
if self.verbose:
|
||||
@ -290,35 +289,44 @@ class MobiReader(object):
|
||||
align = attrib.pop('align').strip()
|
||||
if align:
|
||||
styles.append('text-align: %s' % align)
|
||||
if mobi_version == 1 and tag.tag == 'hr':
|
||||
if tag.tag == 'hr':
|
||||
if mobi_version == 1:
|
||||
tag.tag = 'div'
|
||||
styles.append('page-break-before: always')
|
||||
styles.append('display: block')
|
||||
styles.append('margin: 0')
|
||||
if styles:
|
||||
attrib['style'] = '; '.join(styles)
|
||||
|
||||
if tag.tag.lower() == 'font':
|
||||
elif tag.tag == 'i':
|
||||
tag.tag = 'span'
|
||||
tag.attrib['class'] = 'italic'
|
||||
elif tag.tag == 'b':
|
||||
tag.tag = 'span'
|
||||
tag.attrib['class'] = 'bold'
|
||||
elif tag.tag == 'font':
|
||||
sz = tag.get('size', '').lower()
|
||||
try:
|
||||
float(sz)
|
||||
except ValueError:
|
||||
if sz in size_map.keys():
|
||||
attrib['size'] = size_map[sz]
|
||||
elif tag.tag == 'img':
|
||||
recindex = None
|
||||
for attr in self.IMAGE_ATTRS:
|
||||
recindex = attrib.pop(attr, None) or recindex
|
||||
if recindex is not None:
|
||||
attrib['src'] = 'images/%s.jpg' % recindex
|
||||
elif tag.tag == 'pre':
|
||||
if not tag.text:
|
||||
tag.tag = 'div'
|
||||
if styles:
|
||||
attrib['style'] = '; '.join(styles)
|
||||
if 'filepos-id' in attrib:
|
||||
attrib['id'] = attrib.pop('filepos-id')
|
||||
if 'filepos' in attrib:
|
||||
filepos = attrib.pop('filepos')
|
||||
try:
|
||||
attrib['href'] = "#filepos%d" % int(filepos)
|
||||
except:
|
||||
attrib['href'] = filepos
|
||||
if tag.tag == 'img':
|
||||
recindex = None
|
||||
for attr in self.IMAGE_ATTRS:
|
||||
recindex = attrib.pop(attr, None) or recindex
|
||||
if recindex is not None:
|
||||
attrib['src'] = 'images/%s.jpg' % recindex
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
def create_opf(self, htmlfile, guide=None):
|
||||
mi = self.book_header.exth.mi
|
||||
@ -328,7 +336,7 @@ class MobiReader(object):
|
||||
manifest = [(htmlfile, 'text/x-oeb1-document')]
|
||||
bp = os.path.dirname(htmlfile)
|
||||
for i in getattr(self, 'image_names', []):
|
||||
manifest.append((os.path.join(bp, 'images/', i), 'image/jpg'))
|
||||
manifest.append((os.path.join(bp, 'images/', i), 'image/jpeg'))
|
||||
|
||||
opf.create_manifest(manifest)
|
||||
opf.create_spine([os.path.basename(htmlfile)])
|
||||
@ -452,6 +460,7 @@ class MobiReader(object):
|
||||
pos = end
|
||||
self.processed_html += self.mobi_html[pos:]
|
||||
|
||||
|
||||
def extract_images(self, processed_records, output_dir):
|
||||
if self.verbose:
|
||||
print 'Extracting images...'
|
||||
|
@ -416,7 +416,11 @@ class MobiWriter(object):
|
||||
coverid = metadata.cover[0] if metadata.cover else None
|
||||
for _, href in images:
|
||||
item = self._oeb.manifest.hrefs[href]
|
||||
try:
|
||||
data = rescale_image(item.data, self._imagemax)
|
||||
except IOError:
|
||||
self._oeb.logger.warn('Bad image file %r' % item.href)
|
||||
continue
|
||||
self._records.append(data)
|
||||
|
||||
def _generate_record0(self):
|
||||
@ -486,9 +490,11 @@ class MobiWriter(object):
|
||||
index = self._images[href] - 1
|
||||
exth.write(pack('>III', 0xc9, 0x0c, index))
|
||||
exth.write(pack('>III', 0xcb, 0x0c, 0))
|
||||
index = self._add_thumbnail(item) - 1
|
||||
exth.write(pack('>III', 0xca, 0x0c, index))
|
||||
nrecs += 3
|
||||
nrecs += 2
|
||||
index = self._add_thumbnail(item)
|
||||
if index is not None:
|
||||
exth.write(pack('>III', 0xca, 0x0c, index - 1))
|
||||
nrecs += 1
|
||||
exth = exth.getvalue()
|
||||
trail = len(exth) % 4
|
||||
pad = '\0' * (4 - trail) # Always pad w/ at least 1 byte
|
||||
@ -496,7 +502,11 @@ class MobiWriter(object):
|
||||
return ''.join(exth)
|
||||
|
||||
def _add_thumbnail(self, item):
|
||||
try:
|
||||
data = rescale_image(item.data, MAX_THUMB_SIZE, MAX_THUMB_DIMEN)
|
||||
except IOError:
|
||||
self._oeb.logger.warn('Bad image file %r' % item.href)
|
||||
return None
|
||||
manifest = self._oeb.manifest
|
||||
id, href = manifest.generate('thumbnail', 'thumbnail.jpeg')
|
||||
manifest.add(id, href, 'image/jpeg', data=data)
|
||||
|
@ -8,24 +8,20 @@ from __future__ import with_statement
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Marshall T. Vandegrift <llasram@gmail.com>'
|
||||
|
||||
import sys
|
||||
import os
|
||||
import locale
|
||||
import codecs
|
||||
import itertools
|
||||
import types
|
||||
import re
|
||||
import copy
|
||||
from itertools import izip
|
||||
from weakref import WeakKeyDictionary
|
||||
from xml.dom import SyntaxErr as CSSSyntaxError
|
||||
import cssutils
|
||||
from cssutils.css import CSSStyleRule, CSSPageRule, CSSStyleDeclaration, \
|
||||
CSSValueList, cssproperties
|
||||
from cssutils.profiles import profiles as cssprofiles
|
||||
from lxml import etree
|
||||
from lxml.cssselect import css_to_xpath, ExpressionError
|
||||
from calibre.ebooks.oeb.base import XHTML, XHTML_NS, CSS_MIME, OEB_STYLES
|
||||
from calibre.ebooks.oeb.base import XPNSMAP, xpath, barename, urlnormalize
|
||||
from calibre.ebooks.oeb.base import XPNSMAP, xpath, urlnormalize
|
||||
from calibre.ebooks.oeb.profile import PROFILES
|
||||
from calibre.resources import html_css
|
||||
|
||||
@ -163,7 +159,7 @@ class Stylizer(object):
|
||||
for _, _, cssdict, text, _ in rules:
|
||||
try:
|
||||
selector = CSSSelector(text)
|
||||
except ExpressionError, e:
|
||||
except ExpressionError:
|
||||
continue
|
||||
for elem in selector(tree):
|
||||
self.style(elem)._update_cssdict(cssdict)
|
||||
@ -246,7 +242,7 @@ class Stylizer(object):
|
||||
primitives.reverse()
|
||||
value = primitives.pop()
|
||||
for key in composition:
|
||||
if cssproperties.cssvalues[key](value):
|
||||
if cssprofiles.validate(key, value):
|
||||
style[key] = value
|
||||
if not primitives: break
|
||||
value = primitives.pop()
|
||||
|
@ -6,13 +6,9 @@ from __future__ import with_statement
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Marshall T. Vandegrift <llasram@gmail.com>'
|
||||
|
||||
import sys
|
||||
import os
|
||||
from itertools import chain
|
||||
from urlparse import urldefrag
|
||||
from lxml import etree
|
||||
import cssutils
|
||||
from calibre.ebooks.oeb.base import XPNSMAP, CSS_MIME, OEB_DOCS
|
||||
from calibre.ebooks.oeb.base import CSS_MIME, OEB_DOCS
|
||||
from calibre.ebooks.oeb.base import LINK_SELECTORS, CSSURL_RE
|
||||
from calibre.ebooks.oeb.base import urlnormalize
|
||||
|
||||
|
@ -113,6 +113,10 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
|
||||
def set_cover(self):
|
||||
row = self.formats.currentRow()
|
||||
fmt = self.formats.item(row)
|
||||
if fmt is None:
|
||||
error_dialog(self, _('No format selected'),
|
||||
_('No format selected')).exec_()
|
||||
return
|
||||
ext = fmt.ext.lower()
|
||||
if fmt.path is None:
|
||||
stream = self.db.format(self.row, ext, as_file=True)
|
||||
@ -265,7 +269,7 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
|
||||
def cover_dropped(self):
|
||||
self.cover_changed = True
|
||||
|
||||
def initialize_series_and_publisher(self):
|
||||
def initialize_series(self):
|
||||
all_series = self.db.all_series()
|
||||
all_series.sort(cmp=lambda x, y : cmp(x[1], y[1]))
|
||||
series_id = self.db.series_id(self.row)
|
||||
@ -289,6 +293,8 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
|
||||
l.invalidate()
|
||||
l.activate()
|
||||
|
||||
def initialize_series_and_publisher(self):
|
||||
self.initialize_series()
|
||||
all_publishers = self.db.all_publishers()
|
||||
all_publishers.sort(cmp=lambda x, y : cmp(x[1], y[1]))
|
||||
publisher_id = self.db.publisher_id(self.row)
|
||||
|
BIN
src/calibre/gui2/images/news/chicago_breaking_news.png
Normal file
BIN
src/calibre/gui2/images/news/chicago_breaking_news.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 704 B |
BIN
src/calibre/gui2/images/news/linuxdevices.png
Normal file
BIN
src/calibre/gui2/images/news/linuxdevices.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 535 B |
BIN
src/calibre/gui2/images/news/pressonline.png
Normal file
BIN
src/calibre/gui2/images/news/pressonline.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
@ -216,12 +216,9 @@ class BooksModel(QAbstractTableModel):
|
||||
|
||||
|
||||
def delete_books(self, indices):
|
||||
ids = [ self.id(i) for i in indices ]
|
||||
ids = map(self.id, indices)
|
||||
for id in ids:
|
||||
row = self.db.index(id)
|
||||
self.beginRemoveRows(QModelIndex(), row, row)
|
||||
self.db.delete_book(id)
|
||||
self.endRemoveRows()
|
||||
self.db.delete_book(id, notify=False)
|
||||
self.count_changed()
|
||||
self.clear_caches()
|
||||
self.reset()
|
||||
@ -414,8 +411,11 @@ class BooksModel(QAbstractTableModel):
|
||||
|
||||
|
||||
|
||||
def get_preferred_formats(self, rows, formats, paths=False, set_metadata=False):
|
||||
def get_preferred_formats(self, rows, formats, paths=False,
|
||||
set_metadata=False, specific_format=None):
|
||||
ans = []
|
||||
if specific_format is not None:
|
||||
formats = [specific_format.lower()]
|
||||
for row in (row.row() for row in rows):
|
||||
format = None
|
||||
fmts = self.db.formats(row)
|
||||
|
@ -156,6 +156,9 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
sm.addAction(QIcon(':/images/sd.svg'), _('Send to storage card'))
|
||||
sm.addAction(QIcon(':/images/reader.svg'), _('Send to main memory')+' '+_('and delete from library'))
|
||||
sm.addAction(QIcon(':/images/sd.svg'), _('Send to storage card')+' '+_('and delete from library'))
|
||||
sm.addAction(self.action_send_specific_format_to_device)
|
||||
self.connect(self.action_send_specific_format_to_device,
|
||||
SIGNAL('triggered()'), self.send_specific_format_to_device)
|
||||
sm.addSeparator()
|
||||
sm.addAction(_('Send to storage card by default'))
|
||||
sm.actions()[-1].setCheckable(True)
|
||||
@ -330,11 +333,16 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
self.cover_flow.setVisible(False)
|
||||
if not config['separate_cover_flow']:
|
||||
self.library.layout().addWidget(self.cover_flow)
|
||||
self.connect(self.cover_flow, SIGNAL('currentChanged(int)'), self.sync_cf_to_listview)
|
||||
self.connect(self.cover_flow, SIGNAL('itemActivated(int)'), self.show_book_info)
|
||||
self.connect(self.status_bar.cover_flow_button, SIGNAL('toggled(bool)'), self.toggle_cover_flow)
|
||||
self.connect(self.cover_flow, SIGNAL('stop()'), self.status_bar.cover_flow_button.toggle)
|
||||
QObject.connect(self.library_view.selectionModel(), SIGNAL('currentRowChanged(QModelIndex, QModelIndex)'),
|
||||
self.connect(self.cover_flow, SIGNAL('currentChanged(int)'),
|
||||
self.sync_cf_to_listview)
|
||||
self.connect(self.cover_flow, SIGNAL('itemActivated(int)'),
|
||||
self.show_book_info)
|
||||
self.connect(self.status_bar.cover_flow_button,
|
||||
SIGNAL('toggled(bool)'), self.toggle_cover_flow)
|
||||
self.connect(self.cover_flow, SIGNAL('stop()'),
|
||||
self.status_bar.cover_flow_button.toggle)
|
||||
QObject.connect(self.library_view.selectionModel(),
|
||||
SIGNAL('currentRowChanged(QModelIndex, QModelIndex)'),
|
||||
self.sync_cf_to_listview)
|
||||
self.db_images = DatabaseImages(self.library_view.model())
|
||||
self.cover_flow.setImages(self.db_images)
|
||||
@ -482,7 +490,8 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
if not hasattr(index, 'row') and self.library_view.currentIndex().row() != index:
|
||||
index = self.library_view.model().index(index, 0)
|
||||
self.library_view.setCurrentIndex(index)
|
||||
if hasattr(index, 'row') and self.cover_flow.isVisible() and self.cover_flow.currentSlide() != index.row():
|
||||
if hasattr(index, 'row') and self.cover_flow.isVisible() and \
|
||||
self.cover_flow.currentSlide() != index.row():
|
||||
self.cover_flow.setCurrentSlide(index.row())
|
||||
|
||||
def another_instance_wants_to_talk(self, msg):
|
||||
@ -709,6 +718,7 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
t.process_duplicates()
|
||||
if t.number_of_books_added > 0:
|
||||
self.library_view.model().books_added(t.number_of_books_added)
|
||||
self.db_images.reset()
|
||||
|
||||
def upload_books(self, files, names, metadata, on_card=False, memory=None):
|
||||
'''
|
||||
@ -886,8 +896,16 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
self.upload_books(files, names, metadata, on_card=on_card, memory=[[f.name for f in files], remove])
|
||||
self.status_bar.showMessage(_('Sending news to device.'), 5000)
|
||||
|
||||
def send_specific_format_to_device(self):
|
||||
d = ChooseFormatDialog(self, _('Choose format to send to device'),
|
||||
self.device_manager.device_class.FORMATS)
|
||||
d.exec_()
|
||||
fmt = d.format().lower()
|
||||
on_card = config['send_to_storage_card_by_default']
|
||||
self.sync_to_device(on_card, False, specific_format=fmt)
|
||||
|
||||
def sync_to_device(self, on_card, delete_from_library):
|
||||
|
||||
def sync_to_device(self, on_card, delete_from_library, specific_format=None):
|
||||
rows = self.library_view.selectionModel().selectedRows()
|
||||
if not self.device_manager or not rows or len(rows) == 0:
|
||||
return
|
||||
@ -900,7 +918,8 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
metadata = iter(metadata)
|
||||
_files = self.library_view.model().get_preferred_formats(rows,
|
||||
self.device_manager.device_class.FORMATS,
|
||||
paths=True, set_metadata=True)
|
||||
paths=True, set_metadata=True,
|
||||
specific_format=specific_format)
|
||||
files = [getattr(f, 'name', None) for f in _files]
|
||||
bad, good, gf, names, remove_ids = [], [], [], [], []
|
||||
for f in files:
|
||||
@ -1336,10 +1355,13 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
'''
|
||||
Handle exceptions in threaded device jobs.
|
||||
'''
|
||||
if 'Could not read 32 bytes on the control bus.' in str(job.exception):
|
||||
try:
|
||||
if 'Could not read 32 bytes on the control bus.' in unicode(job.exception):
|
||||
error_dialog(self, _('Error talking to device'),
|
||||
_('There was a temporary error talking to the device. Please unplug and reconnect the device and or reboot.')).show()
|
||||
return
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
print >>sys.stderr, job.console_text()
|
||||
except:
|
||||
|
@ -656,6 +656,15 @@
|
||||
<string>Books with the same tags</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_send_specific_format_to_device" >
|
||||
<property name="icon" >
|
||||
<iconset resource="images.qrc" >
|
||||
<normaloff>:/images/book.svg</normaloff>:/images/book.svg</iconset>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>Send specific format to device</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
|
@ -19,7 +19,8 @@ from calibre.library import title_sort
|
||||
from calibre.library.database import LibraryDatabase
|
||||
from calibre.library.sqlite import connect, IntegrityError
|
||||
from calibre.utils.search_query_parser import SearchQueryParser
|
||||
from calibre.ebooks.metadata import string_to_authors, authors_to_string, MetaInformation
|
||||
from calibre.ebooks.metadata import string_to_authors, authors_to_string, \
|
||||
MetaInformation, authors_to_sort_string
|
||||
from calibre.ebooks.metadata.meta import get_metadata, set_metadata, \
|
||||
metadata_from_formats
|
||||
from calibre.ebooks.metadata.opf2 import OPFCreator
|
||||
@ -728,7 +729,7 @@ class LibraryDatabase2(LibraryDatabase):
|
||||
if notify:
|
||||
self.notify('metadata', [id])
|
||||
|
||||
def delete_book(self, id):
|
||||
def delete_book(self, id, notify=True):
|
||||
'''
|
||||
Removes book from the result cache and the underlying database.
|
||||
'''
|
||||
@ -743,6 +744,7 @@ class LibraryDatabase2(LibraryDatabase):
|
||||
self.conn.commit()
|
||||
self.clean()
|
||||
self.data.books_deleted([id])
|
||||
if notify:
|
||||
self.notify('delete', [id])
|
||||
|
||||
def remove_format(self, index, format, index_is_id=False, notify=True):
|
||||
@ -1197,11 +1199,17 @@ class LibraryDatabase2(LibraryDatabase):
|
||||
|
||||
def import_book(self, mi, formats, notify=True):
|
||||
series_index = 1 if mi.series_index is None else mi.series_index
|
||||
if not mi.title:
|
||||
mi.title = _('Unknown')
|
||||
if not mi.authors:
|
||||
mi.authors = [_('Unknown')]
|
||||
aus = mi.author_sort if mi.author_sort else ', '.join(mi.authors)
|
||||
aus = mi.author_sort if mi.author_sort else authors_to_sort_string(mi.authors)
|
||||
if isinstance(aus, str):
|
||||
aus = aus.decode(preferred_encoding, 'replace')
|
||||
title = mi.title if isinstance(mi.title, unicode) else \
|
||||
mi.title.decode(preferred_encoding, 'replace')
|
||||
obj = self.conn.execute('INSERT INTO books(title, uri, series_index, author_sort) VALUES (?, ?, ?, ?)',
|
||||
(mi.title, None, series_index, aus))
|
||||
(title, None, series_index, aus))
|
||||
id = obj.lastrowid
|
||||
self.data.books_added([id], self.conn)
|
||||
self.set_path(id, True)
|
||||
@ -1210,8 +1218,7 @@ class LibraryDatabase2(LibraryDatabase):
|
||||
ext = os.path.splitext(path)[1][1:].lower()
|
||||
if ext == 'opf':
|
||||
continue
|
||||
stream = open(path, 'rb')
|
||||
self.add_format(id, ext, stream, index_is_id=True)
|
||||
self.add_format_with_hooks(id, ext, path, index_is_id=True)
|
||||
self.conn.commit()
|
||||
self.data.refresh_ids(self.conn, [id]) # Needed to update format list and size
|
||||
if notify:
|
||||
|
@ -12,11 +12,50 @@ from sqlite3 import IntegrityError
|
||||
from threading import Thread
|
||||
from Queue import Queue
|
||||
from threading import RLock
|
||||
from datetime import tzinfo, datetime, timedelta
|
||||
|
||||
from calibre.ebooks.metadata import title_sort
|
||||
|
||||
global_lock = RLock()
|
||||
|
||||
def convert_timestamp(val):
|
||||
datepart, timepart = val.split(' ')
|
||||
tz, mult = None, 1
|
||||
x = timepart.split('+')
|
||||
if len(x) > 1:
|
||||
timepart, tz = x
|
||||
else:
|
||||
x = timepart.split('-')
|
||||
if len(x) > 1:
|
||||
timepart, tz = x
|
||||
mult = -1
|
||||
|
||||
year, month, day = map(int, datepart.split("-"))
|
||||
timepart_full = timepart.split(".")
|
||||
hours, minutes, seconds = map(int, timepart_full[0].split(":"))
|
||||
if len(timepart_full) == 2:
|
||||
microseconds = int(timepart_full[1])
|
||||
else:
|
||||
microseconds = 0
|
||||
if tz is not None:
|
||||
h, m = map(int, tz.split(':'))
|
||||
delta = timedelta(minutes=mult*(60*h + m))
|
||||
tz = type('CustomTZ', (tzinfo,), {'utcoffset':lambda self, dt:delta,
|
||||
'dst':lambda self,dt:timedelta(0)})()
|
||||
|
||||
val = datetime(year, month, day, hours, minutes, seconds, microseconds,
|
||||
tzinfo=tz)
|
||||
if tz is not None:
|
||||
val = datetime(*(val.utctimetuple()[:6]))
|
||||
return val
|
||||
|
||||
def adapt_datetime(dt):
|
||||
dt = datetime(*(dt.utctimetuple()[:6]))
|
||||
return dt.isoformat(' ')
|
||||
|
||||
sqlite.register_adapter(datetime, adapt_datetime)
|
||||
sqlite.register_converter('timestamp', convert_timestamp)
|
||||
|
||||
class Concatenate(object):
|
||||
'''String concatenation aggregator for sqlite'''
|
||||
def __init__(self, sep=','):
|
||||
|
@ -480,6 +480,7 @@ VIEWER = '''\
|
||||
Version=%s
|
||||
Type=Application
|
||||
Name=LRF Viewer
|
||||
GenericName=Viewer for LRF files
|
||||
Comment=Viewer for LRF files (SONY ebook format files)
|
||||
TryExec=lrfviewer
|
||||
Exec=lrfviewer %%F
|
||||
@ -492,8 +493,9 @@ EVIEWER = '''\
|
||||
[Desktop Entry]
|
||||
Version=%s
|
||||
Type=Application
|
||||
Name=Ebook Viewer
|
||||
Comment=Viewer for Ebooks
|
||||
Name=E-book Viewer
|
||||
GenericName=Viewer for E-books
|
||||
Comment=Viewer for E-books
|
||||
TryExec=ebook-viewer
|
||||
Exec=ebook-viewer %%F
|
||||
Icon=calibre-viewer
|
||||
@ -506,7 +508,8 @@ GUI = '''\
|
||||
[Desktop Entry]
|
||||
Version=%s
|
||||
Type=Application
|
||||
Name=calibre - Ebook library management
|
||||
Name=calibre
|
||||
GenericName=E-book library management
|
||||
Comment=E-book library management
|
||||
TryExec=calibre
|
||||
Exec=calibre
|
||||
|
@ -28,7 +28,7 @@ What formats does |app| support conversion to/from?
|
||||
| | | | | |
|
||||
| | LIT | ✔ | ✔ | ✔ |
|
||||
| | | | | |
|
||||
| | PRC | ✔ | ✔ | ✔ |
|
||||
| | PRC** | ✔ | ✔ | ✔ |
|
||||
| | | | | |
|
||||
| | EPUB | ✔ | ✔ | ✔ |
|
||||
| | | | | |
|
||||
@ -49,6 +49,7 @@ What formats does |app| support conversion to/from?
|
||||
| | LRS | | ✔ | |
|
||||
+-------------------+--------+------------------+-----------------------+-----------------------+
|
||||
|
||||
** PRC is a generic format, |app| supports PRC files with TextRead and MOBIBook headers
|
||||
|
||||
|
||||
What are the best source formats to convert?
|
||||
@ -146,7 +147,7 @@ When you first run |app|, it will ask you for a folder in which to store your bo
|
||||
Why doesn't |app| let me store books in my own directory structure?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The whole point if |app|'s library management features is that they provide an interface for locating books that is *much* more efficient than any possible directory scheme you could come up with for your collection. Indeed, once you become comfortable using |app|'s interface to find, sort and browse your collection, you wont ever feel the need to hunt through the files on your disk to find a book again. By managing books in its own directory struture of Author -> Title -> Book files, |app| is able to achieve a high level of reliability and standardization.
|
||||
The whole point of |app|'s library management features is that they provide an interface for locating books that is *much* more efficient than any possible directory scheme you could come up with for your collection. Indeed, once you become comfortable using |app|'s interface to find, sort and browse your collection, you wont ever feel the need to hunt through the files on your disk to find a book again. By managing books in its own directory struture of Author -> Title -> Book files, |app| is able to achieve a high level of reliability and standardization.
|
||||
|
||||
Why doesn't |app| have a column for foo?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
12
src/calibre/manual/templates/layout.html
Normal file
12
src/calibre/manual/templates/layout.html
Normal file
@ -0,0 +1,12 @@
|
||||
{% extends "!layout.html" %}
|
||||
{% block sidebarlogo %}
|
||||
{{ super() }}
|
||||
<form action="https://www.paypal.com/cgi-bin/webscr" method="post">
|
||||
<input type="hidden" name="cmd" value="_s-xclick" />
|
||||
<input type="hidden" name="hosted_button_id" value="3028915" />
|
||||
<input type="image" src="https://www.paypal.com/en_US/i/btn/btn_donate_LG.gif" border="0" name="submit" alt="Donate to support calibre development" style="border:0pt" />
|
||||
<img alt="" border="0" src="https://www.paypal.com/en_US/i/scr/pixel.gif" width="1" height="1"/>
|
||||
</form>
|
||||
<hr/>
|
||||
{% endblock %}
|
||||
|
@ -197,6 +197,7 @@ class Server(object):
|
||||
def calculate_month_trend(self, days=31):
|
||||
stats = self.get_slice(date.today()-timedelta(days=days-1), date.today())
|
||||
fig = plt.figure(2, (12, 4), 96)#, facecolor, edgecolor, frameon, FigureClass)
|
||||
fig.clear()
|
||||
ax = fig.add_subplot(111)
|
||||
x = list(range(days-1, -1, -1))
|
||||
y = stats.daily_totals
|
||||
@ -235,6 +236,7 @@ Donors per day: %(dpd).2f
|
||||
y = [m.total for m in _months]
|
||||
ml = mdates.MonthLocator() # every month
|
||||
fig = plt.figure(1, (8, 4), 96)#, facecolor, edgecolor, frameon, FigureClass)
|
||||
fig.clear()
|
||||
ax = fig.add_subplot(111)
|
||||
average = sum(y)/len(y)
|
||||
ax.bar(x, y, align='center', width=20, color='g')
|
||||
|
@ -1,26 +1,7 @@
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
import re
|
||||
from pkg_resources import resource_filename
|
||||
|
||||
from trac.core import Component, implements
|
||||
from trac.web.chrome import INavigationContributor, ITemplateProvider, add_stylesheet
|
||||
from trac.web.main import IRequestHandler
|
||||
from trac.util import Markup
|
||||
|
||||
|
||||
__appname__ = 'calibre'
|
||||
DOWNLOAD_DIR = '/var/www/calibre.kovidgoyal.net/htdocs/downloads'
|
||||
MOBILEREAD = 'https://dev.mobileread.com/dist/kovid/calibre/'
|
||||
|
||||
class OS(dict):
|
||||
"""Dictionary with a default value for unknown keys."""
|
||||
def __init__(self, dict):
|
||||
self.update(dict)
|
||||
if not dict.has_key('img'):
|
||||
self['img'] = self['name']
|
||||
|
||||
class Distribution(object):
|
||||
import re, textwrap
|
||||
|
||||
DEPENDENCIES = [
|
||||
#(Generic, version, gentoo, ubuntu, fedora)
|
||||
@ -30,7 +11,7 @@ class Distribution(object):
|
||||
('libusb', '0.1.12', None, None, None),
|
||||
('Qt', '4.4.0', 'qt', 'libqt4-core libqt4-gui', 'qt4'),
|
||||
('PyQt', '4.4.2', 'PyQt4', 'python-qt4', 'PyQt4'),
|
||||
('mechanize for python', '0.1.11', 'dev-python/mechanize', 'python-mechanize', 'python-mechanize'),
|
||||
('python-mechanize', '0.1.11', 'dev-python/mechanize', 'python-mechanize', 'python-mechanize'),
|
||||
('ImageMagick', '6.3.5', 'imagemagick', 'imagemagick', 'ImageMagick'),
|
||||
('xdg-utils', '1.0.2', 'xdg-utils', 'xdg-utils', 'xdg-utils'),
|
||||
('dbus-python', '0.82.2', 'dbus-python', 'python-dbus', 'dbus-python'),
|
||||
@ -40,51 +21,59 @@ class Distribution(object):
|
||||
('help2man', '1.36.4', 'help2man', 'help2man', 'help2man'),
|
||||
]
|
||||
|
||||
DISTRO_MAP = {'gentoo':2, 'ubuntu':3, 'fedora':4, 'debian':3}
|
||||
|
||||
INSTALLERS = ('emerge -avn', 'apt-get install', 'yum install')
|
||||
AS_ROOT = (True, False, True)
|
||||
class CoolDistro:
|
||||
|
||||
TITLEMAP = {'gentoo':'Gentoo', 'ubuntu':'Ubuntu Intrepid Ibex',
|
||||
'fedora':'Fedora 10', 'debian':'Debian sid', 'generic': 'Install from source'}
|
||||
def __init__(self, name, title, prefix=''):
|
||||
self.title = title
|
||||
url = prefix + '/chrome/dl/images/%s_logo.png'
|
||||
self.img = url%name
|
||||
|
||||
MANUAL_MAP = {
|
||||
'fedora' : '''<li>You have to upgrade Qt to at least 4.4.0 and PyQt to at least 4.4.2</li>''',
|
||||
}
|
||||
def get_linux_data(version='1.0.0'):
|
||||
data = {'version':version, 'app':__appname__}
|
||||
data['title'] = 'Download calibre for linux'
|
||||
data['supported'] = []
|
||||
for name, title in [
|
||||
('ubuntu', 'Ubuntu Jaunty Jackalope'),
|
||||
('debian', 'Debian Sid'),
|
||||
('exherbo', 'Exherbo'),
|
||||
]:
|
||||
data['supported'].append(CoolDistro(name, title,
|
||||
prefix='http://calibre.kovidgoyal.net'))
|
||||
data['dependencies'] = DEPENDENCIES
|
||||
return data
|
||||
|
||||
def __init__(self, os):
|
||||
self.os = os
|
||||
self.img = os
|
||||
self.title = self.TITLEMAP[os]
|
||||
self.app = __appname__
|
||||
self.is_generic = os == 'generic'
|
||||
offset = 0
|
||||
if not self.is_generic:
|
||||
index = self.DISTRO_MAP[self.os]
|
||||
if os == 'debian':
|
||||
self.as_root = True
|
||||
else: self.as_root = self.AS_ROOT[index-2]
|
||||
prefix = ''
|
||||
if not self.as_root: prefix = 'sudo '
|
||||
cmd = prefix + self.INSTALLERS[index-2]
|
||||
pre = ' \\\n '.ljust(len(cmd)+4)
|
||||
for dep in self.DEPENDENCIES:
|
||||
if len(cmd) > 70+offset:
|
||||
offset += 70
|
||||
cmd += pre
|
||||
cmd += ' '
|
||||
if dep[index]: cmd += dep[index]
|
||||
self.command = cmd.strip()
|
||||
easy_install = 'easy_install'
|
||||
if os == 'debian':
|
||||
easy_install = 'easy_install-2.5'
|
||||
self.command += '\n'+prefix+easy_install+' -U calibre \n'+prefix+'calibre_postinstall'
|
||||
try:
|
||||
self.manual = Markup(self.MANUAL_MAP[os])
|
||||
except KeyError:
|
||||
self.manual = None
|
||||
if __name__ == '__main__':
|
||||
import os
|
||||
from calibre.utils.genshi.template import MarkupTemplate
|
||||
import cherrypy
|
||||
class Test:
|
||||
def index(self):
|
||||
raw = open(os.path.dirname(os.path.abspath(__file__))+'/templates/linux.html').read()
|
||||
return MarkupTemplate(raw).generate(**get_linux_data()).render('xhtml')
|
||||
index.exposed = True
|
||||
t = Test()
|
||||
t.index()
|
||||
cherrypy.quickstart(t)
|
||||
else:
|
||||
self.img = 'linux'
|
||||
from pkg_resources import resource_filename
|
||||
|
||||
from trac.core import Component, implements
|
||||
from trac.web.chrome import INavigationContributor, ITemplateProvider, add_stylesheet
|
||||
from trac.web.main import IRequestHandler
|
||||
from trac.util import Markup
|
||||
|
||||
|
||||
|
||||
DOWNLOAD_DIR = '/var/www/calibre.kovidgoyal.net/htdocs/downloads'
|
||||
MOBILEREAD = 'https://dev.mobileread.com/dist/kovid/calibre/'
|
||||
|
||||
class OS(dict):
|
||||
"""Dictionary with a default value for unknown keys."""
|
||||
def __init__(self, dict):
|
||||
self.update(dict)
|
||||
if not dict.has_key('img'):
|
||||
self['img'] = self['name']
|
||||
|
||||
|
||||
class Download(Component):
|
||||
@ -125,16 +114,6 @@ class Download(Component):
|
||||
return self.osx(req)
|
||||
elif os == 'linux':
|
||||
return self.linux(req)
|
||||
elif 'binary' in os:
|
||||
return self.linux_binary(req)
|
||||
else:
|
||||
return self.linux_distro(req, os)
|
||||
|
||||
def linux_distro(self, req, os):
|
||||
version = self.version_from_filename()
|
||||
distro = Distribution(os)
|
||||
data = dict(distro=distro,title=distro.title, version=version)
|
||||
return 'distro.html', data, None
|
||||
|
||||
def top_level(self, req):
|
||||
operating_systems = [
|
||||
@ -189,10 +168,6 @@ select Install.</li>
|
||||
'''%dict(appname=__appname__)))
|
||||
return 'binary.html', data, None
|
||||
|
||||
def linux_binary(self, req):
|
||||
version = self.version_from_filename()
|
||||
return 'pyinstaller.html', {'app':__appname__, 'version':version}, None
|
||||
|
||||
def osx(self, req):
|
||||
version = self.version_from_filename()
|
||||
file = 'calibre-%s.dmg'%(version,)
|
||||
@ -212,20 +187,11 @@ select Install.</li>
|
||||
return 'binary.html', data, None
|
||||
|
||||
def linux(self, req):
|
||||
operating_systems = [
|
||||
OS({'name' : 'binary', 'title': 'Binary Installer'}),
|
||||
OS({'name' : 'gentoo', 'title': 'Gentoo'}),
|
||||
OS({'name' : 'ubuntu', 'title': 'Ubuntu'}),
|
||||
OS({'name' : 'fedora', 'title': 'Fedora'}),
|
||||
OS({'name' : 'debian', 'title': 'Debian'}),
|
||||
OS({'name' : 'generic','title': 'Install from source', 'img':'linux'}),
|
||||
]
|
||||
data = dict(title='Choose linux distribution', width=100,
|
||||
operating_systems=operating_systems, font_size='x-large', top_level=False)
|
||||
return 'download.html', data, None
|
||||
data = get_linux_data(version=self.version_from_filename())
|
||||
return 'linux.html', data, None
|
||||
|
||||
|
||||
LINUX_INSTALLER = r'''
|
||||
LINUX_INSTALLER = textwrap.dedent(r'''
|
||||
import sys, os, shutil, tarfile, subprocess, tempfile, urllib2, re, stat
|
||||
|
||||
MOBILEREAD='https://dev.mobileread.com/dist/kovid/calibre/'
|
||||
@ -416,4 +382,5 @@ def main():
|
||||
pi = os.path.join(destdir, 'calibre_postinstall')
|
||||
subprocess.call(pi, shell=True)
|
||||
return 0
|
||||
'''
|
||||
''')
|
||||
|
||||
|
BIN
src/calibre/trac/plugins/htdocs/images/exherbo_logo.png
Normal file
BIN
src/calibre/trac/plugins/htdocs/images/exherbo_logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 42 KiB |
@ -1,68 +0,0 @@
|
||||
<!DOCTYPE html
|
||||
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:py="http://genshi.edgewall.org/"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
<xi:include href="layout.html" />
|
||||
<head>
|
||||
<title>$title</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="ctxtnav" class="nav"></div>
|
||||
|
||||
<div id="content" class="download">
|
||||
<h1><img src="${href.chrome('/dl/images/%s_logo.png'%(distro.img,))}" valign="middle" width="60" height="80"/> $title</h1>
|
||||
See the <a href="/wiki/Changelog">Changelog</a> for the changes in the latest version. <span py:if="not distro.is_generic"><b>Note:</b> As of 0.4.80 this install
|
||||
will not work on 64-bit CPUs. Try the precompiled binary available <a href="/download_binary">here</a> instead.</span>
|
||||
<div py:if="not distro.is_generic">
|
||||
First verify that you have a sufficiently new installation of python
|
||||
<pre class="wiki">python --version</pre> should return at least 2.5.1<br />
|
||||
<p py:if="distro.os == 'gentoo'">
|
||||
Make sure your python is compiled with the sqlite USE flag.
|
||||
</p>
|
||||
<br />
|
||||
Run the following commands in a terminal <span py:if="distro.as_root">as root</span>
|
||||
<pre class="wiki">${distro.command}</pre>
|
||||
<div py:if="distro.manual">
|
||||
<h2>Manual steps</h2>
|
||||
<ul>
|
||||
${distro.manual}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div py:if="distro.is_generic">
|
||||
<ol>
|
||||
<li>Make sure that your system has <code>python >= 2.5</code></li>
|
||||
<li>Install the various dependencies listed below: Make sure that any python packages are installed into python2.5 (e.g. setuptools, python-imaging, PyQt4, etc). You will also have to install the development versions of the packages (these packages usually have -dev added to their names).</li>
|
||||
<li>Run the following commands in a terminal:
|
||||
<pre class="wiki">
|
||||
wget -O- http://calibre.kovidgoyal.net/downloads/calibre-${version}.tar.gz | tar xvz
|
||||
cd calibre*
|
||||
python setup.py build && sudo python setup.py install
|
||||
</pre></li>
|
||||
</ol>
|
||||
<h2>Dependencies</h2>
|
||||
<table border="1" cellpadding="10">
|
||||
<tr><th style="font-weight:bold">Name</th><th style="font-weight:bold">Minimum version</th></tr>
|
||||
<tr py:for="dep in distro.DEPENDENCIES">
|
||||
<td>${dep[0]}</td><td>${dep[1]}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>HAL</td><td>0.5.10</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
While you wait for the installation to complete, please consider donating to support the development of ${distro.app}.
|
||||
<div>
|
||||
<form action="https://www.paypal.com/cgi-bin/webscr" method="post">
|
||||
<input type="hidden" name="cmd" value="_s-xclick" />
|
||||
<input type="hidden" name="hosted_button_id" value="3029289" />
|
||||
<input type="image" src="https://www.paypal.com/en_US/i/btn/btn_donateCC_LG.gif" border="0" name="submit" alt="Donate to support calibre development" />
|
||||
<img alt="" border="0" src="https://www.paypal.com/en_US/i/scr/pixel.gif" width="1" height="1" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
155
src/calibre/trac/plugins/templates/linux.html
Normal file
155
src/calibre/trac/plugins/templates/linux.html
Normal file
@ -0,0 +1,155 @@
|
||||
<!DOCTYPE html
|
||||
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:py="http://genshi.edgewall.org/"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
<xi:include href="layout.html" />
|
||||
<head>
|
||||
<title>${title}</title>
|
||||
<style type="text/css">
|
||||
table tr th.distro_type {
|
||||
font-family: monospace;
|
||||
font-weight: bold;
|
||||
font-size: larger;
|
||||
vertical-align:middle;
|
||||
text-align: center;
|
||||
border-bottom: solid 1pt black;
|
||||
}
|
||||
|
||||
.right {
|
||||
margin-left:2em;
|
||||
border-left: solid 1pt black;
|
||||
}
|
||||
|
||||
table#install_info {
|
||||
border-collapse: collapse;
|
||||
border-width: 1pt;
|
||||
border-style: solid;
|
||||
table-layout: fixed;
|
||||
width: 95%;
|
||||
background: #F7F7F0 none repeat scroll 0 0;
|
||||
}
|
||||
table#dependencies {
|
||||
border-collapse: collapse;
|
||||
border-width: 0pt;
|
||||
font-family:monospace;
|
||||
}
|
||||
.tdata {vertical-align: top;}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="ctxtnav" class="nav"></div>
|
||||
<div id="content" class="download">
|
||||
<h1>${title}</h1>
|
||||
<p>
|
||||
The latest release of ${app} is ${version}. See the
|
||||
<a href="/wiki/Changelog">Changelog</a> for a list of new features.
|
||||
</p>
|
||||
<p>
|
||||
${app} is available in the software repositories of the following
|
||||
linux distributions:
|
||||
<table id="install_info">
|
||||
<col width="150" /><col width="*" />
|
||||
<tr>
|
||||
<th class="left distro_type">Supported<br/>distributions</th>
|
||||
<th class="right distro_type">Unsupported distributions</th>
|
||||
</tr>
|
||||
<tr class="tdata">
|
||||
<td class="left" style="overflow-y:scroll">
|
||||
<div py:for="distro in supported"
|
||||
style="text-align:center;margin-top:2ex;">
|
||||
<div>
|
||||
<img width="64px" height="64px"
|
||||
src="${distro.img}" alt="${distro.title}" />
|
||||
</div>
|
||||
${distro.title}
|
||||
</div>
|
||||
</td>
|
||||
<td class="right">
|
||||
<div style="margin-left:2em">
|
||||
<h3>Binary install</h3>
|
||||
<p>
|
||||
${app} has a binary installer that has been
|
||||
tested on a number of distributions on both
|
||||
32-bit and 64-bit x86 machines. To install,
|
||||
copy paste the following command into a terminal
|
||||
and press Enter:
|
||||
</p>
|
||||
<pre class="wiki">
|
||||
sudo python -c "import urllib2; exec urllib2.urlopen('http://calibre.kovidgoyal.net/download_linux_binary_installer').read(); main()"
|
||||
</pre>
|
||||
<h4>Note</h4>
|
||||
<ul>
|
||||
<li>
|
||||
When running the command line utilities,
|
||||
they will segfault after completion. This can
|
||||
be ignored.
|
||||
</li>
|
||||
<li>
|
||||
You must have help2man and xdg-utils installed
|
||||
on your system before running the installer.
|
||||
</li>
|
||||
<li>
|
||||
On a 64bit machine, you must have 32-bit versions
|
||||
of common libraries like X11, freetype, fontconfig,
|
||||
expat and their varios dependencies installed.
|
||||
</li>
|
||||
</ul>
|
||||
<h3>Source install</h3>
|
||||
<p>
|
||||
<ol>
|
||||
<li>
|
||||
Make sure your system has python ≥ ${dependencies[0][1]}
|
||||
</li>
|
||||
<li>
|
||||
Install the various dependencies listed below
|
||||
</li>
|
||||
<li>
|
||||
Run the following commands in a terminal:
|
||||
</li>
|
||||
</ol>
|
||||
<pre class="wiki">
|
||||
wget -O- http://calibre.kovidgoyal.net/downloads/${app}-${version}.tar.gz | tar xvz
|
||||
cd calibre*
|
||||
python setup.py build && sudo python setup.py install
|
||||
</pre>
|
||||
Note that if your distribution does not have a
|
||||
correctly compiled libunrar.so, ${app} will not
|
||||
support rar files.
|
||||
</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</p>
|
||||
<p>
|
||||
While you wait for the download to complete, please consider
|
||||
donating to support the development of ${app}.
|
||||
</p>
|
||||
<div>
|
||||
<form action="https://www.paypal.com/cgi-bin/webscr" method="post">
|
||||
<input type="hidden" name="cmd" value="_s-xclick" />
|
||||
<input type="image" src="https://www.paypal.com/en_US/i/btn/btn_donateCC_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!" />
|
||||
<img alt="" border="0" src="https://www.paypal.com/en_US/i/scr/pixel.gif" width="1" height="1" />
|
||||
<input type="hidden" name="encrypted" value="-----BEGIN PKCS7-----MIIHbwYJKoZIhvcNAQcEoIIHYDCCB1wCAQExggEwMIIBLAIBADCBlDCBjjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtQYXlQYWwgSW5jLjETMBEGA1UECxQKbGl2ZV9jZXJ0czERMA8GA1UEAxQIbGl2ZV9hcGkxHDAaBgkqhkiG9w0BCQEWDXJlQHBheXBhbC5jb20CAQAwDQYJKoZIhvcNAQEBBQAEgYBn7jneGiSLVO8rcDrBtOUXL+HftY+CiC47hTntwICio6qqpLKezIryyG8tKcjY58Rcocur/kDwljEutIafVG7XRA7BJL9eZdHAZsZdX04f4dApzkWwR9w6GQhj0kwmO2ZNE878UcgGZBve4qQKWM8bf2pMY7vJwCNoo6ozpIi3VTELMAkGBSsOAwIaBQAwgewGCSqGSIb3DQEHATAUBggqhkiG9w0DBwQIBTALt7s1gJmAgcjEAwUMRYeIdIOE/yi0Y5vrVKBFxOUCbqTx/lu3Rk4EHsODZXLHT+BDA5WSWYO3AXfv2Lmlv1kJ7jWrjUVirYoQ5M4qdIhY9DtvPioIMMRoTJmYM9JKH8n2TWcjJ1XIzIuDP4zn8/Ya9hap3RHOrj2RBj89g7iSuFRsjoA0PYZgtWAKwR7g3LLpjRachn041JO55BEd3YWUgorNQeo3WEHgowLFfTWgFFePkm8OoWA1klWkYp4S07IhX5NaRc8OegkdshpkiIHGAKCCA4cwggODMIIC7KADAgECAgEAMA0GCSqGSIb3DQEBBQUAMIGOMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxFDASBgNVBAoTC1BheVBhbCBJbmMuMRMwEQYDVQQLFApsaXZlX2NlcnRzMREwDwYDVQQDFAhsaXZlX2FwaTEcMBoGCSqGSIb3DQEJARYNcmVAcGF5cGFsLmNvbTAeFw0wNDAyMTMxMDEzMTVaFw0zNTAyMTMxMDEzMTVaMIGOMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxFDASBgNVBAoTC1BheVBhbCBJbmMuMRMwEQYDVQQLFApsaXZlX2NlcnRzMREwDwYDVQQDFAhsaXZlX2FwaTEcMBoGCSqGSIb3DQEJARYNcmVAcGF5cGFsLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwUdO3fxEzEtcnI7ZKZL412XvZPugoni7i7D7prCe0AtaHTc97CYgm7NsAtJyxNLixmhLV8pyIEaiHXWAh8fPKW+R017+EmXrr9EaquPmsVvTywAAE1PMNOKqo2kl4Gxiz9zZqIajOm1fZGWcGS0f5JQ2kBqNbvbg2/Za+GJ/qwUCAwEAAaOB7jCB6zAdBgNVHQ4EFgQUlp98u8ZvF71ZP1LXChvsENZklGswgbsGA1UdIwSBszCBsIAUlp98u8ZvF71ZP1LXChvsENZklGuhgZSkgZEwgY4xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLUGF5UGFsIEluYy4xEzARBgNVBAsUCmxpdmVfY2VydHMxETAPBgNVBAMUCGxpdmVfYXBpMRwwGgYJKoZIhvcNAQkBFg1yZUBwYXlwYWwuY29tggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAgV86VpqAWuXvX6Oro4qJ1tYVIT5DgWpE692Ag422H7yRIr/9j/iKG4Thia/Oflx4TdL+IFJBAyPK9v6zZNZtBgPBynXb048hsP16l2vi0k5Q2JKiPDsEfBhGI+HnxLXEaUWAcVfCsQFvd2A1sxRr67ip5y2wwBelUecP3AjJ+YcxggGaMIIBlgIBATCBlDCBjjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtQYXlQYWwgSW5jLjETMBEGA1UECxQKbGl2ZV9jZXJ0czERMA8GA1UEAxQIbGl2ZV9hcGkxHDAaBgkqhkiG9w0BCQEWDXJlQHBheXBhbC5jb20CAQAwCQYFKw4DAhoFAKBdMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTA4MDQzMDE1MzkyMlowIwYJKoZIhvcNAQkEMRYEFJSI9/zWx7TUlKPY7kLjnvzB1h6sMA0GCSqGSIb3DQEBAQUABIGAikZNCmQdkWPdfmYnGqOb1f65ViaK0zjHf50azvsigWQLlhHqJ3PgB+jEJH3JU9Pm9M4wgiK23Bg2oIGuIsAfQkYO9mw/HjtDtOQHqXyZZbrM32YGtNWUD4ynakLYnaz7OnPl40aTPD4iDApgsGcj1oMdmw7KA2E9J0l2J9iJXF4=-----END PKCS7-----" />
|
||||
</form>
|
||||
</div>
|
||||
<hr/>
|
||||
<h3>Dependencies</h3>
|
||||
${app} has the following dependencies (the listed version is the minimum version)
|
||||
<br/><br/>
|
||||
<table id="dependencies">
|
||||
<tr>
|
||||
<th class="distro_type" style="margin-right:2em">Package</th>
|
||||
<th class="distro_type">Version</th>
|
||||
</tr>
|
||||
|
||||
<tr class="dependency" py:for="dep in dependencies">
|
||||
<td style="margin-right:2em">${dep[0]}</td><td>${dep[1]}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -1,47 +0,0 @@
|
||||
<!DOCTYPE html
|
||||
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:py="http://genshi.edgewall.org/"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
<xi:include href="layout.html" />
|
||||
<head>
|
||||
<title>Download $app for Linux</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="ctxtnav" class="nav"></div>
|
||||
|
||||
<div id="content" class="binary">
|
||||
<h1>Download $app for Linux</h1>
|
||||
<p>This binary package is compatible with most recent linux distributions running on Intel 32 bit CPUs. It needs testing on 64 bit CPUs. There have been reports of its working on some 64bit machines. </p>
|
||||
<p>
|
||||
<img width="50" height="50" style="border:1px red solid" src="${href.chrome('/dl/images/binary_logo.png')}" />
|
||||
(Version: $version <a href="/wiki/Changelog">Changelog</a>)
|
||||
</p>
|
||||
<p>To install, copy paste the following command into a terminal and press Enter:
|
||||
</p>
|
||||
<pre class="wiki">sudo python -c "import urllib2; exec urllib2.urlopen('http://calibre.kovidgoyal.net/download_linux_binary_installer').read(); main()"</pre>
|
||||
<p>
|
||||
While you wait for the download to complete, please consider donating to support the development
|
||||
of ${app}.</p>
|
||||
<div>
|
||||
<form action="https://www.paypal.com/cgi-bin/webscr" method="post">
|
||||
<input type="hidden" name="cmd" value="_s-xclick" />
|
||||
<input type="image" src="https://www.paypal.com/en_US/i/btn/btn_donateCC_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!" />
|
||||
<img alt="" border="0" src="https://www.paypal.com/en_US/i/scr/pixel.gif" width="1" height="1" />
|
||||
<input type="hidden" name="encrypted" value="-----BEGIN PKCS7-----MIIHbwYJKoZIhvcNAQcEoIIHYDCCB1wCAQExggEwMIIBLAIBADCBlDCBjjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtQYXlQYWwgSW5jLjETMBEGA1UECxQKbGl2ZV9jZXJ0czERMA8GA1UEAxQIbGl2ZV9hcGkxHDAaBgkqhkiG9w0BCQEWDXJlQHBheXBhbC5jb20CAQAwDQYJKoZIhvcNAQEBBQAEgYBn7jneGiSLVO8rcDrBtOUXL+HftY+CiC47hTntwICio6qqpLKezIryyG8tKcjY58Rcocur/kDwljEutIafVG7XRA7BJL9eZdHAZsZdX04f4dApzkWwR9w6GQhj0kwmO2ZNE878UcgGZBve4qQKWM8bf2pMY7vJwCNoo6ozpIi3VTELMAkGBSsOAwIaBQAwgewGCSqGSIb3DQEHATAUBggqhkiG9w0DBwQIBTALt7s1gJmAgcjEAwUMRYeIdIOE/yi0Y5vrVKBFxOUCbqTx/lu3Rk4EHsODZXLHT+BDA5WSWYO3AXfv2Lmlv1kJ7jWrjUVirYoQ5M4qdIhY9DtvPioIMMRoTJmYM9JKH8n2TWcjJ1XIzIuDP4zn8/Ya9hap3RHOrj2RBj89g7iSuFRsjoA0PYZgtWAKwR7g3LLpjRachn041JO55BEd3YWUgorNQeo3WEHgowLFfTWgFFePkm8OoWA1klWkYp4S07IhX5NaRc8OegkdshpkiIHGAKCCA4cwggODMIIC7KADAgECAgEAMA0GCSqGSIb3DQEBBQUAMIGOMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxFDASBgNVBAoTC1BheVBhbCBJbmMuMRMwEQYDVQQLFApsaXZlX2NlcnRzMREwDwYDVQQDFAhsaXZlX2FwaTEcMBoGCSqGSIb3DQEJARYNcmVAcGF5cGFsLmNvbTAeFw0wNDAyMTMxMDEzMTVaFw0zNTAyMTMxMDEzMTVaMIGOMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxFDASBgNVBAoTC1BheVBhbCBJbmMuMRMwEQYDVQQLFApsaXZlX2NlcnRzMREwDwYDVQQDFAhsaXZlX2FwaTEcMBoGCSqGSIb3DQEJARYNcmVAcGF5cGFsLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwUdO3fxEzEtcnI7ZKZL412XvZPugoni7i7D7prCe0AtaHTc97CYgm7NsAtJyxNLixmhLV8pyIEaiHXWAh8fPKW+R017+EmXrr9EaquPmsVvTywAAE1PMNOKqo2kl4Gxiz9zZqIajOm1fZGWcGS0f5JQ2kBqNbvbg2/Za+GJ/qwUCAwEAAaOB7jCB6zAdBgNVHQ4EFgQUlp98u8ZvF71ZP1LXChvsENZklGswgbsGA1UdIwSBszCBsIAUlp98u8ZvF71ZP1LXChvsENZklGuhgZSkgZEwgY4xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLUGF5UGFsIEluYy4xEzARBgNVBAsUCmxpdmVfY2VydHMxETAPBgNVBAMUCGxpdmVfYXBpMRwwGgYJKoZIhvcNAQkBFg1yZUBwYXlwYWwuY29tggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAgV86VpqAWuXvX6Oro4qJ1tYVIT5DgWpE692Ag422H7yRIr/9j/iKG4Thia/Oflx4TdL+IFJBAyPK9v6zZNZtBgPBynXb048hsP16l2vi0k5Q2JKiPDsEfBhGI+HnxLXEaUWAcVfCsQFvd2A1sxRr67ip5y2wwBelUecP3AjJ+YcxggGaMIIBlgIBATCBlDCBjjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtQYXlQYWwgSW5jLjETMBEGA1UECxQKbGl2ZV9jZXJ0czERMA8GA1UEAxQIbGl2ZV9hcGkxHDAaBgkqhkiG9w0BCQEWDXJlQHBheXBhbC5jb20CAQAwCQYFKw4DAhoFAKBdMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTA4MDQzMDE1MzkyMlowIwYJKoZIhvcNAQkEMRYEFJSI9/zWx7TUlKPY7kLjnvzB1h6sMA0GCSqGSIb3DQEBAQUABIGAikZNCmQdkWPdfmYnGqOb1f65ViaK0zjHf50azvsigWQLlhHqJ3PgB+jEJH3JU9Pm9M4wgiK23Bg2oIGuIsAfQkYO9mw/HjtDtOQHqXyZZbrM32YGtNWUD4ynakLYnaz7OnPl40aTPD4iDApgsGcj1oMdmw7KA2E9J0l2J9iJXF4=-----END PKCS7-----" />
|
||||
</form>
|
||||
|
||||
</div>
|
||||
<h2>Note</h2>
|
||||
<div class="note">
|
||||
<ul>
|
||||
<li>This installer is very new and has only been tested on a couple of systems, so if you encounter
|
||||
problems, please report them.</li>
|
||||
<li>You shoud have help2man and xdg-utils installed on your system.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
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
@ -6,7 +6,7 @@ __docformat__ = 'restructuredtext en'
|
||||
'''
|
||||
Manage application-wide preferences.
|
||||
'''
|
||||
import os, re, cPickle, textwrap
|
||||
import os, re, cPickle, textwrap, traceback
|
||||
from copy import deepcopy
|
||||
from functools import partial
|
||||
from optparse import OptionParser as _OptionParser
|
||||
@ -314,7 +314,12 @@ class OptionSet(object):
|
||||
if not isinstance(src, unicode):
|
||||
src = src.decode('utf-8')
|
||||
if src is not None:
|
||||
try:
|
||||
exec src in options
|
||||
except:
|
||||
print 'Failed to parse options string:'
|
||||
print repr(src)
|
||||
traceback.print_exc()
|
||||
opts = OptionValues()
|
||||
for pref in self.preferences:
|
||||
val = options.get(pref.name, pref.default)
|
||||
@ -539,7 +544,7 @@ def _prefs():
|
||||
help=_('Path to directory in which your library of books is stored'))
|
||||
c.add_opt('language', default=None,
|
||||
help=_('The language in which to display the user interface'))
|
||||
c.add_opt('output_format', default='LRF',
|
||||
c.add_opt('output_format', default='EPUB',
|
||||
help=_('The default output format for ebook conversions.'))
|
||||
c.add_opt('read_file_metadata', default=True,
|
||||
help=_('Read metadata from files'))
|
||||
|
@ -190,7 +190,7 @@ class BasicNewsRecipe(object, LoggingInterface):
|
||||
#: For the format for specifying a tag see :attr:`BasicNewsRecipe.remove_tags`.
|
||||
#: For example::
|
||||
#:
|
||||
#: remove_tags_before = [dict(id='content')]
|
||||
#: remove_tags_before = dict(id='content')
|
||||
#:
|
||||
#: will remove all
|
||||
#: tags before the first element with `id="content"`.
|
||||
|
@ -28,7 +28,9 @@ recipe_modules = ['recipe_' + r for r in (
|
||||
'la_tercera', 'el_mercurio_chile', 'la_cuarta', 'lanacion_chile', 'la_segunda',
|
||||
'jb_online', 'estadao', 'o_globo', 'vijesti', 'elmundo', 'the_oz',
|
||||
'honoluluadvertiser', 'starbulletin', 'exiled', 'indy_star', 'dna',
|
||||
'pobjeda',
|
||||
'pobjeda', 'chicago_breaking_news', 'glasgow_herald', 'linuxdevices',
|
||||
'hindu', 'cincinnati_enquirer', 'physics_world', 'pressonline',
|
||||
'la_republica', 'physics_today',
|
||||
)]
|
||||
|
||||
import re, imp, inspect, time, os
|
||||
|
@ -1,50 +1,77 @@
|
||||
#!/usr/bin/env python
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008-2009, Darko Miletic <darko.miletic at gmail.com>'
|
||||
'''
|
||||
arstechnica.com
|
||||
'''
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class ArsTechnica(BasicNewsRecipe):
|
||||
title = 'Ars Technica'
|
||||
description = 'The art of technology'
|
||||
oldest_article = 7
|
||||
class ArsTechnica2(BasicNewsRecipe):
|
||||
title = u'Ars Technica'
|
||||
language = _('English')
|
||||
no_stylesheets = True
|
||||
__author__ = 'Michael Warner'
|
||||
__author__ = 'Darko Miletic'
|
||||
description = 'The art of technology'
|
||||
publisher = 'Ars Technica'
|
||||
category = 'news, IT, technology'
|
||||
oldest_article = 2
|
||||
max_articles_per_feed = 100
|
||||
extra_css = """
|
||||
body {
|
||||
font: normal 19px/180% Times, serif;
|
||||
}
|
||||
no_stylesheets = True
|
||||
encoding = 'utf8'
|
||||
remove_javascript = True
|
||||
use_embedded_content = False
|
||||
|
||||
html2lrf_options = [
|
||||
'--comment', description
|
||||
, '--category', category
|
||||
, '--publisher', publisher
|
||||
]
|
||||
|
||||
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"'
|
||||
|
||||
keep_only_tags = [dict(name='div', attrs={'id':['news-item-info','news-item']})]
|
||||
|
||||
h1, h2, h3, h4 {
|
||||
font: bold 28px/100% Verdana, Arial, Helvetica, sans-serif;
|
||||
margin-top: 19px
|
||||
}
|
||||
"""
|
||||
remove_tags = [
|
||||
dict(id="Masthead"),
|
||||
dict(id="Banner"),
|
||||
dict(id="Nav"),
|
||||
dict(name='div', attrs={'class':'ContentHeader'}),
|
||||
dict(name='img'),
|
||||
dict(name='div', attrs={'class':'Inset RelatedStories'}),
|
||||
dict(name='div', attrs={'class':'Tags'}),
|
||||
dict(name='div', attrs={'class':'PostOptions flat'}),
|
||||
dict(name='div', attrs={'class':'ContentFooter'}),
|
||||
dict(id="Sidebar"),
|
||||
dict(id="LatestPosts"),
|
||||
dict(id="Footer")]
|
||||
feeds = [(u'News and Features', u'http://feeds.arstechnica.com/arstechnica/BAaf'),
|
||||
(u'Nobel Intent (Science)', u'http://arstechnica.com/journals/science.rssx'),
|
||||
(u'Infinite Loop (Apple)', u'http://arstechnica.com/journals/apple.rssx'),
|
||||
(u'M-Dollar (Microsoft)', u'http://arstechnica.com/journals/microsoft.rssx'),
|
||||
(u'Open Ended (Linux)', u'http://arstechnica.com/journals/linux.rssx'),
|
||||
(u'Opposable Thumbs (Games)', u'http://arstechnica.com/journals/thumbs.rssx'),
|
||||
(u'Kit (Hardware)', u'http://arstechnica.com/journals/hardware.rssx'),
|
||||
(u'Journals', u'http://arstechnica.com/journals.rssx')]
|
||||
dict(name=['object','link','embed'])
|
||||
,dict(name='div', attrs={'class':'related-stories'})
|
||||
]
|
||||
|
||||
|
||||
feeds = [
|
||||
(u'Infinite Loop (Apple content)' , u'http://feeds.arstechnica.com/arstechnica/apple/' )
|
||||
,(u'Opposable Thumbs (Gaming content)' , u'http://feeds.arstechnica.com/arstechnica/gaming/' )
|
||||
,(u'Gear and Gadgets' , u'http://feeds.arstechnica.com/arstechnica/gadgets/' )
|
||||
,(u'Chipster (Hardware content)' , u'http://feeds.arstechnica.com/arstechnica/hardware/' )
|
||||
,(u'Uptime (IT content)' , u'http://feeds.arstechnica.com/arstechnica/business/' )
|
||||
,(u'Open Ended (Open Source content)' , u'http://feeds.arstechnica.com/arstechnica/open-source/')
|
||||
,(u'One Microsoft Way' , u'http://feeds.arstechnica.com/arstechnica/microsoft/' )
|
||||
,(u'Nobel Intent (Science content)' , u'http://feeds.arstechnica.com/arstechnica/science/' )
|
||||
,(u'Law & Disorder (Tech policy content)' , u'http://feeds.arstechnica.com/arstechnica/tech-policy/')
|
||||
]
|
||||
|
||||
def append_page(self, soup, appendtag, position):
|
||||
pager = soup.find('div',attrs={'id':'pager'})
|
||||
if pager:
|
||||
for atag in pager.findAll('a',href=True):
|
||||
str = self.tag_to_string(atag)
|
||||
if str.startswith('Next'):
|
||||
soup2 = self.index_to_soup(atag['href'])
|
||||
texttag = soup2.find('div', attrs={'class':'news-item-text'})
|
||||
for it in texttag.findAll(style=True):
|
||||
del it['style']
|
||||
newpos = len(texttag.contents)
|
||||
self.append_page(soup2,texttag,newpos)
|
||||
texttag.extract()
|
||||
pager.extract()
|
||||
appendtag.insert(position,texttag)
|
||||
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
ftag = soup.find('div', attrs={'class':'news-item-byline'})
|
||||
if ftag:
|
||||
ftag.insert(4,'<br /><br />')
|
||||
for item in soup.findAll(style=True):
|
||||
del item['style']
|
||||
self.append_page(soup, soup.body, 3)
|
||||
return soup
|
||||
|
@ -1,13 +1,12 @@
|
||||
#!/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>'
|
||||
'''
|
||||
b92.net
|
||||
'''
|
||||
|
||||
import re
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class B92(BasicNewsRecipe):
|
||||
@ -22,19 +21,22 @@ class B92(BasicNewsRecipe):
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
cover_url = 'http://static.b92.net/images/fp/logo.gif'
|
||||
extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} @font-face {font-family: "monospace1";src:url(res:///opt/sony/ebook/FONT/tt0419m_.ttf)} @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} body{text-align: left; font-family: serif1, serif} .article_date{font-family: monospace1, monospace} .article_description{font-family: sans1, sans-serif} .navbar{font-family: monospace1, monospace}'
|
||||
|
||||
keep_only_tags = [ dict(name='div', attrs={'class':'sama_vest'}) ]
|
||||
language = _('Serbian')
|
||||
extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} body{font-family: serif1, serif} .article_description{font-family: sans1, sans-serif}'
|
||||
|
||||
html2lrf_options = [
|
||||
'--comment' , description
|
||||
, '--category' , category
|
||||
, '--publisher', publisher
|
||||
, '--ignore-tables'
|
||||
]
|
||||
|
||||
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"'
|
||||
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\nlinearize_tables=True'
|
||||
|
||||
keep_only_tags = [ dict(name='div', attrs={'class':'sama_vest'}) ]
|
||||
|
||||
preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')]
|
||||
|
||||
feeds = [
|
||||
(u'Vesti', u'http://www.b92.net/info/rss/vesti.xml')
|
||||
,(u'Biz' , u'http://www.b92.net/info/rss/biz.xml' )
|
||||
@ -54,9 +56,10 @@ class B92(BasicNewsRecipe):
|
||||
return nurl
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
soup.html['xml:lang'] = 'sr-Latn'
|
||||
soup.html['lang'] = 'sr-Latn'
|
||||
mtag = '<meta http-equiv="Content-Language" content="sr-Latn"/>'
|
||||
lng = 'sr-Latn-RS'
|
||||
soup.html['xml:lang'] = lng
|
||||
soup.html['lang'] = lng
|
||||
mtag = '<meta http-equiv="Content-Language" content="sr-Latn-RS"/>'
|
||||
soup.head.insert(0,mtag)
|
||||
for item in soup.findAll(style=True):
|
||||
del item['style']
|
||||
@ -64,4 +67,3 @@ class B92(BasicNewsRecipe):
|
||||
del item['align']
|
||||
item.insert(0,'<br /><br />')
|
||||
return soup
|
||||
language = _('Serbian')
|
@ -1,13 +1,12 @@
|
||||
#!/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>'
|
||||
'''
|
||||
blic.rs
|
||||
'''
|
||||
|
||||
import re
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class Blic(BasicNewsRecipe):
|
||||
@ -21,15 +20,17 @@ class Blic(BasicNewsRecipe):
|
||||
remove_javascript = True
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} @font-face {font-family: "monospace1";src:url(res:///opt/sony/ebook/FONT/tt0419m_.ttf)} @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} body{text-align: left; font-family: serif1, serif} .article_date{font-family: monospace1, monospace} .article_description{font-family: sans1, sans-serif} .navbar{font-family: monospace1, monospace}'
|
||||
language = _('Serbian')
|
||||
extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} body{font-family: serif1, serif} .article_description{font-family: sans1, sans-serif}'
|
||||
|
||||
html2lrf_options = [
|
||||
'--comment' , description
|
||||
, '--category' , category
|
||||
, '--publisher', publisher
|
||||
, '--ignore-tables'
|
||||
]
|
||||
|
||||
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"'
|
||||
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\nlinearize_tables=True'
|
||||
|
||||
preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')]
|
||||
|
||||
@ -44,10 +45,9 @@ class Blic(BasicNewsRecipe):
|
||||
return u'http://www.blic.rs/_print.php?' + rest_url
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
mtag = '<meta http-equiv="Content-Language" content="sr-Latn"/>'
|
||||
mtag = '<meta http-equiv="Content-Language" content="sr-Latn-RS"/>'
|
||||
soup.head.insert(0,mtag)
|
||||
for item in soup.findAll(style=True):
|
||||
del item['style']
|
||||
return soup
|
||||
|
||||
language = _('Serbian')
|
@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>'
|
||||
'''
|
||||
chicagobreakingnews.com
|
||||
'''
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class ChicagoBreakingNews(BasicNewsRecipe):
|
||||
title = 'Chicago Breaking News'
|
||||
__author__ = 'Darko Miletic'
|
||||
description = 'Breaking News from Chicago'
|
||||
oldest_article = 1
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets = True
|
||||
use_embedded_content = True
|
||||
publisher = 'Chicago Breaking News'
|
||||
category = 'news, politics, USA, Chicago'
|
||||
encoding = 'utf8'
|
||||
language = _('English')
|
||||
|
||||
html2lrf_options = [
|
||||
'--comment', description
|
||||
, '--category', category
|
||||
, '--publisher', publisher
|
||||
]
|
||||
|
||||
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"'
|
||||
|
||||
feeds = [(u'Breaking news', u'http://feeds2.feedburner.com/ChicagoBreakingNews/')]
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
links = soup.findAll('a')
|
||||
for item in soup.findAll('a'):
|
||||
if item['href'].find('http://feedads.googleadservices.com') > -1:
|
||||
item.extract()
|
||||
for item in soup.findAll(style=True):
|
||||
del item['style']
|
||||
for item in soup.findAll(color=True):
|
||||
del item['color']
|
||||
for item in soup.findAll(size=True):
|
||||
del item['size']
|
||||
return soup
|
34
src/calibre/web/feeds/recipes/recipe_cincinnati_enquirer.py
Normal file
34
src/calibre/web/feeds/recipes/recipe_cincinnati_enquirer.py
Normal file
@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env python
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2009 Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class AdvancedUserRecipe1234144423(BasicNewsRecipe):
|
||||
title = u'Cincinnati Enquirer'
|
||||
oldest_article = 7
|
||||
language = _('English')
|
||||
__author__ = 'Joseph Kitzmiller'
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
remove_javascript = True
|
||||
encoding = 'cp1252'
|
||||
extra_css = ' p {font-size: medium; font-weight: normal;} '
|
||||
|
||||
keep_only_tags = [dict(name='div', attrs={'class':'padding'})]
|
||||
|
||||
remove_tags = [
|
||||
dict(name=['object','link','table','embed'])
|
||||
,dict(name='div',attrs={'id':'pluckcomments'})
|
||||
,dict(name='div',attrs={'class':'articleflex-container'})
|
||||
]
|
||||
|
||||
feeds = [(u'Cincinnati Enquirer', u'http://rss.cincinnati.com/apps/pbcs.dll/section?category=rssenq01&mime=xml')]
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
for item in soup.findAll(style=True):
|
||||
del item['style']
|
||||
for item in soup.findAll(face=True):
|
||||
del item['face']
|
||||
return soup
|
@ -1,12 +1,11 @@
|
||||
#!/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>'
|
||||
'''
|
||||
danas.rs
|
||||
'''
|
||||
import re
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class Danas(BasicNewsRecipe):
|
||||
@ -20,15 +19,17 @@ class Danas(BasicNewsRecipe):
|
||||
no_stylesheets = False
|
||||
remove_javascript = True
|
||||
use_embedded_content = False
|
||||
extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} @font-face {font-family: "monospace1";src:url(res:///opt/sony/ebook/FONT/tt0419m_.ttf)} @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} body{text-align: left; font-family: serif1, serif} .article_date{font-family: monospace1, monospace} .article_description{font-family: sans1, sans-serif} .navbar{font-family: monospace1, monospace}'
|
||||
language = _('Serbian')
|
||||
extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} body{font-family: serif1, serif} .article_description{font-family: sans1, sans-serif}'
|
||||
|
||||
html2lrf_options = [
|
||||
'--comment' , description
|
||||
, '--category' , category
|
||||
, '--publisher', publisher
|
||||
, '--ignore-tables'
|
||||
]
|
||||
|
||||
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"'
|
||||
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\nlinearize_tables=True'
|
||||
|
||||
|
||||
preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')]
|
||||
@ -43,9 +44,8 @@ class Danas(BasicNewsRecipe):
|
||||
feeds = [ (u'Vesti', u'http://www.danas.rs/rss/rss.asp')]
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
mtag = '<meta http-equiv="Content-Language" content="sr-Latn"/>'
|
||||
mtag = '<meta http-equiv="Content-Language" content="sr-Latn-RS"/>'
|
||||
soup.head.insert(0,mtag)
|
||||
for item in soup.findAll(style=True):
|
||||
del item['style']
|
||||
return soup
|
||||
language = _('Serbian')
|
@ -60,8 +60,11 @@ class Economist(BasicNewsRecipe):
|
||||
continue
|
||||
a = tag.find('a', href=True)
|
||||
if a is not None:
|
||||
url=a['href'].replace('displaystory', 'PrinterFriendly')
|
||||
if url.startswith('/'):
|
||||
url = 'http://www.economist.com' + url
|
||||
article = dict(title=text,
|
||||
url='http://www.economist.com'+a['href'].replace('displaystory', 'PrinterFriendly'),
|
||||
url = url,
|
||||
description='', content='', date='')
|
||||
feeds[key].append(article)
|
||||
|
||||
|
@ -5,7 +5,6 @@ __copyright__ = '2008-2009, Darko Miletic <darko.miletic at gmail.com>'
|
||||
'''
|
||||
elargentino.com
|
||||
'''
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class ElArgentino(BasicNewsRecipe):
|
||||
@ -21,6 +20,7 @@ class ElArgentino(BasicNewsRecipe):
|
||||
use_embedded_content = False
|
||||
encoding = 'utf8'
|
||||
cover_url = 'http://www.elargentino.com/TemplateWeb/MediosFooter/tapa_elargentino.png'
|
||||
language = _('Spanish')
|
||||
|
||||
html2lrf_options = [
|
||||
'--comment', description
|
||||
@ -59,5 +59,3 @@ class ElArgentino(BasicNewsRecipe):
|
||||
for item in soup.findAll(style=True):
|
||||
del item['style']
|
||||
return soup
|
||||
|
||||
language = _('Spanish')
|
34
src/calibre/web/feeds/recipes/recipe_glasgow_herald.py
Normal file
34
src/calibre/web/feeds/recipes/recipe_glasgow_herald.py
Normal file
@ -0,0 +1,34 @@
|
||||
import re
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class GlasgowHerald(BasicNewsRecipe):
|
||||
title = u'Glasgow Herald'
|
||||
oldest_article = 1
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets = True
|
||||
language = _('English')
|
||||
__author__ = 'McCande'
|
||||
|
||||
preprocess_regexps = [ (re.compile(i[0], re.IGNORECASE | re.DOTALL), i[1]) for i in
|
||||
[
|
||||
(r'<center><h3>', lambda match : '<h3>'),
|
||||
(r'Click here to comment on this story...', lambda match : ''),
|
||||
(r'<h3>Related links</h3>.*?</head>', lambda match : '</head>'),
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
|
||||
|
||||
feeds = [
|
||||
(u'News', u'http://www.theherald.co.uk/news/news/rss.xml'),
|
||||
(u'Politics', u'http://www.theherald.co.uk/politics/news/rss.xml'),
|
||||
(u'Features', u'http://www.theherald.co.uk/features/features/rss.xml'),
|
||||
(u'Business', u'http://www.theherald.co.uk/business/news/rss.xml')]
|
||||
|
||||
def print_version(self, url):
|
||||
(beginning,end)=url.split(".var.")
|
||||
num=end[0:7]
|
||||
main="http://www.theherald.co.uk/misc/print.php?artid="+num
|
||||
return main
|
@ -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>'
|
||||
'''
|
||||
granma.cubaweb.cu
|
||||
'''
|
||||
@ -21,6 +21,7 @@ class Granma(BasicNewsRecipe):
|
||||
use_embedded_content = False
|
||||
encoding = 'cp1252'
|
||||
cover_url = 'http://www.granma.cubaweb.cu/imagenes/granweb229d.jpg'
|
||||
language = _('Spanish')
|
||||
remove_javascript = True
|
||||
|
||||
html2lrf_options = [
|
||||
@ -30,10 +31,12 @@ class Granma(BasicNewsRecipe):
|
||||
, '--ignore-tables'
|
||||
]
|
||||
|
||||
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"'
|
||||
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\nlinearize_tables=True'
|
||||
|
||||
keep_only_tags = [dict(name='table', attrs={'height':'466'})]
|
||||
|
||||
remove_tags = [dict(name=['embed','link','object'])]
|
||||
|
||||
feeds = [(u'Noticias', u'http://www.granma.cubaweb.cu/noticias.xml' )]
|
||||
|
||||
|
||||
@ -49,4 +52,3 @@ class Granma(BasicNewsRecipe):
|
||||
del item['style']
|
||||
return soup
|
||||
|
||||
language = _('Spanish')
|
@ -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>'
|
||||
'''
|
||||
harpers.org
|
||||
'''
|
||||
@ -12,18 +12,29 @@ class Harpers(BasicNewsRecipe):
|
||||
__author__ = u'Darko Miletic'
|
||||
language = _('English')
|
||||
description = u"Harper's Magazine: Founded June 1850."
|
||||
publisher = "Harper's Magazine "
|
||||
category = 'news, politics, USA'
|
||||
oldest_article = 30
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
timefmt = ' [%A, %d %B, %Y]'
|
||||
remove_javascript = True
|
||||
|
||||
html2lrf_options = [
|
||||
'--comment', description
|
||||
, '--category', category
|
||||
, '--publisher', publisher
|
||||
]
|
||||
|
||||
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\noverride_css=" p {text-indent: 0em; margin-top: 0em; margin-bottom: 0.5em} img {margin-top: 0em; margin-bottom: 0.4em}"'
|
||||
|
||||
|
||||
keep_only_tags = [ dict(name='div', attrs={'id':'cached'}) ]
|
||||
remove_tags = [
|
||||
dict(name='table', attrs={'class':'rcnt'})
|
||||
,dict(name='table', attrs={'class':'rcnt topline'})
|
||||
,dict(name=['link','object','embed'])
|
||||
]
|
||||
|
||||
feeds = [
|
||||
(u"Harper's Magazine", u'http://www.harpers.org/rss/frontpage-rss20.xml')
|
||||
]
|
||||
feeds = [(u"Harper's Magazine", u'http://www.harpers.org/rss/frontpage-rss20.xml')]
|
||||
|
||||
|
@ -24,6 +24,7 @@ class Harpers_full(BasicNewsRecipe):
|
||||
use_embedded_content = False
|
||||
simultaneous_downloads = 1
|
||||
delay = 1
|
||||
language = _('English')
|
||||
needs_subscription = True
|
||||
INDEX = strftime('http://www.harpers.org/archive/%Y/%m')
|
||||
LOGIN = 'http://www.harpers.org'
|
||||
@ -36,7 +37,7 @@ class Harpers_full(BasicNewsRecipe):
|
||||
, '--publisher', publisher
|
||||
]
|
||||
|
||||
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"'
|
||||
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\noverride_css=" p {text-indent: 0em; margin-top: 0em; margin-bottom: 0.5em} img {margin-top: 0em; margin-bottom: 0.4em}"'
|
||||
|
||||
keep_only_tags = [ dict(name='div', attrs={'id':'cached'}) ]
|
||||
remove_tags = [
|
||||
@ -72,9 +73,3 @@ class Harpers_full(BasicNewsRecipe):
|
||||
})
|
||||
return [(soup.head.title.string, articles)]
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
for item in soup.findAll(style=True):
|
||||
del item['style']
|
||||
return soup
|
||||
|
||||
language = _('English')
|
47
src/calibre/web/feeds/recipes/recipe_hindu.py
Normal file
47
src/calibre/web/feeds/recipes/recipe_hindu.py
Normal file
@ -0,0 +1,47 @@
|
||||
from __future__ import with_statement
|
||||
__license__ = 'GPL 3'
|
||||
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
|
||||
import re
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class TheHindu(BasicNewsRecipe):
|
||||
title = u'The Hindu'
|
||||
language = _('English')
|
||||
oldest_article = 7
|
||||
__author__ = _('Kovid Goyal')
|
||||
max_articles_per_feed = 100
|
||||
|
||||
remove_tags_before = {'name':'font', 'class':'storyhead'}
|
||||
preprocess_regexps = [
|
||||
(re.compile(r'<!-- story ends -->.*', re.DOTALL),
|
||||
lambda match: '</body></html>'),
|
||||
]
|
||||
|
||||
feeds = [
|
||||
(u'Main - Font Page', u'http://www.hindu.com/rss/01hdline.xml'),
|
||||
(u'Main - National', u'http://www.hindu.com/rss/02hdline.xml'),
|
||||
(u'Main - International', u'http://www.hindu.com/rss/03hdline.xml'),
|
||||
(u'Main - Opinion', u'http://www.hindu.com/rss/05hdline.xml'),
|
||||
(u'Main - Business', u'http://www.hindu.com/rss/06hdline.xml'),
|
||||
(u'Main - Sport', u'http://www.hindu.com/rss/07hdline.xml'),
|
||||
(u'Main - Weather / Religion / Crossword / Cartoon',
|
||||
u'http://www.hindu.com/rss/10hdline.xml'),
|
||||
(u'Main - Engagements', u'http://www.hindu.com/rss/26hdline.xml'),
|
||||
(u'Supplement - Literary Review',
|
||||
u'http://www.hindu.com/rss/lrhdline.xml'),
|
||||
(u'Supplement - Sunday Magazine',
|
||||
u'http://www.hindu.com/rss/maghdline.xml'),
|
||||
(u'Supplement - Open Page', u'http://www.hindu.com/rss/ophdline.xml'),
|
||||
(u'Supplement - Business Review',
|
||||
u'http://www.hindu.com/rss/bizhdline.xml'),
|
||||
(u'Supplement - Book Review',
|
||||
u'http://www.hindu.com/rss/brhdline.xml'),
|
||||
(u'Supplement - Science & Technology',
|
||||
u'http://www.hindu.com/rss/setahdline.xml')
|
||||
]
|
||||
|
||||
def postprocess_html(self, soup, first_fetch):
|
||||
for t in soup.findAll(['table', 'tr', 'td']):
|
||||
t.name = 'div'
|
||||
return soup
|
@ -14,10 +14,11 @@ class Infobae(BasicNewsRecipe):
|
||||
description = 'Informacion Libre las 24 horas'
|
||||
publisher = 'Infobae.com'
|
||||
category = 'news, politics, Argentina'
|
||||
oldest_article = 2
|
||||
oldest_article = 1
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
language = _('Spanish')
|
||||
encoding = 'iso-8859-1'
|
||||
cover_url = 'http://www.infobae.com/imgs/header/header.gif'
|
||||
remove_javascript = True
|
||||
@ -26,9 +27,15 @@ class Infobae(BasicNewsRecipe):
|
||||
'--comment' , description
|
||||
, '--category' , category
|
||||
, '--publisher', publisher
|
||||
, '--ignore-tables'
|
||||
]
|
||||
|
||||
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"'
|
||||
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\nlinearize_tables=True'
|
||||
|
||||
remove_tags = [
|
||||
dict(name=['embed','link','object'])
|
||||
,dict(name='a', attrs={'onclick':'javascript:window.print()'})
|
||||
]
|
||||
|
||||
feeds = [
|
||||
(u'Noticias' , u'http://www.infobae.com/adjuntos/html/RSS/hoy.xml' )
|
||||
@ -48,5 +55,3 @@ class Infobae(BasicNewsRecipe):
|
||||
for item in soup.findAll(style=True):
|
||||
del item['style']
|
||||
return soup
|
||||
|
||||
language = _('Spanish')
|
@ -1,13 +1,12 @@
|
||||
#!/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>'
|
||||
'''
|
||||
jutarnji.hr
|
||||
'''
|
||||
|
||||
import re
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class Jutarnji(BasicNewsRecipe):
|
||||
@ -16,32 +15,32 @@ class Jutarnji(BasicNewsRecipe):
|
||||
description = u'Hrvatski portal'
|
||||
publisher = 'Jutarnji.hr'
|
||||
category = 'news, politics, Croatia'
|
||||
oldest_article = 2
|
||||
oldest_article = 1
|
||||
max_articles_per_feed = 100
|
||||
simultaneous_downloads = 1
|
||||
simultaneous_downloads = 2
|
||||
delay = 1
|
||||
language = _('Croatian')
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
remove_javascript = True
|
||||
encoding = 'cp1250'
|
||||
extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} @font-face {font-family: "monospace1";src:url(res:///opt/sony/ebook/FONT/tt0419m_.ttf)} @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} body{text-align: left; font-family: serif1, serif} .article_date{font-family: monospace1, monospace} .article_description{font-family: sans1, sans-serif} .navbar{font-family: monospace1, monospace}'
|
||||
extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} body{text-align: justify; font-family: serif1, serif} .article_description{font-family: sans1, sans-serif}'
|
||||
|
||||
html2lrf_options = [
|
||||
'--comment' , description
|
||||
, '--category' , category
|
||||
, '--publisher', publisher
|
||||
, '--ignore-tables'
|
||||
]
|
||||
|
||||
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"'
|
||||
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\nlinearize_tables=True'
|
||||
|
||||
|
||||
preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')]
|
||||
|
||||
remove_tags = [
|
||||
dict(name='embed')
|
||||
dict(name=['embed','hr','link','object'])
|
||||
,dict(name='a', attrs={'class':'a11'})
|
||||
,dict(name='hr')
|
||||
]
|
||||
|
||||
feeds = [
|
||||
@ -60,9 +59,7 @@ class Jutarnji(BasicNewsRecipe):
|
||||
return 'http://www.jutarnji.hr/ispis_clanka.jl?artid=' + rrest
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
mtag = '<meta http-equiv="Content-Type" content="text/html; charset=utf-8">'
|
||||
soup.head.insert(0,mtag)
|
||||
mtag = '<meta http-equiv="Content-Language" content="hr"/>'
|
||||
mtag = '<meta http-equiv="Content-Type" content="text/html; charset=utf-8">\n<meta http-equiv="Content-Language" content="hr-HR"/>'
|
||||
soup.head.insert(0,mtag)
|
||||
for item in soup.findAll(style=True):
|
||||
del item['style']
|
||||
|
@ -1,12 +1,12 @@
|
||||
#!/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>'
|
||||
'''
|
||||
juventudrebelde.cu
|
||||
'''
|
||||
from calibre import strftime
|
||||
|
||||
from calibre import strftime
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class Juventudrebelde(BasicNewsRecipe):
|
||||
@ -20,6 +20,7 @@ class Juventudrebelde(BasicNewsRecipe):
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
encoding = 'cp1252'
|
||||
language = _('Spanish')
|
||||
cover_url = strftime('http://www.juventudrebelde.cu/UserFiles/File/impreso/iportada-%Y-%m-%d.jpg')
|
||||
remove_javascript = True
|
||||
|
||||
@ -30,7 +31,7 @@ class Juventudrebelde(BasicNewsRecipe):
|
||||
, '--ignore-tables'
|
||||
]
|
||||
|
||||
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"'
|
||||
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\nlinearize_tables=True'
|
||||
|
||||
keep_only_tags = [dict(name='div', attrs={'id':'noticia'})]
|
||||
|
||||
@ -51,4 +52,3 @@ class Juventudrebelde(BasicNewsRecipe):
|
||||
del item['style']
|
||||
return soup
|
||||
|
||||
language = _('Spanish')
|
28
src/calibre/web/feeds/recipes/recipe_la_republica.py
Normal file
28
src/calibre/web/feeds/recipes/recipe_la_republica.py
Normal file
@ -0,0 +1,28 @@
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class LaRepublica(BasicNewsRecipe):
|
||||
title = u'la Repubblica'
|
||||
oldest_article = 1
|
||||
language = _('Italian')
|
||||
author = 'Darko Miletic'
|
||||
max_articles_per_feed = 100
|
||||
remove_javascript = True
|
||||
no_stylesheets = True
|
||||
|
||||
keep_only_tags = [dict(name='div', attrs={'class':'articolo'})]
|
||||
|
||||
|
||||
remove_tags = [
|
||||
dict(name=['object','link'])
|
||||
,dict(name='span',attrs={'class':'linkindice'})
|
||||
,dict(name='div',attrs={'class':'bottom-mobile'})
|
||||
,dict(name='div',attrs={'id':['rssdiv','blocco']})
|
||||
]
|
||||
|
||||
feeds = [
|
||||
(u'Repubblica homepage', u'http://www.repubblica.it/rss/homepage/rss2.0.xml'),
|
||||
(u'Repubblica Scienze', u'http://www.repubblica.it/rss/scienze/rss2.0.xml'),
|
||||
(u'Repubblica Tecnologia', u'http://www.repubblica.it/rss/tecnologia/rss2.0.xml'),
|
||||
(u'Repubblica Esteri', u'http://www.repubblica.it/rss/esteri/rss2.0.xml')
|
||||
]
|
||||
|
80
src/calibre/web/feeds/recipes/recipe_linuxdevices.py
Normal file
80
src/calibre/web/feeds/recipes/recipe_linuxdevices.py
Normal file
@ -0,0 +1,80 @@
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
'''
|
||||
Fetch Linuxdevices.
|
||||
'''
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
|
||||
class Sueddeutsche(BasicNewsRecipe):
|
||||
|
||||
title = u'Linuxdevices'
|
||||
description = 'News about Linux driven Hardware'
|
||||
__author__ = 'Oliver Niesner'
|
||||
use_embedded_content = False
|
||||
timefmt = ' [%a, %d %b %Y]'
|
||||
language = _('English')
|
||||
max_articles_per_feed = 50
|
||||
no_stylesheets = True
|
||||
encoding = 'latin1'
|
||||
|
||||
remove_tags_after = [dict(id='nointelliTXT')]
|
||||
filter_regexps = [r'ad\.doubleclick\.net']
|
||||
|
||||
|
||||
remove_tags = [dict(name='div', attrs={'class':'bannerSuperBanner'}),
|
||||
dict(name='div', attrs={'class':'bannerSky'}),
|
||||
dict(name='div', attrs={'class':'footerLinks'}),
|
||||
dict(name='div', attrs={'class':'seitenanfang'}),
|
||||
dict(name='td', attrs={'class':'mar5'}),
|
||||
dict(name='td', attrs={'class':'mar5'}),
|
||||
dict(name='table', attrs={'class':'pageAktiv'}),
|
||||
dict(name='table', attrs={'class':'xartable'}),
|
||||
dict(name='table', attrs={'class':'wpnavi'}),
|
||||
dict(name='table', attrs={'class':'bgcontent absatz'}),
|
||||
dict(name='table', attrs={'class':'footer'}),
|
||||
dict(name='table', attrs={'class':'artikelBox'}),
|
||||
dict(name='table', attrs={'class':'kommentare'}),
|
||||
dict(name='table', attrs={'class':'pageBoxBot'}),
|
||||
#dict(name='table', attrs={'with':'100%'}),
|
||||
dict(name='td', attrs={'nowrap':'nowrap'}),
|
||||
dict(name='td', attrs={'valign':'middle'}),
|
||||
dict(name='td', attrs={'align':'left'}),
|
||||
dict(name='td', attrs={'align':'center'}),
|
||||
dict(name='td', attrs={'height':'5'}),
|
||||
dict(name='div', attrs={'class':'artikelBox navigatorBox'}),
|
||||
dict(name='div', attrs={'class':'similar-article-box'}),
|
||||
dict(name='div', attrs={'class':'videoBigHack'}),
|
||||
dict(name='td', attrs={'class':'artikelDruckenRight'}),
|
||||
dict(name='td', attrs={'class':'width="200"'}),
|
||||
dict(name='a', attrs={'href':'/news'}),
|
||||
dict(name='a', attrs={'href':'/'}),
|
||||
dict(name='a', attrs={'href':'/articles'}),
|
||||
dict(name='a', attrs={'href':'/cgi-bin/survey/survey.cgi'}),
|
||||
dict(name='a', attrs={'href':'/cgi-bin/board/UltraBoard.pl'}),
|
||||
dict(name='iframe'),
|
||||
dict(name='form'),
|
||||
#dict(name='tr', attrs={'td':'Click here to learn'}),
|
||||
dict(name='span', attrs={'class':'hidePrint'}),
|
||||
dict(id='headerLBox'),
|
||||
dict(id='nointelliTXT'),
|
||||
dict(id='rechteSpalte'),
|
||||
dict(id='newsticker-list-small'),
|
||||
dict(id='ntop5'),
|
||||
dict(id='ntop5send'),
|
||||
dict(id='ntop5commented'),
|
||||
dict(id='nnav-bgheader'),
|
||||
dict(id='nnav-headerteaser'),
|
||||
dict(id='nnav-head'),
|
||||
dict(id='nnav-top'),
|
||||
dict(id='nnav-logodiv'),
|
||||
dict(id='nnav-logo'),
|
||||
dict(id='nnav-oly'),
|
||||
dict(id='readcomment')]
|
||||
|
||||
|
||||
|
||||
feeds = [ (u'Linuxdevices', u'http://www.linuxdevices.com/backend/headlines.rss') ]
|
||||
|
@ -1,13 +1,12 @@
|
||||
#!/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>'
|
||||
'''
|
||||
nin.co.yu
|
||||
'''
|
||||
|
||||
import re, urllib
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class Nin(BasicNewsRecipe):
|
||||
@ -27,15 +26,17 @@ class Nin(BasicNewsRecipe):
|
||||
LOGIN = PREFIX + '/?logout=true'
|
||||
remove_javascript = True
|
||||
use_embedded_content = False
|
||||
extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} @font-face {font-family: "monospace1";src:url(res:///opt/sony/ebook/FONT/tt0419m_.ttf)} @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} body{text-align: left; font-family: serif1, serif} .article_date{font-family: monospace1, monospace} .article_description{font-family: sans1, sans-serif} .navbar{font-family: monospace1, monospace}'
|
||||
language = _('Serbian')
|
||||
extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} body{text-align: justify; font-family: serif1, serif} .article_description{font-family: sans1, sans-serif}'
|
||||
|
||||
html2lrf_options = [
|
||||
'--comment' , description
|
||||
, '--category' , category
|
||||
, '--publisher', publisher
|
||||
, '--ignore-tables'
|
||||
]
|
||||
|
||||
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"'
|
||||
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\nlinearize_tables=True'
|
||||
|
||||
preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')]
|
||||
|
||||
@ -69,5 +70,3 @@ class Nin(BasicNewsRecipe):
|
||||
for item in soup.findAll(style=True):
|
||||
del item['style']
|
||||
return soup
|
||||
|
||||
language = _('Serbian')
|
@ -1,13 +1,12 @@
|
||||
#!/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>'
|
||||
'''
|
||||
novosti.rs
|
||||
'''
|
||||
|
||||
import re
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class Novosti(BasicNewsRecipe):
|
||||
@ -22,15 +21,17 @@ class Novosti(BasicNewsRecipe):
|
||||
use_embedded_content = False
|
||||
encoding = 'utf8'
|
||||
remove_javascript = True
|
||||
extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} @font-face {font-family: "monospace1";src:url(res:///opt/sony/ebook/FONT/tt0419m_.ttf)} @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} body{text-align: left; font-family: serif1, serif} .article_date{font-family: monospace1, monospace} .article_description{font-family: sans1, sans-serif} .navbar{font-family: monospace1, monospace}'
|
||||
language = _('Serbian')
|
||||
extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} body{font-family: serif1, serif} .article_description{font-family: sans1, sans-serif}'
|
||||
|
||||
html2lrf_options = [
|
||||
'--comment' , description
|
||||
, '--category' , category
|
||||
, '--publisher', publisher
|
||||
, '--ignore-tables'
|
||||
]
|
||||
|
||||
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"'
|
||||
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\nlinearize_tables=True'
|
||||
|
||||
preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')]
|
||||
|
||||
@ -40,10 +41,8 @@ class Novosti(BasicNewsRecipe):
|
||||
feeds = [(u'Vesti', u'http://www.novosti.rs/php/vesti/rss.php')]
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
mtag = '<meta http-equiv="Content-Language" content="sr-Latn"/>'
|
||||
mtag = '<meta http-equiv="Content-Language" content="sr-Latn-RS"/>'
|
||||
soup.head.insert(0,mtag)
|
||||
for item in soup.findAll(style=True):
|
||||
del item['style']
|
||||
return soup
|
||||
|
||||
language = _('Serbian')
|
@ -1,13 +1,12 @@
|
||||
#!/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>'
|
||||
'''
|
||||
nspm.rs
|
||||
'''
|
||||
|
||||
import re
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class Nspm(BasicNewsRecipe):
|
||||
@ -16,14 +15,15 @@ class Nspm(BasicNewsRecipe):
|
||||
description = 'Casopis za politicku teoriju i drustvena istrazivanja'
|
||||
publisher = 'NSPM'
|
||||
category = 'news, politics, Serbia'
|
||||
oldest_article = 7
|
||||
oldest_article = 2
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
INDEX = 'http://www.nspm.rs/?alphabet=l'
|
||||
encoding = 'utf8'
|
||||
remove_javascript = True
|
||||
extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} @font-face {font-family: "monospace1";src:url(res:///opt/sony/ebook/FONT/tt0419m_.ttf)} @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} body{text-align: left; font-family: serif1, serif} .article_date{font-family: monospace1, monospace} .article_description{font-family: sans1, sans-serif} .navbar{font-family: monospace1, monospace}'
|
||||
language = _('Serbian')
|
||||
extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} body{text-align: justify; font-family: serif1, serif} .article_description{font-family: sans1, sans-serif}'
|
||||
|
||||
html2lrf_options = [
|
||||
'--comment' , description
|
||||
@ -32,10 +32,13 @@ class Nspm(BasicNewsRecipe):
|
||||
, '--ignore-tables'
|
||||
]
|
||||
|
||||
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"'
|
||||
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\nlinearize_tables=True'
|
||||
|
||||
preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')]
|
||||
remove_tags = [dict(name='a')]
|
||||
remove_tags = [
|
||||
dict(name=['a','img','link','object','embed'])
|
||||
,dict(name='td', attrs={'class':'buttonheading'})
|
||||
]
|
||||
|
||||
def get_browser(self):
|
||||
br = BasicNewsRecipe.get_browser()
|
||||
@ -48,13 +51,12 @@ class Nspm(BasicNewsRecipe):
|
||||
return url.replace('.html','/stampa.html')
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
soup.html['xml:lang'] = 'sr-Latn-RS'
|
||||
soup.html['lang'] = 'sr-Latn-RS'
|
||||
lng = 'sr-Latn-RS'
|
||||
soup.html['xml:lang'] = lng
|
||||
soup.html['lang'] = lng
|
||||
ftag = soup.find('meta',attrs={'http-equiv':'Content-Language'})
|
||||
if ftag:
|
||||
ftag['content'] = 'sr-Latn-RS'
|
||||
ftag['content'] = lng
|
||||
for item in soup.findAll(style=True):
|
||||
del item['style']
|
||||
return soup
|
||||
|
||||
language = _('Serbian')
|
@ -1,13 +1,12 @@
|
||||
#!/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>'
|
||||
'''
|
||||
pescanik.net
|
||||
'''
|
||||
|
||||
import re
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class Pescanik(BasicNewsRecipe):
|
||||
@ -16,30 +15,32 @@ class Pescanik(BasicNewsRecipe):
|
||||
description = 'Pescanik'
|
||||
publisher = 'Pescanik'
|
||||
category = 'news, politics, Serbia'
|
||||
oldest_article = 7
|
||||
oldest_article = 5
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
remove_javascript = True
|
||||
encoding = 'utf8'
|
||||
extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} @font-face {font-family: "monospace1";src:url(res:///opt/sony/ebook/FONT/tt0419m_.ttf)} @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} body{text-align: left; font-family: serif1, serif} .article_date{font-family: monospace1, monospace} .article_description{font-family: sans1, sans-serif} .navbar{font-family: monospace1, monospace}'
|
||||
cover_url = "http://pescanik.net/templates/ja_teline/images/logo.png"
|
||||
language = _('Serbian')
|
||||
extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} body{font-family: serif1, serif} .article_description{font-family: sans1, sans-serif}'
|
||||
|
||||
html2lrf_options = [
|
||||
'--comment' , description
|
||||
, '--category' , category
|
||||
, '--publisher', publisher
|
||||
, '--ignore-tables'
|
||||
]
|
||||
|
||||
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"'
|
||||
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\nlinearize_tables=True'
|
||||
|
||||
cover_url = "http://pescanik.net/templates/ja_teline/images/logo.png"
|
||||
|
||||
preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')]
|
||||
|
||||
remove_tags = [
|
||||
dict(name='td' , attrs={'class':'buttonheading'})
|
||||
,dict(name='span', attrs={'class':'article_seperator'})
|
||||
,dict(name=['object','link'])
|
||||
,dict(name=['object','link','img','h4','ul'])
|
||||
]
|
||||
|
||||
feeds = [(u'Pescanik Online', u'http://pescanik.net/index.php?option=com_rd_rss&id=12')]
|
||||
@ -54,5 +55,3 @@ class Pescanik(BasicNewsRecipe):
|
||||
for item in soup.findAll(style=True):
|
||||
del item['style']
|
||||
return soup
|
||||
|
||||
language = _('Serbian')
|
39
src/calibre/web/feeds/recipes/recipe_physics_today.py
Normal file
39
src/calibre/web/feeds/recipes/recipe_physics_today.py
Normal file
@ -0,0 +1,39 @@
|
||||
import re
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
from calibre import strftime
|
||||
|
||||
class Physicstoday(BasicNewsRecipe):
|
||||
title = u'Physicstoday'
|
||||
__author__ = 'Hypernova'
|
||||
description = u'Physics Today magazine'
|
||||
publisher = 'American Institute of Physics'
|
||||
category = 'Physics'
|
||||
language = _('English')
|
||||
cover_url = strftime('http://ptonline.aip.org/journals/doc/PHTOAD-home/jrnls/images/medcover%m_%Y.jpg')
|
||||
oldest_article = 30
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
needs_subscription = True
|
||||
remove_javascript = True
|
||||
remove_tags_before = dict(name='h1')
|
||||
remove_tags = [dict(name='div', attrs={'class':'highslide-footer'})]
|
||||
remove_tags = [dict(name='div', attrs={'class':'highslide-header'})]
|
||||
#remove_tags = [dict(name='a', attrs={'class':'highslide'})]
|
||||
preprocess_regexps = [
|
||||
#(re.compile(r'<!--start PHTOAD_tail.jsp -->.*</body>', re.DOTALL|re.IGNORECASE),
|
||||
(re.compile(r'<!-- END ARTICLE and footer section -->.*</body>', re.DOTALL|re.IGNORECASE),
|
||||
lambda match: '</body>'),
|
||||
]
|
||||
|
||||
def get_browser(self):
|
||||
br = BasicNewsRecipe.get_browser()
|
||||
if self.username is not None and self.password is not None:
|
||||
br.open('http://www.physicstoday.org/pt/sso_login.jsp')
|
||||
br.select_form(name='login')
|
||||
br['username'] = self.username
|
||||
br['password'] = self.password
|
||||
br.submit()
|
||||
return br
|
||||
|
||||
feeds = [(u'All', u'http://www.physicstoday.org/feed.xml')]
|
35
src/calibre/web/feeds/recipes/recipe_physics_world.py
Normal file
35
src/calibre/web/feeds/recipes/recipe_physics_world.py
Normal file
@ -0,0 +1,35 @@
|
||||
import re
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class PhysicsWorld(BasicNewsRecipe):
|
||||
title = u'Physicsworld'
|
||||
description = 'News from the world of physics'
|
||||
__author__ = 'Hypernova'
|
||||
language = _('English')
|
||||
oldest_article = 7
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
remove_javascript = True
|
||||
needs_subscription = True
|
||||
remove_tags_before = dict(name='h1')
|
||||
remove_tags_after = [dict(name='div', attrs={'id':'shareThis'})]
|
||||
preprocess_regexps = [
|
||||
(re.compile(r'<div id="shareThis">.*</body>', re.DOTALL|re.IGNORECASE),
|
||||
lambda match: '</body>'),
|
||||
]
|
||||
feeds = [
|
||||
(u'Headlines News', u'http://feeds.feedburner.com/PhysicsWorldNews')
|
||||
]
|
||||
|
||||
def get_browser(self):
|
||||
br = BasicNewsRecipe.get_browser(self)
|
||||
if self.username is not None and self.password is not None:
|
||||
br.open('http://physicsworld.com/cws/sign-in')
|
||||
br.select_form(nr=1)
|
||||
br['username'] = self.username
|
||||
br['password'] = self.password
|
||||
br.submit()
|
||||
return br
|
||||
|
||||
|
@ -17,9 +17,6 @@ class Pobjeda(BasicNewsRecipe):
|
||||
description = 'News from Montenegro'
|
||||
publisher = 'Pobjeda a.d.'
|
||||
category = 'news, politics, Montenegro'
|
||||
language = _('Serbian')
|
||||
oldest_article = 2
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets = True
|
||||
remove_javascript = True
|
||||
encoding = 'utf8'
|
||||
@ -30,11 +27,13 @@ class Pobjeda(BasicNewsRecipe):
|
||||
|
||||
html2lrf_options = [
|
||||
'--comment', description
|
||||
, '--base-font-size', '10'
|
||||
, '--category', category
|
||||
, '--publisher', publisher
|
||||
]
|
||||
|
||||
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"'
|
||||
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\noverride_css=" p {text-indent: 0em; margin-top: 0em; margin-bottom: 0.5em} img {margin-top: 0em; margin-bottom: 0.4em}"'
|
||||
|
||||
|
||||
preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')]
|
||||
|
||||
@ -64,8 +63,6 @@ class Pobjeda(BasicNewsRecipe):
|
||||
soup.html['lang'] = 'sr-Latn-ME'
|
||||
mtag = '<meta http-equiv="Content-Language" content="sr-Latn-ME"/>'
|
||||
soup.head.insert(0,mtag)
|
||||
for item in soup.findAll(style=True):
|
||||
del item['style']
|
||||
return soup
|
||||
|
||||
def get_cover_url(self):
|
||||
@ -87,7 +84,7 @@ class Pobjeda(BasicNewsRecipe):
|
||||
for item in soup.findAll('div', attrs={'class':'vijest'}):
|
||||
description = self.tag_to_string(item.h2)
|
||||
atag = item.h1.find('a')
|
||||
if atag:
|
||||
if atag and atag.has_key('href'):
|
||||
url = self.INDEX + '/' + atag['href']
|
||||
title = self.tag_to_string(atag)
|
||||
date = strftime(self.timefmt)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user