mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-07 18:24:30 -04:00
Implement #1830 (Add Exherbo to http://calibre.kovidgoyal.net/download_linux (Get Calibre / Linux))
This commit is contained in:
parent
e7cc8d990d
commit
08615ee359
@ -1,419 +1,394 @@
|
|||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
__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'
|
__appname__ = 'calibre'
|
||||||
DOWNLOAD_DIR = '/var/www/calibre.kovidgoyal.net/htdocs/downloads'
|
import re
|
||||||
MOBILEREAD = 'https://dev.mobileread.com/dist/kovid/calibre/'
|
|
||||||
|
|
||||||
class OS(dict):
|
DEPENDENCIES = [
|
||||||
"""Dictionary with a default value for unknown keys."""
|
#(Generic, version, gentoo, ubuntu, fedora)
|
||||||
def __init__(self, dict):
|
('python', '2.5', None, None, None),
|
||||||
self.update(dict)
|
('setuptools', '0.6c5', 'setuptools', 'python-setuptools', 'python-setuptools-devel'),
|
||||||
if not dict.has_key('img'):
|
('Python Imaging Library', '1.1.6', 'imaging', 'python-imaging', 'python-imaging'),
|
||||||
self['img'] = self['name']
|
('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'),
|
||||||
|
('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'),
|
||||||
|
('lxml', '2.0.5', 'lxml', 'python-lxml', 'python-lxml'),
|
||||||
|
('python-dateutil', '1.4.1', 'python-dateutil', 'python-dateutil', 'python-dateutil'),
|
||||||
|
('BeautifulSoup', '3.0.5', 'beautifulsoup', 'python-beautifulsoup', 'python-BeautifulSoup'),
|
||||||
|
('help2man', '1.36.4', 'help2man', 'help2man', 'help2man'),
|
||||||
|
]
|
||||||
|
|
||||||
class Distribution(object):
|
|
||||||
|
|
||||||
DEPENDENCIES = [
|
class CoolDistro:
|
||||||
#(Generic, version, gentoo, ubuntu, fedora)
|
|
||||||
('python', '2.5', None, None, None),
|
|
||||||
('setuptools', '0.6c5', 'setuptools', 'python-setuptools', 'python-setuptools-devel'),
|
|
||||||
('Python Imaging Library', '1.1.6', 'imaging', 'python-imaging', 'python-imaging'),
|
|
||||||
('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'),
|
|
||||||
('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'),
|
|
||||||
('lxml', '2.0.5', 'lxml', 'python-lxml', 'python-lxml'),
|
|
||||||
('python-dateutil', '1.4.1', 'python-dateutil', 'python-dateutil', 'python-dateutil'),
|
|
||||||
('BeautifulSoup', '3.0.5', 'beautifulsoup', 'python-beautifulsoup', 'python-BeautifulSoup'),
|
|
||||||
('help2man', '1.36.4', 'help2man', 'help2man', 'help2man'),
|
|
||||||
]
|
|
||||||
|
|
||||||
DISTRO_MAP = {'gentoo':2, 'ubuntu':3, 'fedora':4, 'debian':3}
|
def __init__(self, name, title, prefix=''):
|
||||||
|
self.title = title
|
||||||
|
url = prefix + '/chrome/dl/images/%s_logo.png'
|
||||||
|
self.img = url%name
|
||||||
|
|
||||||
INSTALLERS = ('emerge -avn', 'apt-get install', 'yum install')
|
def get_linux_data(version='1.0.0'):
|
||||||
AS_ROOT = (True, False, True)
|
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
|
||||||
|
|
||||||
TITLEMAP = {'gentoo':'Gentoo', 'ubuntu':'Ubuntu Intrepid Ibex',
|
if __name__ == '__main__':
|
||||||
'fedora':'Fedora 10', 'debian':'Debian sid', 'generic': 'Install from source'}
|
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/download.html').read()
|
||||||
|
return MarkupTemplate(raw).generate(**get_linux_data()).render('xhtml')
|
||||||
|
index.exposed = True
|
||||||
|
t = Test()
|
||||||
|
t.index()
|
||||||
|
cherrypy.quickstart(t)
|
||||||
|
else:
|
||||||
|
from pkg_resources import resource_filename
|
||||||
|
|
||||||
MANUAL_MAP = {
|
from trac.core import Component, implements
|
||||||
'fedora' : '''<li>You have to upgrade Qt to at least 4.4.0 and PyQt to at least 4.4.2</li>''',
|
from trac.web.chrome import INavigationContributor, ITemplateProvider, add_stylesheet
|
||||||
}
|
from trac.web.main import IRequestHandler
|
||||||
|
from trac.util import Markup
|
||||||
|
|
||||||
def __init__(self, os):
|
|
||||||
self.os = os
|
|
||||||
self.img = os
|
DOWNLOAD_DIR = '/var/www/calibre.kovidgoyal.net/htdocs/downloads'
|
||||||
self.title = self.TITLEMAP[os]
|
MOBILEREAD = 'https://dev.mobileread.com/dist/kovid/calibre/'
|
||||||
self.app = __appname__
|
|
||||||
self.is_generic = os == 'generic'
|
class OS(dict):
|
||||||
offset = 0
|
"""Dictionary with a default value for unknown keys."""
|
||||||
if not self.is_generic:
|
def __init__(self, dict):
|
||||||
index = self.DISTRO_MAP[self.os]
|
self.update(dict)
|
||||||
if os == 'debian':
|
if not dict.has_key('img'):
|
||||||
self.as_root = True
|
self['img'] = self['name']
|
||||||
else: self.as_root = self.AS_ROOT[index-2]
|
|
||||||
prefix = ''
|
|
||||||
if not self.as_root: prefix = 'sudo '
|
class Download(Component):
|
||||||
cmd = prefix + self.INSTALLERS[index-2]
|
implements(INavigationContributor, IRequestHandler, ITemplateProvider)
|
||||||
pre = ' \\\n '.ljust(len(cmd)+4)
|
|
||||||
for dep in self.DEPENDENCIES:
|
request_pat = re.compile(r'\/download$|\/download_\S+')
|
||||||
if len(cmd) > 70+offset:
|
|
||||||
offset += 70
|
# INavigationContributor methods
|
||||||
cmd += pre
|
def get_active_navigation_item(self, req):
|
||||||
cmd += ' '
|
return 'download'
|
||||||
if dep[index]: cmd += dep[index]
|
|
||||||
self.command = cmd.strip()
|
def get_navigation_items(self, req):
|
||||||
easy_install = 'easy_install'
|
yield 'mainnav', 'download', Markup('<a href="/download">Get %s</a>'%(__appname__,))
|
||||||
if os == 'debian':
|
|
||||||
easy_install = 'easy_install-2.5'
|
def get_templates_dirs(self):
|
||||||
self.command += '\n'+prefix+easy_install+' -U calibre \n'+prefix+'calibre_postinstall'
|
return [resource_filename(__name__, 'templates')]
|
||||||
|
|
||||||
|
def get_htdocs_dirs(self):
|
||||||
|
return [('dl', resource_filename(__name__, 'htdocs'))]
|
||||||
|
|
||||||
|
# IRequestHandler methods
|
||||||
|
def match_request(self, req):
|
||||||
|
return self.__class__.request_pat.match(req.path_info)
|
||||||
|
|
||||||
|
def process_request(self, req):
|
||||||
|
add_stylesheet(req, 'dl/css/download.css')
|
||||||
|
if req.path_info == '/download':
|
||||||
|
return self.top_level(req)
|
||||||
|
elif req.path_info == '/download_linux_binary_installer':
|
||||||
|
req.send(LINUX_INSTALLER.replace('%version', self.version_from_filename()), 'text/x-python')
|
||||||
|
else:
|
||||||
|
match = re.match(r'\/download_(\S+)', req.path_info)
|
||||||
|
if match:
|
||||||
|
os = match.group(1)
|
||||||
|
if os == 'windows':
|
||||||
|
return self.windows(req)
|
||||||
|
elif os == 'osx':
|
||||||
|
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 top_level(self, req):
|
||||||
|
operating_systems = [
|
||||||
|
OS({'name' : 'windows', 'title' : 'Windows'}),
|
||||||
|
OS({'name' : 'osx', 'title' : 'OS X'}),
|
||||||
|
OS({'name' : 'linux', 'title' : 'Linux'}),
|
||||||
|
]
|
||||||
|
data = dict(title='Get ' + __appname__,
|
||||||
|
operating_systems=operating_systems, width=200,
|
||||||
|
font_size='xx-large', top_level=True)
|
||||||
|
return 'download.html', data, None
|
||||||
|
|
||||||
|
def version_from_filename(self):
|
||||||
try:
|
try:
|
||||||
self.manual = Markup(self.MANUAL_MAP[os])
|
return open(DOWNLOAD_DIR+'/latest_version', 'rb').read().strip()
|
||||||
except KeyError:
|
except:
|
||||||
self.manual = None
|
return '0.0.0'
|
||||||
else:
|
|
||||||
self.img = 'linux'
|
def windows(self, req):
|
||||||
|
version = self.version_from_filename()
|
||||||
|
file = '%s-%s.exe'%(__appname__, version,)
|
||||||
|
data = dict(version = version, name='windows',
|
||||||
|
installer_name='Windows installer',
|
||||||
|
title='Download %s for windows'%(__appname__),
|
||||||
|
compatibility='%s works on Windows XP and Windows Vista.'%(__appname__,),
|
||||||
|
path=MOBILEREAD+file, app=__appname__,
|
||||||
|
note=Markup(\
|
||||||
|
'''
|
||||||
|
<p>If you are using the <b>SONY PRS-500</b> and %(appname)s does not detect your reader, read on:</p>
|
||||||
|
<blockquote>
|
||||||
|
<p>
|
||||||
|
If you are using 64-bit windows, you're out of luck.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
There may be a conflict with the USB driver from SONY. In windows, you cannot install two drivers
|
||||||
|
for one device. In order to resolve the conflict:
|
||||||
|
<ol>
|
||||||
|
<li>Start Device Manager by clicking Start->Run, typing devmgmt.msc and pressing enter.</li>
|
||||||
|
<li>Uninstall all PRS500 related drivers. You will find them in two locations:
|
||||||
|
<ul>
|
||||||
|
<li>Under "Libusb-Win32"</li>
|
||||||
|
<li>Under "Universal Serial ..." (... depends on the version of windows)</li>
|
||||||
|
</ul>
|
||||||
|
You can uninstall a driver by right clicking on it and selecting uninstall.
|
||||||
|
</li>
|
||||||
|
<li>Once the drivers have been uninstalled, find the file prs500.inf (it will be in the
|
||||||
|
driver folder in the folder in which you installed %(appname)s. Right click on it and
|
||||||
|
select Install.</li>
|
||||||
|
</ol>
|
||||||
|
</p>
|
||||||
|
</blockquote>
|
||||||
|
'''%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,)
|
||||||
|
data = dict(version = version, name='osx',
|
||||||
|
installer_name='OS X universal dmg',
|
||||||
|
title='Download %s for OS X'%(__appname__),
|
||||||
|
compatibility='%s works on OS X Tiger and above.'%(__appname__,),
|
||||||
|
path=MOBILEREAD+file, app=__appname__,
|
||||||
|
note=Markup(\
|
||||||
|
'''
|
||||||
|
<ol>
|
||||||
|
<li>Before trying to use the command line tools, you must run the app at least once. This will ask you for you password and then setup the symbolic links for the command line tools.</li>
|
||||||
|
<li>The app cannot be run from within the dmg. You must drag it to a folder on your filesystem (The Desktop, Applications, wherever).</li>
|
||||||
|
<li>In order for localization of the user interface in your language, select your language in the configuration dialog (by clicking the hammer icon next to the search bar) and select your language.</li>
|
||||||
|
</ol>
|
||||||
|
'''))
|
||||||
|
return 'binary.html', data, None
|
||||||
|
|
||||||
|
def linux(self, req):
|
||||||
|
data = get_linux_data(version=self.version_from_filename())
|
||||||
|
return 'download.html', data, None
|
||||||
|
|
||||||
|
|
||||||
class Download(Component):
|
LINUX_INSTALLER = r'''
|
||||||
implements(INavigationContributor, IRequestHandler, ITemplateProvider)
|
import sys, os, shutil, tarfile, subprocess, tempfile, urllib2, re, stat
|
||||||
|
|
||||||
request_pat = re.compile(r'\/download$|\/download_\S+')
|
MOBILEREAD='https://dev.mobileread.com/dist/kovid/calibre/'
|
||||||
|
|
||||||
# INavigationContributor methods
|
class TerminalController:
|
||||||
def get_active_navigation_item(self, req):
|
BOL = '' #: Move the cursor to the beginning of the line
|
||||||
return 'download'
|
UP = '' #: Move the cursor up one line
|
||||||
|
DOWN = '' #: Move the cursor down one line
|
||||||
|
LEFT = '' #: Move the cursor left one char
|
||||||
|
RIGHT = '' #: Move the cursor right one char
|
||||||
|
|
||||||
def get_navigation_items(self, req):
|
# Deletion:
|
||||||
yield 'mainnav', 'download', Markup('<a href="/download">Get %s</a>'%(__appname__,))
|
CLEAR_SCREEN = '' #: Clear the screen and move to home position
|
||||||
|
CLEAR_EOL = '' #: Clear to the end of the line.
|
||||||
|
CLEAR_BOL = '' #: Clear to the beginning of the line.
|
||||||
|
CLEAR_EOS = '' #: Clear to the end of the screen
|
||||||
|
|
||||||
def get_templates_dirs(self):
|
# Output modes:
|
||||||
return [resource_filename(__name__, 'templates')]
|
BOLD = '' #: Turn on bold mode
|
||||||
|
BLINK = '' #: Turn on blink mode
|
||||||
|
DIM = '' #: Turn on half-bright mode
|
||||||
|
REVERSE = '' #: Turn on reverse-video mode
|
||||||
|
NORMAL = '' #: Turn off all modes
|
||||||
|
|
||||||
def get_htdocs_dirs(self):
|
# Cursor display:
|
||||||
return [('dl', resource_filename(__name__, 'htdocs'))]
|
HIDE_CURSOR = '' #: Make the cursor invisible
|
||||||
|
SHOW_CURSOR = '' #: Make the cursor visible
|
||||||
|
|
||||||
# IRequestHandler methods
|
# Terminal size:
|
||||||
def match_request(self, req):
|
COLS = None #: Width of the terminal (None for unknown)
|
||||||
return self.__class__.request_pat.match(req.path_info)
|
LINES = None #: Height of the terminal (None for unknown)
|
||||||
|
|
||||||
def process_request(self, req):
|
# Foreground colors:
|
||||||
add_stylesheet(req, 'dl/css/download.css')
|
BLACK = BLUE = GREEN = CYAN = RED = MAGENTA = YELLOW = WHITE = ''
|
||||||
if req.path_info == '/download':
|
|
||||||
return self.top_level(req)
|
|
||||||
elif req.path_info == '/download_linux_binary_installer':
|
|
||||||
req.send(LINUX_INSTALLER.replace('%version', self.version_from_filename()), 'text/x-python')
|
|
||||||
else:
|
|
||||||
match = re.match(r'\/download_(\S+)', req.path_info)
|
|
||||||
if match:
|
|
||||||
os = match.group(1)
|
|
||||||
if os == 'windows':
|
|
||||||
return self.windows(req)
|
|
||||||
elif os == 'osx':
|
|
||||||
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):
|
# Background colors:
|
||||||
version = self.version_from_filename()
|
BG_BLACK = BG_BLUE = BG_GREEN = BG_CYAN = ''
|
||||||
distro = Distribution(os)
|
BG_RED = BG_MAGENTA = BG_YELLOW = BG_WHITE = ''
|
||||||
data = dict(distro=distro,title=distro.title, version=version)
|
|
||||||
return 'distro.html', data, None
|
|
||||||
|
|
||||||
def top_level(self, req):
|
_STRING_CAPABILITIES = """
|
||||||
operating_systems = [
|
BOL=cr UP=cuu1 DOWN=cud1 LEFT=cub1 RIGHT=cuf1
|
||||||
OS({'name' : 'windows', 'title' : 'Windows'}),
|
CLEAR_SCREEN=clear CLEAR_EOL=el CLEAR_BOL=el1 CLEAR_EOS=ed BOLD=bold
|
||||||
OS({'name' : 'osx', 'title' : 'OS X'}),
|
BLINK=blink DIM=dim REVERSE=rev UNDERLINE=smul NORMAL=sgr0
|
||||||
OS({'name' : 'linux', 'title' : 'Linux'}),
|
HIDE_CURSOR=cinvis SHOW_CURSOR=cnorm""".split()
|
||||||
]
|
_COLORS = """BLACK BLUE GREEN CYAN RED MAGENTA YELLOW WHITE""".split()
|
||||||
data = dict(title='Get ' + __appname__,
|
_ANSICOLORS = "BLACK RED GREEN YELLOW BLUE MAGENTA CYAN WHITE".split()
|
||||||
operating_systems=operating_systems, width=200,
|
|
||||||
font_size='xx-large', top_level=True)
|
|
||||||
return 'download.html', data, None
|
|
||||||
|
|
||||||
def version_from_filename(self):
|
def __init__(self, term_stream=sys.stdout):
|
||||||
|
# Curses isn't available on all platforms
|
||||||
|
try: import curses
|
||||||
|
except: return
|
||||||
|
|
||||||
|
# If the stream isn't a tty, then assume it has no capabilities.
|
||||||
|
if not hasattr(term_stream, 'isatty') or not term_stream.isatty(): return
|
||||||
|
|
||||||
|
# Check the terminal type. If we fail, then assume that the
|
||||||
|
# terminal has no capabilities.
|
||||||
|
try: curses.setupterm()
|
||||||
|
except: return
|
||||||
|
|
||||||
|
# Look up numeric capabilities.
|
||||||
|
self.COLS = curses.tigetnum('cols')
|
||||||
|
self.LINES = curses.tigetnum('lines')
|
||||||
|
|
||||||
|
# Look up string capabilities.
|
||||||
|
for capability in self._STRING_CAPABILITIES:
|
||||||
|
(attrib, cap_name) = capability.split('=')
|
||||||
|
setattr(self, attrib, self._tigetstr(cap_name) or '')
|
||||||
|
|
||||||
|
# Colors
|
||||||
|
set_fg = self._tigetstr('setf')
|
||||||
|
if set_fg:
|
||||||
|
for i,color in zip(range(len(self._COLORS)), self._COLORS):
|
||||||
|
setattr(self, color, curses.tparm(set_fg, i) or '')
|
||||||
|
set_fg_ansi = self._tigetstr('setaf')
|
||||||
|
if set_fg_ansi:
|
||||||
|
for i,color in zip(range(len(self._ANSICOLORS)), self._ANSICOLORS):
|
||||||
|
setattr(self, color, curses.tparm(set_fg_ansi, i) or '')
|
||||||
|
set_bg = self._tigetstr('setb')
|
||||||
|
if set_bg:
|
||||||
|
for i,color in zip(range(len(self._COLORS)), self._COLORS):
|
||||||
|
setattr(self, 'BG_'+color, curses.tparm(set_bg, i) or '')
|
||||||
|
set_bg_ansi = self._tigetstr('setab')
|
||||||
|
if set_bg_ansi:
|
||||||
|
for i,color in zip(range(len(self._ANSICOLORS)), self._ANSICOLORS):
|
||||||
|
setattr(self, 'BG_'+color, curses.tparm(set_bg_ansi, i) or '')
|
||||||
|
|
||||||
|
def _tigetstr(self, cap_name):
|
||||||
|
# String capabilities can include "delays" of the form "$<2>".
|
||||||
|
# For any modern terminal, we should be able to just ignore
|
||||||
|
# these, so strip them out.
|
||||||
|
import curses
|
||||||
|
cap = curses.tigetstr(cap_name) or ''
|
||||||
|
return re.sub(r'\$<\d+>[/*]?', '', cap)
|
||||||
|
|
||||||
|
def render(self, template):
|
||||||
|
return re.sub(r'\$\$|\${\w+}', self._render_sub, template)
|
||||||
|
|
||||||
|
def _render_sub(self, match):
|
||||||
|
s = match.group()
|
||||||
|
if s == '$$': return s
|
||||||
|
else: return getattr(self, s[2:-1])
|
||||||
|
|
||||||
|
class ProgressBar:
|
||||||
|
BAR = '%3d%% ${GREEN}[${BOLD}%s%s${NORMAL}${GREEN}]${NORMAL}\n'
|
||||||
|
HEADER = '${BOLD}${CYAN}%s${NORMAL}\n\n'
|
||||||
|
|
||||||
|
def __init__(self, term, header):
|
||||||
|
self.term = term
|
||||||
|
if not (self.term.CLEAR_EOL and self.term.UP and self.term.BOL):
|
||||||
|
raise ValueError("Terminal isn't capable enough -- you "
|
||||||
|
"should use a simpler progress dispaly.")
|
||||||
|
self.width = self.term.COLS or 75
|
||||||
|
self.bar = term.render(self.BAR)
|
||||||
|
self.header = self.term.render(self.HEADER % header.center(self.width))
|
||||||
|
self.cleared = 1 #: true if we haven't drawn the bar yet.
|
||||||
|
|
||||||
|
def update(self, percent, message=''):
|
||||||
|
if isinstance(message, unicode):
|
||||||
|
message = message.encode('utf-8', 'ignore')
|
||||||
|
if self.cleared:
|
||||||
|
sys.stdout.write(self.header)
|
||||||
|
self.cleared = 0
|
||||||
|
n = int((self.width-10)*percent)
|
||||||
|
msg = message.center(self.width)
|
||||||
|
sys.stdout.write(
|
||||||
|
self.term.BOL + self.term.UP + self.term.CLEAR_EOL +
|
||||||
|
(self.bar % (100*percent, '='*n, '-'*(self.width-10-n))) +
|
||||||
|
self.term.CLEAR_EOL + msg)
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
if not self.cleared:
|
||||||
|
sys.stdout.write(self.term.BOL + self.term.CLEAR_EOL +
|
||||||
|
self.term.UP + self.term.CLEAR_EOL +
|
||||||
|
self.term.UP + self.term.CLEAR_EOL)
|
||||||
|
self.cleared = 1
|
||||||
|
|
||||||
|
def download_tarball():
|
||||||
try:
|
try:
|
||||||
return open(DOWNLOAD_DIR+'/latest_version', 'rb').read().strip()
|
pb = ProgressBar(TerminalController(sys.stdout), 'Downloading calibre...')
|
||||||
except:
|
except ValueError:
|
||||||
return '0.0.0'
|
print 'Downloading calibre...'
|
||||||
|
pb = None
|
||||||
def windows(self, req):
|
local = 'calibre-test.tar.bz2'
|
||||||
version = self.version_from_filename()
|
src = open(local) if os.access(local, os.R_OK) else urllib2.urlopen(MOBILEREAD+'calibre-%version-i686.tar.bz2')
|
||||||
file = '%s-%s.exe'%(__appname__, version,)
|
if hasattr(src, 'info'):
|
||||||
data = dict(version = version, name='windows',
|
size = int(src.info()['content-length'])
|
||||||
installer_name='Windows installer',
|
|
||||||
title='Download %s for windows'%(__appname__),
|
|
||||||
compatibility='%s works on Windows XP and Windows Vista.'%(__appname__,),
|
|
||||||
path=MOBILEREAD+file, app=__appname__,
|
|
||||||
note=Markup(\
|
|
||||||
'''
|
|
||||||
<p>If you are using the <b>SONY PRS-500</b> and %(appname)s does not detect your reader, read on:</p>
|
|
||||||
<blockquote>
|
|
||||||
<p>
|
|
||||||
If you are using 64-bit windows, you're out of luck.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
There may be a conflict with the USB driver from SONY. In windows, you cannot install two drivers
|
|
||||||
for one device. In order to resolve the conflict:
|
|
||||||
<ol>
|
|
||||||
<li>Start Device Manager by clicking Start->Run, typing devmgmt.msc and pressing enter.</li>
|
|
||||||
<li>Uninstall all PRS500 related drivers. You will find them in two locations:
|
|
||||||
<ul>
|
|
||||||
<li>Under "Libusb-Win32"</li>
|
|
||||||
<li>Under "Universal Serial ..." (... depends on the version of windows)</li>
|
|
||||||
</ul>
|
|
||||||
You can uninstall a driver by right clicking on it and selecting uninstall.
|
|
||||||
</li>
|
|
||||||
<li>Once the drivers have been uninstalled, find the file prs500.inf (it will be in the
|
|
||||||
driver folder in the folder in which you installed %(appname)s. Right click on it and
|
|
||||||
select Install.</li>
|
|
||||||
</ol>
|
|
||||||
</p>
|
|
||||||
</blockquote>
|
|
||||||
'''%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,)
|
|
||||||
data = dict(version = version, name='osx',
|
|
||||||
installer_name='OS X universal dmg',
|
|
||||||
title='Download %s for OS X'%(__appname__),
|
|
||||||
compatibility='%s works on OS X Tiger and above.'%(__appname__,),
|
|
||||||
path=MOBILEREAD+file, app=__appname__,
|
|
||||||
note=Markup(\
|
|
||||||
'''
|
|
||||||
<ol>
|
|
||||||
<li>Before trying to use the command line tools, you must run the app at least once. This will ask you for you password and then setup the symbolic links for the command line tools.</li>
|
|
||||||
<li>The app cannot be run from within the dmg. You must drag it to a folder on your filesystem (The Desktop, Applications, wherever).</li>
|
|
||||||
<li>In order for localization of the user interface in your language, select your language in the configuration dialog (by clicking the hammer icon next to the search bar) and select your language.</li>
|
|
||||||
</ol>
|
|
||||||
'''))
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
LINUX_INSTALLER = r'''
|
|
||||||
import sys, os, shutil, tarfile, subprocess, tempfile, urllib2, re, stat
|
|
||||||
|
|
||||||
MOBILEREAD='https://dev.mobileread.com/dist/kovid/calibre/'
|
|
||||||
|
|
||||||
class TerminalController:
|
|
||||||
BOL = '' #: Move the cursor to the beginning of the line
|
|
||||||
UP = '' #: Move the cursor up one line
|
|
||||||
DOWN = '' #: Move the cursor down one line
|
|
||||||
LEFT = '' #: Move the cursor left one char
|
|
||||||
RIGHT = '' #: Move the cursor right one char
|
|
||||||
|
|
||||||
# Deletion:
|
|
||||||
CLEAR_SCREEN = '' #: Clear the screen and move to home position
|
|
||||||
CLEAR_EOL = '' #: Clear to the end of the line.
|
|
||||||
CLEAR_BOL = '' #: Clear to the beginning of the line.
|
|
||||||
CLEAR_EOS = '' #: Clear to the end of the screen
|
|
||||||
|
|
||||||
# Output modes:
|
|
||||||
BOLD = '' #: Turn on bold mode
|
|
||||||
BLINK = '' #: Turn on blink mode
|
|
||||||
DIM = '' #: Turn on half-bright mode
|
|
||||||
REVERSE = '' #: Turn on reverse-video mode
|
|
||||||
NORMAL = '' #: Turn off all modes
|
|
||||||
|
|
||||||
# Cursor display:
|
|
||||||
HIDE_CURSOR = '' #: Make the cursor invisible
|
|
||||||
SHOW_CURSOR = '' #: Make the cursor visible
|
|
||||||
|
|
||||||
# Terminal size:
|
|
||||||
COLS = None #: Width of the terminal (None for unknown)
|
|
||||||
LINES = None #: Height of the terminal (None for unknown)
|
|
||||||
|
|
||||||
# Foreground colors:
|
|
||||||
BLACK = BLUE = GREEN = CYAN = RED = MAGENTA = YELLOW = WHITE = ''
|
|
||||||
|
|
||||||
# Background colors:
|
|
||||||
BG_BLACK = BG_BLUE = BG_GREEN = BG_CYAN = ''
|
|
||||||
BG_RED = BG_MAGENTA = BG_YELLOW = BG_WHITE = ''
|
|
||||||
|
|
||||||
_STRING_CAPABILITIES = """
|
|
||||||
BOL=cr UP=cuu1 DOWN=cud1 LEFT=cub1 RIGHT=cuf1
|
|
||||||
CLEAR_SCREEN=clear CLEAR_EOL=el CLEAR_BOL=el1 CLEAR_EOS=ed BOLD=bold
|
|
||||||
BLINK=blink DIM=dim REVERSE=rev UNDERLINE=smul NORMAL=sgr0
|
|
||||||
HIDE_CURSOR=cinvis SHOW_CURSOR=cnorm""".split()
|
|
||||||
_COLORS = """BLACK BLUE GREEN CYAN RED MAGENTA YELLOW WHITE""".split()
|
|
||||||
_ANSICOLORS = "BLACK RED GREEN YELLOW BLUE MAGENTA CYAN WHITE".split()
|
|
||||||
|
|
||||||
def __init__(self, term_stream=sys.stdout):
|
|
||||||
# Curses isn't available on all platforms
|
|
||||||
try: import curses
|
|
||||||
except: return
|
|
||||||
|
|
||||||
# If the stream isn't a tty, then assume it has no capabilities.
|
|
||||||
if not hasattr(term_stream, 'isatty') or not term_stream.isatty(): return
|
|
||||||
|
|
||||||
# Check the terminal type. If we fail, then assume that the
|
|
||||||
# terminal has no capabilities.
|
|
||||||
try: curses.setupterm()
|
|
||||||
except: return
|
|
||||||
|
|
||||||
# Look up numeric capabilities.
|
|
||||||
self.COLS = curses.tigetnum('cols')
|
|
||||||
self.LINES = curses.tigetnum('lines')
|
|
||||||
|
|
||||||
# Look up string capabilities.
|
|
||||||
for capability in self._STRING_CAPABILITIES:
|
|
||||||
(attrib, cap_name) = capability.split('=')
|
|
||||||
setattr(self, attrib, self._tigetstr(cap_name) or '')
|
|
||||||
|
|
||||||
# Colors
|
|
||||||
set_fg = self._tigetstr('setf')
|
|
||||||
if set_fg:
|
|
||||||
for i,color in zip(range(len(self._COLORS)), self._COLORS):
|
|
||||||
setattr(self, color, curses.tparm(set_fg, i) or '')
|
|
||||||
set_fg_ansi = self._tigetstr('setaf')
|
|
||||||
if set_fg_ansi:
|
|
||||||
for i,color in zip(range(len(self._ANSICOLORS)), self._ANSICOLORS):
|
|
||||||
setattr(self, color, curses.tparm(set_fg_ansi, i) or '')
|
|
||||||
set_bg = self._tigetstr('setb')
|
|
||||||
if set_bg:
|
|
||||||
for i,color in zip(range(len(self._COLORS)), self._COLORS):
|
|
||||||
setattr(self, 'BG_'+color, curses.tparm(set_bg, i) or '')
|
|
||||||
set_bg_ansi = self._tigetstr('setab')
|
|
||||||
if set_bg_ansi:
|
|
||||||
for i,color in zip(range(len(self._ANSICOLORS)), self._ANSICOLORS):
|
|
||||||
setattr(self, 'BG_'+color, curses.tparm(set_bg_ansi, i) or '')
|
|
||||||
|
|
||||||
def _tigetstr(self, cap_name):
|
|
||||||
# String capabilities can include "delays" of the form "$<2>".
|
|
||||||
# For any modern terminal, we should be able to just ignore
|
|
||||||
# these, so strip them out.
|
|
||||||
import curses
|
|
||||||
cap = curses.tigetstr(cap_name) or ''
|
|
||||||
return re.sub(r'\$<\d+>[/*]?', '', cap)
|
|
||||||
|
|
||||||
def render(self, template):
|
|
||||||
return re.sub(r'\$\$|\${\w+}', self._render_sub, template)
|
|
||||||
|
|
||||||
def _render_sub(self, match):
|
|
||||||
s = match.group()
|
|
||||||
if s == '$$': return s
|
|
||||||
else: return getattr(self, s[2:-1])
|
|
||||||
|
|
||||||
class ProgressBar:
|
|
||||||
BAR = '%3d%% ${GREEN}[${BOLD}%s%s${NORMAL}${GREEN}]${NORMAL}\n'
|
|
||||||
HEADER = '${BOLD}${CYAN}%s${NORMAL}\n\n'
|
|
||||||
|
|
||||||
def __init__(self, term, header):
|
|
||||||
self.term = term
|
|
||||||
if not (self.term.CLEAR_EOL and self.term.UP and self.term.BOL):
|
|
||||||
raise ValueError("Terminal isn't capable enough -- you "
|
|
||||||
"should use a simpler progress dispaly.")
|
|
||||||
self.width = self.term.COLS or 75
|
|
||||||
self.bar = term.render(self.BAR)
|
|
||||||
self.header = self.term.render(self.HEADER % header.center(self.width))
|
|
||||||
self.cleared = 1 #: true if we haven't drawn the bar yet.
|
|
||||||
|
|
||||||
def update(self, percent, message=''):
|
|
||||||
if isinstance(message, unicode):
|
|
||||||
message = message.encode('utf-8', 'ignore')
|
|
||||||
if self.cleared:
|
|
||||||
sys.stdout.write(self.header)
|
|
||||||
self.cleared = 0
|
|
||||||
n = int((self.width-10)*percent)
|
|
||||||
msg = message.center(self.width)
|
|
||||||
sys.stdout.write(
|
|
||||||
self.term.BOL + self.term.UP + self.term.CLEAR_EOL +
|
|
||||||
(self.bar % (100*percent, '='*n, '-'*(self.width-10-n))) +
|
|
||||||
self.term.CLEAR_EOL + msg)
|
|
||||||
sys.stdout.flush()
|
|
||||||
|
|
||||||
def clear(self):
|
|
||||||
if not self.cleared:
|
|
||||||
sys.stdout.write(self.term.BOL + self.term.CLEAR_EOL +
|
|
||||||
self.term.UP + self.term.CLEAR_EOL +
|
|
||||||
self.term.UP + self.term.CLEAR_EOL)
|
|
||||||
self.cleared = 1
|
|
||||||
|
|
||||||
def download_tarball():
|
|
||||||
try:
|
|
||||||
pb = ProgressBar(TerminalController(sys.stdout), 'Downloading calibre...')
|
|
||||||
except ValueError:
|
|
||||||
print 'Downloading calibre...'
|
|
||||||
pb = None
|
|
||||||
local = 'calibre-test.tar.bz2'
|
|
||||||
src = open(local) if os.access(local, os.R_OK) else urllib2.urlopen(MOBILEREAD+'calibre-%version-i686.tar.bz2')
|
|
||||||
if hasattr(src, 'info'):
|
|
||||||
size = int(src.info()['content-length'])
|
|
||||||
else:
|
|
||||||
src.seek(0, 2)
|
|
||||||
size = src.tell()
|
|
||||||
src.seek(0)
|
|
||||||
f = tempfile.NamedTemporaryFile()
|
|
||||||
while f.tell() < size:
|
|
||||||
f.write(src.read(4*1024))
|
|
||||||
percent = f.tell()/float(size)
|
|
||||||
if pb is not None:
|
|
||||||
pb.update(percent)
|
|
||||||
else:
|
else:
|
||||||
print '%d%%, '%int(percent*100),
|
src.seek(0, 2)
|
||||||
f.seek(0)
|
size = src.tell()
|
||||||
return f
|
src.seek(0)
|
||||||
|
f = tempfile.NamedTemporaryFile()
|
||||||
|
while f.tell() < size:
|
||||||
|
f.write(src.read(4*1024))
|
||||||
|
percent = f.tell()/float(size)
|
||||||
|
if pb is not None:
|
||||||
|
pb.update(percent)
|
||||||
|
else:
|
||||||
|
print '%d%%, '%int(percent*100),
|
||||||
|
f.seek(0)
|
||||||
|
return f
|
||||||
|
|
||||||
def extract_tarball(tar, destdir):
|
def extract_tarball(tar, destdir):
|
||||||
print 'Extracting application files...'
|
print 'Extracting application files...'
|
||||||
if hasattr(tar, 'read'):
|
if hasattr(tar, 'read'):
|
||||||
try:
|
try:
|
||||||
tarfile.open(fileobj=tar, mode='r').extractall(destdir)
|
tarfile.open(fileobj=tar, mode='r').extractall(destdir)
|
||||||
except: # tarfile.py on Fedora 9 is buggy
|
except: # tarfile.py on Fedora 9 is buggy
|
||||||
subprocess.check_call(['tar', 'xjf', tar.name, '-C', destdir])
|
subprocess.check_call(['tar', 'xjf', tar.name, '-C', destdir])
|
||||||
else:
|
else:
|
||||||
tarfile.open(tar, 'r').extractall(destdir)
|
tarfile.open(tar, 'r').extractall(destdir)
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
defdir = '/opt/calibre'
|
defdir = '/opt/calibre'
|
||||||
destdir = raw_input('Enter the installation directory for calibre (Its contents will be deleted!)[%s]: '%defdir).strip()
|
destdir = raw_input('Enter the installation directory for calibre (Its contents will be deleted!)[%s]: '%defdir).strip()
|
||||||
if not destdir:
|
if not destdir:
|
||||||
destdir = defdir
|
destdir = defdir
|
||||||
destdir = os.path.abspath(destdir)
|
destdir = os.path.abspath(destdir)
|
||||||
if os.path.exists(destdir):
|
if os.path.exists(destdir):
|
||||||
shutil.rmtree(destdir)
|
shutil.rmtree(destdir)
|
||||||
os.makedirs(destdir)
|
os.makedirs(destdir)
|
||||||
|
|
||||||
f = download_tarball()
|
f = download_tarball()
|
||||||
|
|
||||||
|
print 'Extracting files to %s ...'%destdir
|
||||||
|
extract_tarball(f, destdir)
|
||||||
|
pi = os.path.join(destdir, 'calibre_postinstall')
|
||||||
|
subprocess.call(pi, shell=True)
|
||||||
|
return 0
|
||||||
|
'''
|
||||||
|
|
||||||
print 'Extracting files to %s ...'%destdir
|
|
||||||
extract_tarball(f, destdir)
|
|
||||||
pi = os.path.join(destdir, 'calibre_postinstall')
|
|
||||||
subprocess.call(pi, shell=True)
|
|
||||||
return 0
|
|
||||||
'''
|
|
||||||
|
@ -6,36 +6,142 @@
|
|||||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||||
<xi:include href="layout.html" />
|
<xi:include href="layout.html" />
|
||||||
<head>
|
<head>
|
||||||
<title>$title</title>
|
<title>${title}</title>
|
||||||
|
<style type="text/css">
|
||||||
|
.distro_type {
|
||||||
|
font-family: monospace;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: larger;
|
||||||
|
text-align: center;
|
||||||
|
border-bottom: solid 1pt black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right {
|
||||||
|
margin-left:2em;
|
||||||
|
border-left: solid 1pt black;
|
||||||
|
}
|
||||||
|
|
||||||
|
table#info {
|
||||||
|
border-collapse: collapse;
|
||||||
|
border-width: 1pt;
|
||||||
|
border-style: solid;
|
||||||
|
table-layout: fixed;
|
||||||
|
width: 80%;
|
||||||
|
}
|
||||||
|
table#dependencies {
|
||||||
|
border-collapse: collapse;
|
||||||
|
border-width: 0pt;
|
||||||
|
font-family:monospace;
|
||||||
|
}
|
||||||
|
.tdata {vertical-align: top;}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div id="ctxtnav" class="nav"></div>
|
<div id="ctxtnav" class="nav"></div>
|
||||||
<p py:if="not top_level">If you have a 64bit computer, or an outdated operating system, the distribution
|
|
||||||
specific installation may not work for you. In that case, first try the binary installer.
|
|
||||||
If that also does not work, your only recourse is to try to install from source. In order
|
|
||||||
to do that you will have to install all the dependencies as well as the -dev package of PyQt4.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div id="content" class="download">
|
<div id="content" class="download">
|
||||||
<h1>$title</h1>
|
<h1>${title}</h1>
|
||||||
<center>
|
<p>
|
||||||
<table cellspacing="40px">
|
The latest release of ${app} is ${version}. See the
|
||||||
<tr>
|
<a href="/wiki/Changelog">Changelog</a> for a list of new features.
|
||||||
<td style="text-align:center; font-size: ${font_size}" py:for="os in operating_systems">
|
</p>
|
||||||
<a href="download_${os['name']}" style="text-decoration:none; border:0;">
|
<p>
|
||||||
<img width="${width}" height="${width+20}" src="${href.chrome('/dl/images/%s_logo.png'%(os['img'],))}" />
|
${app} is available in the software repositories of the following
|
||||||
</a>
|
linux distributions:
|
||||||
<a href="download_${os['name']}" style="text-decoration:none; border:0">
|
<table id="info">
|
||||||
<br />
|
<col width="150" /><col width="*" />
|
||||||
${os['title']}
|
<tr class="distro_type">
|
||||||
</a>
|
<td class="left">Supported distributions</td>
|
||||||
</td>
|
<td class="right">Unsupported distributions</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
<tr class="tdata">
|
||||||
<br />
|
<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>
|
||||||
|
</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-----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-----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 style="margin-right:2em">Package</th><th>Version</th>
|
||||||
|
</tr>
|
||||||
|
|
||||||
</center>
|
<tr class="dependency" py:for="dep in dependencies">
|
||||||
|
<td style="margin-right:2em">${dep[0]}</td><td>${dep[1]}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -58,7 +58,7 @@ class Checker(object):
|
|||||||
"specific sections. You must explicitly pass "
|
"specific sections. You must explicitly pass "
|
||||||
"application config via "
|
"application config via "
|
||||||
"cherrypy.tree.mount(..., config=app_config)")
|
"cherrypy.tree.mount(..., config=app_config)")
|
||||||
warnings.warn(msg)
|
warnings.warn(msg[:5])
|
||||||
return
|
return
|
||||||
|
|
||||||
def check_static_paths(self):
|
def check_static_paths(self):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user