Update to 0.4.72 from trunk

This commit is contained in:
Kovid Goyal 2008-06-16 14:09:33 -07:00
commit cd1340871c
29 changed files with 680 additions and 530 deletions

View File

@ -1,8 +1,10 @@
PYTHON = python
all : plugins pictureflow gui2 translations resources
all : plugins gui2 translations resources
plugins:
plugins : src/calibre/plugins pictureflow
src/calibre/plugins:
mkdir -p src/calibre/plugins
clean :

153
linux_installer.py Normal file
View File

@ -0,0 +1,153 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en'
'''
Create linux binary.
'''
import glob, sys, subprocess, tarfile, os, re
HOME = '/home/kovid'
PYINSTALLER = os.path.expanduser('~/build/pyinstaller')
CALIBREPREFIX = '___'
CLIT = '/usr/bin/clit'
PDFTOHTML = '/usr/bin/pdftohtml'
LIBUNRAR = '/usr/lib/libunrar.so'
QTDIR = '/usr/lib/qt4'
QTDLLS = ('QtCore', 'QtGui', 'QtNetwork', 'QtSvg', 'QtXml')
EXTRAS = ('/usr/lib/python2.5/site-packages/PIL', os.path.expanduser('~/ipython/IPython'))
CALIBRESRC = os.path.join(CALIBREPREFIX, 'src')
CALIBREPLUGINS = os.path.join(CALIBRESRC, 'calibre', 'plugins')
sys.path.insert(0, CALIBRESRC)
from calibre import __version__
def run_pyinstaller(args=sys.argv):
subprocess.check_call(('/usr/bin/sudo', 'chown', '-R', 'kovid:users', glob.glob('/usr/lib/python*/site-packages/')[-1]))
subprocess.check_call('rm -rf %(py)s/dist/* %(py)s/build/*'%dict(py=PYINSTALLER), shell=True)
subprocess.check_call('make plugins', shell=True)
cp = HOME+'/build/'+os.path.basename(os.getcwd())
spec = open(os.path.join(PYINSTALLER, 'calibre', 'calibre.spec'), 'wb')
raw = re.sub(r'CALIBREPREFIX\s+=\s+\'___\'', 'CALIBREPREFIX = '+repr(cp),
open(__file__).read())
spec.write(raw)
spec.close()
os.chdir(PYINSTALLER)
subprocess.check_call('python -OO Build.py calibre/calibre.spec', shell=True)
return 0
if __name__ == '__main__' and 'linux_installer.py' in __file__:
sys.exit(run_pyinstaller())
loader = os.path.join(os.path.expanduser('~/temp'), 'calibre_installer_loader.py')
if not os.path.exists(loader):
open(loader, 'wb').write('''
import sys, os
sys.frozen_path = os.getcwd()
os.chdir(os.environ.get("ORIGWD", "."))
sys.path.insert(0, os.path.join(sys.frozen_path, "library.pyz"))
sys.path.insert(0, sys.frozen_path)
from PyQt4.QtCore import QCoreApplication
QCoreApplication.setLibraryPaths([sys.frozen_path, os.path.join(sys.frozen_path, "plugins")])
''')
excludes = ['gtk._gtk', 'gtk.glade', 'qt', 'matplotlib.nxutils', 'matplotlib._cntr',
'matplotlib.ttconv', 'matplotlib._image', 'matplotlib.ft2font',
'matplotlib._transforms', 'matplotlib._agg', 'matplotlib.backends._backend_agg',
'matplotlib.axes', 'matplotlib', 'matplotlib.pyparsing',
'TKinter', 'atk', 'gobject._gobject', 'pango', 'PIL', 'Image', 'IPython']
temp = ['keyword', 'codeop']
recipes = ['calibre', 'web', 'feeds', 'recipes']
prefix = '.'.join(recipes)+'.'
for f in glob.glob(os.path.join(CALIBRESRC, *(recipes+['*.py']))):
temp.append(prefix + os.path.basename(f).partition('.')[0])
hook = os.path.expanduser('~/temp/hook-calibre.py')
f = open(hook, 'wb')
hook_script = 'hiddenimports = %s'%repr(temp)
f.write(hook_script)
sys.path.insert(0, CALIBRESRC)
from calibre.linux import entry_points
executables, scripts = ['calibre_postinstall', 'parallel'], \
[os.path.join(CALIBRESRC, 'calibre', 'linux.py'), os.path.join(CALIBRESRC, 'calibre', 'parallel.py')]
for entry in entry_points['console_scripts'] + entry_points['gui_scripts']:
fields = entry.split('=')
executables.append(fields[0].strip())
scripts.append(os.path.join(CALIBRESRC, *map(lambda x: x.strip(), fields[1].split(':')[0].split('.')))+'.py')
recipes = Analysis(glob.glob(os.path.join(CALIBRESRC, 'calibre', 'web', 'feeds', 'recipes', '*.py')),
pathex=[CALIBRESRC], hookspath=[os.path.dirname(hook)], excludes=excludes)
analyses = [Analysis([os.path.join(HOMEPATH,'support/_mountzlib.py'), os.path.join(HOMEPATH,'support/useUnicode.py'), loader, script],
pathex=[PYINSTALLER, CALIBRESRC, CALIBREPLUGINS], excludes=excludes) for script in scripts]
pyz = TOC()
binaries = TOC()
for a in analyses:
pyz = a.pure + pyz
binaries = a.binaries + binaries
pyz = PYZ(pyz + recipes.pure, name='library.pyz')
built_executables = []
for script, exe, a in zip(scripts, executables, analyses):
built_executables.append(EXE(PYZ(TOC()),
a.scripts+[('O','','OPTION'),],
exclude_binaries=1,
name=os.path.join('buildcalibre', exe),
debug=False,
strip=True,
upx=False,
excludes=excludes,
console=1))
print 'Adding plugins...'
for f in glob.glob(os.path.join(CALIBREPLUGINS, '*.so')):
binaries += [(os.path.basename(f), f, 'BINARY')]
print 'Adding external programs...'
binaries += [('clit', CLIT, 'BINARY'), ('pdftohtml', PDFTOHTML, 'BINARY'),
('libunrar.so', LIBUNRAR, 'BINARY')]
qt = []
for dll in QTDLLS:
path = os.path.join(QTDIR, 'lib'+dll+'.so.4')
qt.append((os.path.basename(path), path, 'BINARY'))
binaries += qt
plugins = []
plugdir = os.path.join(QTDIR, 'plugins')
for dirpath, dirnames, filenames in os.walk(plugdir):
for f in filenames:
if not f.endswith('.so') or 'designer' in dirpath or 'codcs' in dirpath or 'sqldrivers' in dirpath : continue
f = os.path.join(dirpath, f)
plugins.append(('plugins/'+f.replace(plugdir, ''), f, 'BINARY'))
binaries += plugins
manifest = '/tmp/manifest'
open(manifest, 'wb').write('\\n'.join(executables))
version = '/tmp/version'
open(version, 'wb').write(__version__)
coll = COLLECT(binaries, pyz,
[('manifest', manifest, 'DATA'), ('version', version, 'DATA')],
*built_executables,
**dict(strip=True,
upx=False,
excludes=excludes,
name='dist'))
os.chdir(os.path.join(HOMEPATH, 'calibre', 'dist'))
for folder in EXTRAS:
subprocess.check_call('cp -rf %s .'%folder, shell=True)
print 'Building tarball...'
tbz2 = 'calibre-%s-i686.tar.bz2'%__version__
tf = tarfile.open(os.path.join('/tmp', tbz2), 'w:bz2')
for f in os.listdir('.'):
tf.add(f)

View File

@ -99,8 +99,7 @@ _check_symlinks_prescript()
includes=list(self.includes) + main_modules['console'],
packages=self.packages,
excludes=self.excludes,
debug=debug,
)
debug=debug)
@classmethod
def makedmg(cls, d, volname,
@ -249,6 +248,11 @@ _check_symlinks_prescript()
else:
os.link(src, os.path.join(module_dir, dest))
print
print 'Adding IPython'
dst = os.path.join(resource_dir, 'lib', 'python2.5', 'IPython')
if os.path.exists(dst): shutil.rmtree(dst)
shutil.copytree(os.path.expanduser('~/build/ipython/IPython'), dst)
print
print 'Installing prescipt'
sf = [os.path.basename(s) for s in all_names]
cs = BuildAPP.CHECK_SYMLINKS_PRESCRIPT % dict(dest_path=repr('/usr/bin'),
@ -277,13 +281,7 @@ sys.frameworks_dir = os.path.join(os.path.dirname(os.environ['RESOURCEPATH']), '
def main():
# auto = '--auto' in sys.argv
# if auto:
# sys.argv.remove('--auto')
# if auto and not os.path.exists('dist/auto'):
# print '%s does not exist'%os.path.abspath('dist/auto')
# return 1
#
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src'))
sys.argv[1:2] = ['py2app']
setup(
name = APPNAME,
@ -300,12 +298,12 @@ def main():
'PyQt4.QtSvg',
'mechanize', 'ClientForm', 'usbobserver',
'genshi', 'calibre.web.feeds.recipes.*',
'IPython.Extensions.*', 'pydoc'],
'keyword', 'codeop', 'pydoc'],
'packages' : ['PIL', 'Authorization', 'rtf2xml', 'lxml'],
'excludes' : [],
'excludes' : ['IPython'],
'plist' : { 'CFBundleGetInfoString' : '''calibre, an E-book management application.'''
''' Visit http://calibre.kovidgoyal.net for details.''',
'CFBundleIdentifier':'net.kovidgoyal.librs500',
'CFBundleIdentifier':'net.kovidgoyal.calibre',
'CFBundleShortVersionString':VERSION,
'CFBundleVersion':APPNAME + ' ' + VERSION,
'LSMinimumSystemVersion':'10.4.3',

View File

@ -1,7 +1,7 @@
''' E-book management software'''
__license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
__version__ = '0.4.70'
__version__ = '0.4.72'
__docformat__ = "epytext"
__author__ = "Kovid Goyal <kovid at kovidgoyal.net>"
__appname__ = 'calibre'
@ -447,14 +447,13 @@ class Settings(QSettings):
self.setValue(str(key), QVariant(QByteArray(val)))
_settings = Settings()
if not _settings.get('rationalized'):
__settings = Settings(name='calibre')
dbpath = os.path.join(os.path.expanduser('~'), 'library1.db').decode(sys.getfilesystemencoding())
dbpath = unicode(__settings.value('database path',
QVariant(QString.fromUtf8(dbpath.encode('utf-8')))).toString())
cmdline = __settings.value('LRF conversion defaults', QVariant(QByteArray(''))).toByteArray().data()
_settings.set('database path', dbpath)
if cmdline:
cmdline = cPickle.loads(cmdline)
@ -464,6 +463,7 @@ if not _settings.get('rationalized'):
os.unlink(unicode(__settings.fileName()))
except:
pass
_settings.set('database path', dbpath)
_spat = re.compile(r'^the\s+|^a\s+|^an\s+', re.IGNORECASE)
def english_sort(x, y):

View File

@ -275,10 +275,10 @@ class PRS505(Device):
if not iswindows:
if self._main_prefix is not None:
stats = os.statvfs(self._main_prefix)
msz = stats.f_bsize * stats.f_bavail
msz = stats.f_frsize * stats.f_bavail
if self._card_prefix is not None:
stats = os.statvfs(self._card_prefix)
csz = stats.f_bsize * stats.f_bavail
csz = stats.f_frsize * stats.f_bavail
else:
msz = self._windows_space(self._main_prefix)[1]
csz = self._windows_space(self._card_prefix)[1]

View File

@ -12,7 +12,7 @@ from PyQt4.QtGui import QDialog, QItemSelectionModel
from calibre.gui2.dialogs.fetch_metadata_ui import Ui_FetchMetadata
from calibre.gui2 import error_dialog, NONE, info_dialog
from calibre.ebooks.metadata.isbndb import create_books, option_parser
from calibre.ebooks.metadata.isbndb import create_books, option_parser, ISBNDBError
from calibre import Settings
class Matches(QAbstractTableModel):
@ -88,8 +88,7 @@ class FetchMetadata(QDialog, Ui_FetchMetadata):
self.connect(self.matches, SIGNAL('activated(QModelIndex)'), self.chosen)
key = str(self.key.text())
if key:
QTimer.singleShot(100, self.fetch.click)
QTimer.singleShot(100, self.fetch_metadata)
def show_summary(self, current, previous):
@ -106,7 +105,7 @@ class FetchMetadata(QDialog, Ui_FetchMetadata):
_('You must specify a valid access key for isbndb.com'))
return
else:
Settings().set('isbndb.com key', str(self.key.text()))
Settings().set('isbndb.com key', key)
args = ['isbndb']
if self.isbn:
@ -121,36 +120,41 @@ class FetchMetadata(QDialog, Ui_FetchMetadata):
self.fetch.setEnabled(False)
self.setCursor(Qt.WaitCursor)
QCoreApplication.instance().processEvents()
try:
args.append(key)
parser = option_parser()
opts, args = parser.parse_args(args)
args.append(key)
parser = option_parser()
opts, args = parser.parse_args(args)
self.logger = logging.getLogger('Job #'+str(id))
self.logger.setLevel(logging.DEBUG)
self.log_dest = cStringIO.StringIO()
handler = logging.StreamHandler(self.log_dest)
handler.setLevel(logging.DEBUG)
handler.setFormatter(logging.Formatter('[%(levelname)s] %(filename)s:%(lineno)s: %(message)s'))
self.logger.addHandler(handler)
self.logger = logging.getLogger('Job #'+str(id))
self.logger.setLevel(logging.DEBUG)
self.log_dest = cStringIO.StringIO()
handler = logging.StreamHandler(self.log_dest)
handler.setLevel(logging.DEBUG)
handler.setFormatter(logging.Formatter('[%(levelname)s] %(filename)s:%(lineno)s: %(message)s'))
self.logger.addHandler(handler)
try:
books = create_books(opts, args, self.logger, self.timeout)
except ISBNDBError, err:
error_dialog(self, _('Error fetching metadata'), str(err)).exec_()
return
books = create_books(opts, args, self.logger, self.timeout)
self.model = Matches(books)
if self.model.rowCount() < 1:
info_dialog(self, _('No metadata found'), _('No metadata found, try adjusting the title and author or the ISBN key.')).exec_()
self.reject()
self.model = Matches(books)
if self.model.rowCount() < 1:
info_dialog(self, _('No metadata found'), _('No metadata found, try adjusting the title and author or the ISBN key.')).exec_()
self.reject()
self.matches.setModel(self.model)
QObject.connect(self.matches.selectionModel(), SIGNAL('currentRowChanged(QModelIndex, QModelIndex)'),
self.show_summary)
self.model.reset()
self.matches.selectionModel().select(self.model.index(0, 0),
QItemSelectionModel.Select | QItemSelectionModel.Rows)
self.matches.setCurrentIndex(self.model.index(0, 0))
self.fetch.setEnabled(True)
self.unsetCursor()
self.matches.resizeColumnsToContents()
self.matches.setModel(self.model)
QObject.connect(self.matches.selectionModel(), SIGNAL('currentRowChanged(QModelIndex, QModelIndex)'),
self.show_summary)
self.model.reset()
self.matches.selectionModel().select(self.model.index(0, 0),
QItemSelectionModel.Select | QItemSelectionModel.Rows)
self.matches.setCurrentIndex(self.model.index(0, 0))
finally:
self.fetch.setEnabled(True)
self.unsetCursor()
self.matches.resizeColumnsToContents()

View File

@ -47,7 +47,7 @@
<item>
<widget class="QLabel" name="label_2" >
<property name="text" >
<string>&amp;Access Key;</string>
<string>&amp;Access Key:</string>
</property>
<property name="buddy" >
<cstring>key</cstring>

View File

@ -6,7 +6,7 @@ add/remove formats
'''
import os
from PyQt4.QtCore import SIGNAL, QObject, QCoreApplication, Qt, QVariant
from PyQt4.QtCore import SIGNAL, QObject, QCoreApplication, Qt
from PyQt4.QtGui import QPixmap, QListWidgetItem, QErrorMessage, QDialog

View File

@ -8,8 +8,8 @@ from math import cos, sin, pi
from itertools import repeat
from PyQt4.QtGui import QTableView, QProgressDialog, QAbstractItemView, QColor, \
QItemDelegate, QPainterPath, QLinearGradient, QBrush, \
QPen, QStyle, QPainter, QLineEdit, QApplication, \
QPalette, QImage
QPen, QStyle, QPainter, QLineEdit, \
QPalette, QImage, QApplication
from PyQt4.QtCore import QAbstractTableModel, QVariant, Qt, QString, \
QCoreApplication, SIGNAL, QObject, QSize, QModelIndex
@ -54,9 +54,11 @@ class LibraryDelegate(QItemDelegate):
painter.restore()
painter.save()
if hasattr(QStyle, 'CE_ItemViewItem'):
QApplication.style().drawControl(QStyle.CE_ItemViewItem, option, painter)
elif option.state & QStyle.State_Selected:
painter.fillRect(option.rect, option.palette.highlight())
try:
if option.state & QStyle.State_Selected:
painter.fillRect(option.rect, option.palette.highlight())
painter.setRenderHint(QPainter.Antialiasing)
y = option.rect.center().y()-self.SIZE/2.
x = option.rect.right() - self.SIZE

View File

@ -4,7 +4,7 @@ import os, sys, textwrap, collections, traceback, time
from xml.parsers.expat import ExpatError
from functools import partial
from PyQt4.QtCore import Qt, SIGNAL, QObject, QCoreApplication, \
QVariant, QThread, QSize, QUrl
QVariant, QThread, QUrl, QSize
from PyQt4.QtGui import QPixmap, QColor, QPainter, QMenu, QIcon, QMessageBox, \
QToolButton, QDialog, QDesktopServices
from PyQt4.QtSvg import QSvgRenderer
@ -1210,4 +1210,14 @@ def main(args=sys.argv):
if __name__ == '__main__':
sys.exit(main())
try:
sys.exit(main())
except:
if not iswindows: raise
from PyQt4.QtGui import QErrorMessage
logfile = os.path.expanduser('~/calibre.log')
if os.path.exists(logfile):
log = open(logfile).read()
if log.strip():
d = QErrorMessage()
d.showMessage(log)

View File

@ -27,9 +27,9 @@
<property name="geometry" >
<rect>
<x>0</x>
<y>86</y>
<y>79</y>
<width>865</width>
<height>712</height>
<height>716</height>
</rect>
</property>
<layout class="QGridLayout" >
@ -44,7 +44,7 @@
<item>
<widget class="LocationView" name="location_view" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Minimum" hsizetype="Expanding" >
<sizepolicy vsizetype="Expanding" hsizetype="Expanding" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
@ -52,19 +52,25 @@
<property name="maximumSize" >
<size>
<width>10000</width>
<height>100</height>
<height>110</height>
</size>
</property>
<property name="verticalScrollBarPolicy" >
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="horizontalScrollBarPolicy" >
<enum>Qt::ScrollBarAlwaysOff</enum>
<enum>Qt::ScrollBarAsNeeded</enum>
</property>
<property name="tabKeyNavigation" >
<bool>true</bool>
</property>
<property name="showDropIndicator" stdset="0" >
<bool>true</bool>
</property>
<property name="iconSize" >
<size>
<width>32</width>
<height>32</height>
<width>40</width>
<height>40</height>
</size>
</property>
<property name="movement" >
@ -77,14 +83,11 @@
<bool>false</bool>
</property>
<property name="spacing" >
<number>20</number>
<number>10</number>
</property>
<property name="viewMode" >
<enum>QListView::IconMode</enum>
</property>
<property name="uniformItemSizes" >
<bool>true</bool>
</property>
</widget>
</item>
<item>
@ -332,8 +335,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>847</width>
<height>553</height>
<width>857</width>
<height>552</height>
</rect>
</property>
<layout class="QGridLayout" >
@ -380,7 +383,7 @@
<x>0</x>
<y>0</y>
<width>865</width>
<height>86</height>
<height>79</height>
</rect>
</property>
<property name="minimumSize" >
@ -425,9 +428,9 @@
<property name="geometry" >
<rect>
<x>0</x>
<y>798</y>
<y>795</y>
<width>865</width>
<height>24</height>
<height>27</height>
</rect>
</property>
<property name="mouseTracking" >

View File

@ -1,4 +1,4 @@
import os, sys, glob
import os, sys, glob, shutil
import sipconfig
if os.environ.get('PYQT4PATH', None):
print os.environ['PYQT4PATH']
@ -37,7 +37,7 @@ makefile = pyqtconfig.QtGuiModuleMakefile (
# ".dll" extension on Windows).
if 'linux' in sys.platform:
for f in glob.glob('../../.build/libpictureflow.a'):
os.link(f, './'+os.path.basename(f))
shutil.copyfile(f, os.path.basename(f))
makefile.extra_lib_dirs = ['.']
else:
makefile.extra_lib_dirs = ['..\\..\\.build\\release', '../../.build', '.']

View File

@ -7,9 +7,9 @@ import re, os
from PyQt4.QtGui import QListView, QIcon, QFont, QLabel, QListWidget, \
QListWidgetItem, QTextCharFormat, QApplication, \
QSyntaxHighlighter, QCursor, QColor, QWidget, \
QAbstractItemDelegate, QPixmap
from PyQt4.QtCore import QAbstractListModel, QVariant, Qt, QRect, SIGNAL, \
QObject, QRegExp, QRectF
QAbstractItemDelegate, QPixmap, QStyle, QFontMetrics
from PyQt4.QtCore import QAbstractListModel, QVariant, Qt, SIGNAL, \
QObject, QRegExp, QString
from calibre.gui2.jobs import DetailView
from calibre.gui2 import human_readable, NONE, TableView, qstring_to_unicode, error_dialog
@ -123,40 +123,43 @@ class LocationDelegate(QAbstractItemDelegate):
def __init__(self):
QAbstractItemDelegate.__init__(self)
self.icon_rect = QRect(0, 10, 150, 45)
self.buffer = 5
self.pixmap = QPixmap(40, 40)
self.text = QString('Reader\n999.9 MB Available202')
def get_rects(self, index, option):
row = index.row()
irect = QRect(self.icon_rect)
irect.translate(row*(irect.width()+self.buffer), 0)
trect = irect.translated(0, irect.height())
trect.adjust(0, 7, 0, 0)
return irect.adjusted(50, 0, -50, 0), trect
def rects(self, option):
style = QApplication.style()
font = QFont(option.font)
font.setBold(True)
irect = style.itemPixmapRect(option.rect, Qt.AlignHCenter|Qt.AlignTop, self.pixmap)
trect = style.itemTextRect(QFontMetrics(font), option.rect,
Qt.AlignHCenter|Qt.AlignTop, True, self.text)
trect.moveTop(irect.bottom())
return irect, trect
def sizeHint(self, option, index):
irect, trect = self.get_rects(index, option)
irect, trect = self.rects(option)
return irect.united(trect).size()
def paint(self, painter, option, index):
font = QFont()
font.setPointSize(9)
icon = QIcon(index.model().data(index, Qt.DecorationRole))
highlight = getattr(index.model(), 'highlight_row', -1) == index.row()
text = index.model().data(index, Qt.DisplayRole).toString()
style = QApplication.style()
painter.save()
irect, trect = self.get_rects(index, option)
mode = QIcon.Normal
if highlight:
font.setBold(True)
mode = QIcon.Active
if hasattr(QStyle, 'CE_ItemViewItem'):
QApplication.style().drawControl(QStyle.CE_ItemViewItem, option, painter)
highlight = getattr(index.model(), 'highlight_row', -1) == index.row()
mode = QIcon.Active if highlight else QIcon.Normal
pixmap = QIcon(index.model().data(index, Qt.DecorationRole)).pixmap(self.pixmap.size())
pixmap = style.generatedIconPixmap(mode, pixmap, option)
text = index.model().data(index, Qt.DisplayRole).toString()
irect, trect = self.rects(option)
style.drawItemPixmap(painter, irect, Qt.AlignHCenter|Qt.AlignTop, pixmap)
font = QFont(option.font)
font.setBold(highlight)
painter.setFont(font)
icon.paint(painter, irect, Qt.AlignHCenter|Qt.AlignTop, mode, QIcon.On)
painter.drawText(QRectF(trect), Qt.AlignTop|Qt.AlignHCenter, text)
style.drawItemText(painter, trect, Qt.AlignHCenter|Qt.AlignBottom,
option.palette, True, text)
painter.restore()
class LocationModel(QAbstractListModel):
def __init__(self, parent):
QAbstractListModel.__init__(self, parent)

View File

@ -15,6 +15,7 @@ from calibre.gui2 import SingleApplication
from calibre.ebooks.metadata.meta import get_metadata
from calibre.library.database2 import LibraryDatabase2
from calibre.library.database import text_to_tokens
from calibre.ebooks.metadata.opf import OPFCreator, OPFReader
FIELDS = set(['title', 'authors', 'publisher', 'rating', 'timestamp', 'size', 'tags', 'comments', 'series', 'series_index', 'formats'])
@ -304,9 +305,67 @@ do nothing.
do_remove_format(get_db(dbpath, opts), id, fmt)
return 0
def do_show_metadata(db, id, as_opf):
if not db.has_id(id):
raise ValueError('Id #%d is not present in database.'%id)
mi = db.get_metadata(id, index_is_id=True)
if as_opf:
mi = OPFCreator(os.getcwd(), mi)
mi.render(sys.stdout)
else:
print mi
def command_show_metadata(args, dbpath):
parser = get_parser(_(
'''
%prog show_metadata [options] id
Show the metadata stored in the calibre database for the book identified by id.
id is an id number from the list command.
'''))
parser.add_option('--as-opf', default=False, action='store_true',
help=_('Print metadata in OPF form (XML)'))
opts, args = parser.parse_args(sys.argv[1:]+args)
if len(args) < 2:
parser.print_help()
print
print _('You must specify an id')
return 1
id = int(args[1])
do_show_metadata(get_db(dbpath, opts), id, opts.as_opf)
return 0
def do_set_metadata(db, id, stream):
mi = OPFReader(stream)
db.set_metadata(id, mi)
do_show_metadata(db, id, False)
if SingleApplication is not None:
sa = SingleApplication('calibre GUI')
sa.send_message('refreshdb:')
def command_set_metadata(args, dbpath):
parser = get_parser(_(
'''
%prog set_metadata [options] id /path/to/metadata.opf
Set the metadata stored in the calibre database for the book identified by id
from the OPF file metadata.opf. id is an id number from the list command. You
can get a quick feel for the OPF format by using the --as-opf switch to the
show_metadata command.
'''))
opts, args = parser.parse_args(sys.argv[1:]+args)
if len(args) < 3:
parser.print_help()
print
print _('You must specify an id and a metadata file')
return 1
id, opf = int(args[1]), open(args[2], 'rb')
do_set_metadata(get_db(dbpath, opts), id, opf)
return 0
def main(args=sys.argv):
commands = ('list', 'add', 'remove', 'add_format', 'remove_format')
commands = ('list', 'add', 'remove', 'add_format', 'remove_format',
'show_metadata', 'set_metadata')
parser = OptionParser(_(
'''\
%%prog command [options] [arguments]

View File

@ -911,13 +911,19 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE;
def title(self, index, index_is_id=False):
if not index_is_id:
return self.data[index][1]
return self.conn.execute('SELECT title FROM meta WHERE id=?',(index,)).fetchone()[0]
try:
return self.conn.execute('SELECT title FROM meta WHERE id=?',(index,)).fetchone()[0]
except:
return _('Unknown')
def authors(self, index, index_is_id=False):
''' Authors as a comma separated list or None'''
if not index_is_id:
return self.data[index][2]
return self.conn.execute('SELECT authors FROM meta WHERE id=?',(index,)).fetchone()[0]
try:
return self.conn.execute('SELECT authors FROM meta WHERE id=?',(index,)).fetchone()[0]
except:
pass
def isbn(self, idx, index_is_id=False):
id = idx if index_is_id else self.id(idx)
@ -929,22 +935,22 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE;
def publisher(self, index, index_is_id=False):
if index_is_id:
return self.conn.execute('SELECT publisher FROM meta WHERE id=?', (id,)).fetchone()[0]
return self.conn.execute('SELECT publisher FROM meta WHERE id=?', (index,)).fetchone()[0]
return self.data[index][3]
def rating(self, index, index_is_id=False):
if index_is_id:
return self.conn.execute('SELECT rating FROM meta WHERE id=?', (id,)).fetchone()[0]
return self.conn.execute('SELECT rating FROM meta WHERE id=?', (index,)).fetchone()[0]
return self.data[index][4]
def timestamp(self, index, index_is_id=False):
if index_is_id:
return self.conn.execute('SELECT timestamp FROM meta WHERE id=?', (id,)).fetchone()[0]
return self.conn.execute('SELECT timestamp FROM meta WHERE id=?', (index,)).fetchone()[0]
return self.data[index][5]
def max_size(self, index, index_is_id=False):
if index_is_id:
return self.conn.execute('SELECT size FROM meta WHERE id=?', (id,)).fetchone()[0]
return self.conn.execute('SELECT size FROM meta WHERE id=?', (index,)).fetchone()[0]
return self.data[index][6]
def cover(self, index, index_is_id=False):
@ -1251,6 +1257,8 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE;
'''
Set metadata for the book C{id} from the L{MetaInformation} object C{mi}
'''
if mi.title:
self.set_title(id, mi.title)
if not mi.authors:
mi.authors = ['Unknown']
authors = []
@ -1516,6 +1524,9 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE;
def has_book(self, mi):
return bool(self.conn.execute('SELECT id FROM books where title=?', (mi.title,)).fetchone())
def has_id(self, id):
return self.conn.execute('SELECT id FROM books where id=?', (id,)).fetchone() is not None
def recursive_import(self, root, single_book_per_directory=True):
root = os.path.abspath(root)
duplicates = []

View File

@ -131,7 +131,7 @@ Why does |app| show only some of my fonts on OS X?
The graphical user interface of |app| is not starting on Windows?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you've never used the graphical user interface before, try deleting the file library1.db (it will be somewhere under :file:`C:\\Documents and Settings` on Windows XP and :file:`C:\\Users` on Windows Vista. If that doesn't fix the problem, locate the file libprs500.log (in the same places as library1.db) and post its contents in a help message on the `Forums <http://calibre.kovidgoyal.net/discussion>`_.
If you've never used the graphical user interface before, try deleting the file library1.db (it will be somewhere under :file:`C:\\Documents and Settings` on Windows XP and :file:`C:\\Users` on Windows Vista. If that doesn't fix the problem, locate the file calibre.log (in the same places as library1.db) and post its contents in a help message on the `Forums <http://calibre.kovidgoyal.net/discussion>`_.
I want some feature added to |app|. What can I do?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -13,7 +13,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2008-06-14 07:16+0000\n"
"X-Launchpad-Export-Date: 2008-06-15 22:20+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
"Generated-By: pygettext.py 1.5\n"

View File

@ -17,7 +17,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2008-06-14 07:16+0000\n"
"X-Launchpad-Export-Date: 2008-06-15 22:20+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
#~ msgid ""

View File

@ -14,7 +14,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2008-06-14 07:16+0000\n"
"X-Launchpad-Export-Date: 2008-06-15 22:20+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
"Generated-By: pygettext.py 1.5\n"

View File

@ -11,13 +11,13 @@ msgstr ""
"Project-Id-Version: es\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2008-06-12 20:18+0000\n"
"PO-Revision-Date: 2008-06-12 22:40+0000\n"
"PO-Revision-Date: 2008-06-15 08:31+0000\n"
"Last-Translator: S. Dorscht <Unknown>\n"
"Language-Team: Spanish\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2008-06-14 07:16+0000\n"
"X-Launchpad-Export-Date: 2008-06-15 22:20+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
#~ msgid ""
@ -151,7 +151,7 @@ msgstr "Creado por "
#: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:146
#: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:174
msgid "Unable to detect the %s disk drive. Try rebooting."
msgstr ""
msgstr "No se ha podido detectar la unidad de disco %s. Trate de reiniciar."
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:73
msgid "Set the title. Default: filename."
@ -671,7 +671,6 @@ msgid "Creating XML..."
msgstr "Creando XML..."
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/lrfparser.py:156
#, fuzzy
msgid "LRS written to "
msgstr "LRS escrito en "
@ -1130,7 +1129,6 @@ msgid "Show &text in toolbar buttons"
msgstr "Mostrar &texto en los botones de la barra de herramientas"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:219
#, fuzzy
msgid "Free unused diskspace from the database"
msgstr "Espacio de disco disponible de la base de datos"
@ -1896,18 +1894,16 @@ msgstr ""
"actual"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:63
#, fuzzy
msgid "No recipe selected"
msgstr "No hay ninguna receta seleccionada"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:69
#, fuzzy
msgid "The attached file: %s is a recipe to download %s."
msgstr "el archivo adjunto: %s es una receta para descargar %s"
msgstr "El archivo adjunto: %s es una receta para descargar %s"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:70
msgid "Recipe for "
msgstr ""
msgstr "Receta para "
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:86
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:96
@ -1941,9 +1937,8 @@ msgid "Already exists"
msgstr "Ya existe"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:121
#, fuzzy
msgid "This feed has already been added to the recipe"
msgstr "el Feed ya se ha añadido a la receta"
msgstr "Este Feed ya se ha añadido a la receta"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:162
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:171
@ -1968,9 +1963,8 @@ msgid "A custom recipe named %s already exists. Do you want to replace it?"
msgstr "una receta personalizada llamada %s ya existe. Quiere reemplazarla?"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:187
#, fuzzy
msgid "Choose a recipe file"
msgstr "Seleccionarr un archivo de receta"
msgstr "Seleccionar un archivo de receta"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:187
msgid "Recipes"
@ -2651,6 +2645,9 @@ msgid ""
"href=\"http://calibre.kovidgoyal.net/wiki/Changelog\">new features</a>. "
"Visit the download page?"
msgstr ""
"%s se ha actualizado a la versión %s. Ver las <a "
"href=\"http://calibre.kovidgoyal.net/wiki/Changelog\">nuevas "
"características</a>. Visita la página de descarga?"
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1155
msgid "Update available"
@ -2833,6 +2830,8 @@ msgid ""
"Path to the calibre database. Default is to use the path stored in the "
"settings."
msgstr ""
"Camino a la base de datos calibre. El valor predeterminado es a usar la ruta "
"almacenada en la configuración."
#: /home/kovid/work/calibre/src/calibre/library/cli.py:80
msgid ""
@ -2840,6 +2839,9 @@ msgid ""
"\n"
"List the books available in the calibre database. \n"
msgstr ""
"%prog list [options]\n"
"\n"
"Mostrar los libros disponibles en la base de datos calibre. \n"
#: /home/kovid/work/calibre/src/calibre/library/cli.py:88
msgid ""
@ -2866,6 +2868,9 @@ msgid ""
"please see the search related documentation in the User Manual. Default is "
"to do no filtering."
msgstr ""
"Filtrar los resultados de la consulta de búsqueda. Para el formato de la "
"consulta de búsqueda consulte la documentación relacionada con la búsqueda "
"en el Manual del usuario. El valor predeterminado es a no hacer el filtrado."
#: /home/kovid/work/calibre/src/calibre/library/cli.py:101
msgid "Invalid fields. Available fields:"
@ -2891,12 +2896,20 @@ msgid ""
"directories, see\n"
"the directory related options below. \n"
msgstr ""
"%prog add [options] file1 file2 file3 ...\n"
"\n"
"Añadir los archivos especificados como libros a la base de datos. También "
"puede especificar\n"
"directorios, consulte las opciones relacionadas a los directorios más abajo. "
"\n"
#: /home/kovid/work/calibre/src/calibre/library/cli.py:204
msgid ""
"Assume that each directory has only a single logical book and that all files "
"in it are different e-book formats of that book"
msgstr ""
"Supongamos que cada directorio tiene un solo libro lógico y que todos los "
"archivos en este directorio son diferentes formatos de este libro"
#: /home/kovid/work/calibre/src/calibre/library/cli.py:206
msgid "Process directories recursively"
@ -2922,6 +2935,12 @@ msgid ""
"separated list of id numbers (you can get id numbers by using the list "
"command). For example, 23,34,57-85\n"
msgstr ""
"%prog remove ids\n"
"\n"
"Eliminar los libros identificados por ID de la base de datos. ID debe ser "
"una lista separada por comas de números de identificación (se puede obtener "
"números de identificación utilizando el commando \"list\"). Por ejemplo, "
"23,34,57-85\n"
#: /home/kovid/work/calibre/src/calibre/library/cli.py:243
msgid "You must specify at least one book to remove"
@ -2953,6 +2972,13 @@ msgid ""
"by using the list command. fmt should be a file extension like LRF or TXT or "
"EPUB. If the logical book does not have fmt available, do nothing.\n"
msgstr ""
"\n"
"%prog remove_format [options] id fmt\n"
"\n"
"Eliminar el formato fmt del libro lógico identificado por id. Usted puede "
"obtener id utilizando el comando \"list\". fmt debe ser una extensión de "
"archivo como LRF o TXT o EPUB. Si el libro lógico no tiene fmt disponible, "
"no hacer nada.\n"
#: /home/kovid/work/calibre/src/calibre/library/cli.py:300
msgid "You must specify an id and a format"
@ -2976,11 +3002,11 @@ msgstr "Trabajo detenido por el usuario"
#: /home/kovid/work/calibre/src/calibre/utils/fontconfig.py:124
msgid "Could not initialize the fontconfig library"
msgstr ""
msgstr "No se ha podido inicializar la biblioteca fontconfig"
#: /home/kovid/work/calibre/src/calibre/utils/sftp.py:53
msgid "URL must have the scheme sftp"
msgstr ""
msgstr "La URL debe tener el régimen de sftp"
#: /home/kovid/work/calibre/src/calibre/utils/sftp.py:57
msgid "host must be of the form user@hostname"
@ -2988,11 +3014,11 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/utils/sftp.py:68
msgid "Failed to negotiate SSH session: "
msgstr ""
msgstr "No se ha podido negociar período de sesiones SSH: "
#: /home/kovid/work/calibre/src/calibre/utils/sftp.py:71
msgid "Failed to authenticate with server: %s"
msgstr ""
msgstr "No se ha podido autenticar con el servidor: %s"
#: /home/kovid/work/calibre/src/calibre/web/feeds/__init__.py:56
#: /home/kovid/work/calibre/src/calibre/web/feeds/__init__.py:77

View File

@ -13,7 +13,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2008-06-14 07:16+0000\n"
"X-Launchpad-Export-Date: 2008-06-15 22:20+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
"Generated-By: pygettext.py 1.5\n"

View File

@ -15,7 +15,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2008-06-14 07:16+0000\n"
"X-Launchpad-Export-Date: 2008-06-15 22:20+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
"Generated-By: pygettext.py 1.5\n"

View File

@ -14,7 +14,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2008-06-14 07:16+0000\n"
"X-Launchpad-Export-Date: 2008-06-15 22:20+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
"Generated-By: pygettext.py 1.5\n"

View File

@ -14,7 +14,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2008-06-14 07:16+0000\n"
"X-Launchpad-Export-Date: 2008-06-15 22:20+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
#~ msgid ""

View File

@ -13,7 +13,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2008-06-14 07:16+0000\n"
"X-Launchpad-Export-Date: 2008-06-15 22:20+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
"Generated-By: pygettext.py 1.5\n"

View File

@ -13,7 +13,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2008-06-14 07:16+0000\n"
"X-Launchpad-Export-Date: 2008-06-15 22:20+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
"Generated-By: pygettext.py 1.5\n"

View File

@ -109,6 +109,8 @@ class Feed(object):
if id in self.added_articles:
return
published = item.get('date_parsed', time.gmtime())
if not published:
published = time.gmtime()
self.id_counter += 1
self.added_articles.append(id)

234
upload.py
View File

@ -1,13 +1,19 @@
#!/usr/bin/python
import sys, os, shutil, time, tempfile, socket
import sys, os, shutil, time, tempfile, socket, fcntl, struct
sys.path.append('src')
import subprocess
from subprocess import check_call as _check_call
from functools import partial
#from pyvix.vix import Host, VIX_SERVICEPROVIDER_VMWARE_WORKSTATION
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(('google.com', 0))
HOST=s.getsockname()[0]
def get_ip_address(ifname):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
return socket.inet_ntoa(fcntl.ioctl(
s.fileno(),
0x8915, # SIOCGIFADDR
struct.pack('256s', ifname[:15])
)[20:24])
HOST=get_ip_address('eth0')
PROJECT=os.path.basename(os.getcwd())
from calibre import __version__, __appname__
@ -21,11 +27,12 @@ TXT2LRF = "src/calibre/ebooks/lrf/txt/demo"
BUILD_SCRIPT ='''\
#!/bin/bash
cd ~/build && \
rsync -avz --exclude docs --exclude .bzr --exclude .build --exclude build --exclude dist --exclude "*.pyc" --exclude "*.pyo" rsync://%(host)s/work/%(project)s . && \
rsync -avz --exclude src/calibre/plugins --exclude docs --exclude .bzr --exclude .build --exclude build --exclude dist --exclude "*.pyc" --exclude "*.pyo" rsync://%(host)s/work/%(project)s . && \
cd %(project)s && \
mkdir -p build dist && \
mkdir -p build dist src/calibre/plugins && \
%%s && \
rm -rf build/* dist/* && \
python %%s
%%s %%s
'''%dict(host=HOST, project=PROJECT)
check_call = partial(_check_call, shell=True)
#h = Host(hostType=VIX_SERVICEPROVIDER_VMWARE_WORKSTATION)
@ -41,22 +48,24 @@ def installer_name(ext):
return 'dist/%s-%s.%s'%(__appname__, __version__, ext)
return 'dist/%s-%s-i686.%s'%(__appname__, __version__, ext)
def start_vm(vm, ssh_host, build_script, sleep):
def start_vm(vm, ssh_host, build_script, sleep=75):
vmware = ('vmware', '-q', '-x', '-n', vm)
subprocess.Popen(vmware)
t = tempfile.NamedTemporaryFile(suffix='.sh')
t.write(build_script)
t.flush()
print 'Waiting for VM to startup'
time.sleep(sleep)
while subprocess.call('ping -q -c1 '+ssh_host, shell=True, stdout=open('/dev/null', 'w')) != 0:
time.sleep(5)
time.sleep(20)
print 'Trying to SSH into VM'
subprocess.check_call(('scp', t.name, ssh_host+':build-'+PROJECT))
subprocess.check_call('ssh -t %s bash build-%s'%(ssh_host, PROJECT), shell=True)
def build_windows():
installer = installer_name('exe')
vm = '/vmware/Windows XP/Windows XP Professional.vmx'
start_vm(vm, 'windows', BUILD_SCRIPT%'windows_installer.py', 75)
subprocess.check_call(('ssh', 'windows', '/bin/bash', '~/build-'+PROJECT))
start_vm(vm, 'windows', BUILD_SCRIPT%('python setup.py develop', 'python','windows_installer.py'))
subprocess.check_call(('scp', 'windows:build/%s/dist/*.exe'%PROJECT, 'dist'))
if not os.path.exists(installer):
raise Exception('Failed to build installer '+installer)
@ -66,160 +75,24 @@ def build_windows():
def build_osx():
installer = installer_name('dmg')
vm = '/vmware/Mac OSX/Mac OSX.vmx'
vmware = ('vmware', '-q', '-x', '-n', vm)
start_vm(vm, 'osx', BUILD_SCRIPT%'osx_installer.py', 120)
subprocess.check_call(('ssh', 'osx', '/bin/bash', '~/build-'+PROJECT))
subprocess.check_call(('scp', 'windows:build/%s/dist/*.dmg'%PROJECT, 'dist'))
python = '/Library/Frameworks/Python.framework/Versions/Current/bin/python'
start_vm(vm, 'osx', BUILD_SCRIPT%('sudo %s setup.py develop'%python, python, 'osx_installer.py'))
subprocess.check_call(('scp', 'osx:build/%s/dist/*.dmg'%PROJECT, 'dist'))
if not os.path.exists(installer):
raise Exception('Failed to build installer '+installer)
subprocess.Popen(('ssh', 'osx', 'sudo', '/sbin/shutdown', '-h', 'now'))
return os.path.basename(installer)
def _build_linux():
cwd = os.getcwd()
tbz2 = os.path.join(cwd, installer_name('tar.bz2'))
SPEC="""\
import os
HOME = '%(home)s'
PYINSTALLER = os.path.expanduser('~/build/pyinstaller')
CALIBREPREFIX = HOME+'/work/%(project)s'
CLIT = '/usr/bin/clit'
PDFTOHTML = '/usr/bin/pdftohtml'
LIBUNRAR = '/usr/lib/libunrar.so'
QTDIR = '/usr/lib/qt4'
QTDLLS = ('QtCore', 'QtGui', 'QtNetwork', 'QtSvg', 'QtXml')
EXTRAS = ('/usr/lib/python2.5/site-packages/PIL', os.path.expanduser('~/ipython/IPython'))
import glob, sys, subprocess, tarfile
CALIBRESRC = os.path.join(CALIBREPREFIX, 'src')
CALIBREPLUGINS = os.path.join(CALIBRESRC, 'calibre', 'plugins')
subprocess.check_call(('/usr/bin/sudo', 'chown', '-R', 'kovid:users', glob.glob('/usr/lib/python*/site-packages/')[-1]))
subprocess.check_call('rm -rf %%(py)s/dist/* %%(py)s/build/*'%%dict(py=PYINSTALLER), shell=True)
loader = os.path.join('/tmp', 'calibre_installer_loader.py')
if not os.path.exists(loader):
open(loader, 'wb').write('''
import sys, os
sys.frozen_path = os.getcwd()
os.chdir(os.environ.get("ORIGWD", "."))
sys.path.insert(0, os.path.join(sys.frozen_path, "library.pyz"))
sys.path.insert(0, sys.frozen_path)
from PyQt4.QtCore import QCoreApplication
QCoreApplication.setLibraryPaths([sys.frozen_path, os.path.join(sys.frozen_path, "plugins")])
''')
excludes = ['gtk._gtk', 'gtk.glade', 'qt', 'matplotlib.nxutils', 'matplotlib._cntr',
'matplotlib.ttconv', 'matplotlib._image', 'matplotlib.ft2font',
'matplotlib._transforms', 'matplotlib._agg', 'matplotlib.backends._backend_agg',
'matplotlib.axes', 'matplotlib', 'matplotlib.pyparsing',
'TKinter', 'atk', 'gobject._gobject', 'pango', 'PIL', 'Image', 'IPython']
temp = ['keyword', 'codeop']
recipes = ['calibre', 'web', 'feeds', 'recipes']
prefix = '.'.join(recipes)+'.'
for f in glob.glob(os.path.join(CALIBRESRC, *(recipes+['*.py']))):
temp.append(prefix + os.path.basename(f).partition('.')[0])
hook = '/tmp/hook-calibre.py'
open(hook, 'wb').write('hiddenimports = %%s'%%repr(temp) + '\\n')
sys.path.insert(0, CALIBRESRC)
from calibre.linux import entry_points
executables, scripts = ['calibre_postinstall', 'parallel'], \
[os.path.join(CALIBRESRC, 'calibre', 'linux.py'), os.path.join(CALIBRESRC, 'calibre', 'parallel.py')]
for entry in entry_points['console_scripts'] + entry_points['gui_scripts']:
fields = entry.split('=')
executables.append(fields[0].strip())
scripts.append(os.path.join(CALIBRESRC, *map(lambda x: x.strip(), fields[1].split(':')[0].split('.')))+'.py')
recipes = Analysis(glob.glob(os.path.join(CALIBRESRC, 'calibre', 'web', 'feeds', 'recipes', '*.py')),
pathex=[CALIBRESRC], hookspath=[os.path.dirname(hook)], excludes=excludes)
analyses = [Analysis([os.path.join(HOMEPATH,'support/_mountzlib.py'), os.path.join(HOMEPATH,'support/useUnicode.py'), loader, script],
pathex=[PYINSTALLER, CALIBRESRC, CALIBREPLUGINS], excludes=excludes) for script in scripts]
pyz = TOC()
binaries = TOC()
for a in analyses:
pyz = a.pure + pyz
binaries = a.binaries + binaries
pyz = PYZ(pyz + recipes.pure, name='library.pyz')
built_executables = []
for script, exe, a in zip(scripts, executables, analyses):
built_executables.append(EXE(PYZ(TOC()),
a.scripts+[('O','','OPTION'),],
exclude_binaries=1,
name=os.path.join('buildcalibre', exe),
debug=False,
strip=True,
upx=False,
excludes=excludes,
console=1))
print 'Adding plugins...'
for f in glob.glob(os.path.join(CALIBREPLUGINS, '*.so')):
binaries += [(os.path.basename(f), f, 'BINARY')]
print 'Adding external programs...'
binaries += [('clit', CLIT, 'BINARY'), ('pdftohtml', PDFTOHTML, 'BINARY'),
('libunrar.so', LIBUNRAR, 'BINARY')]
qt = []
for dll in QTDLLS:
path = os.path.join(QTDIR, 'lib'+dll+'.so.4')
qt.append((os.path.basename(path), path, 'BINARY'))
binaries += qt
plugins = []
plugdir = os.path.join(QTDIR, 'plugins')
for dirpath, dirnames, filenames in os.walk(plugdir):
for f in filenames:
if not f.endswith('.so') or 'designer' in dirpath or 'codcs' in dirpath or 'sqldrivers' in dirpath : continue
f = os.path.join(dirpath, f)
plugins.append(('plugins/'+f.replace(plugdir, ''), f, 'BINARY'))
binaries += plugins
manifest = '/tmp/manifest'
open(manifest, 'wb').write('\\n'.join(executables))
from calibre import __version__
version = '/tmp/version'
open(version, 'wb').write(__version__)
coll = COLLECT(binaries, pyz, [('manifest', manifest, 'DATA'), ('version', version, 'DATA')],
*built_executables,
**dict(strip=True,
upx=False,
excludes=excludes,
name='dist'))
os.chdir(os.path.join(HOMEPATH, 'calibre', 'dist'))
for folder in EXTRAS:
subprocess.check_call('cp -rf %%s .'%%folder, shell=True)
print 'Building tarball...'
tf = tarfile.open('%(tarfile)s', 'w:bz2')
for f in os.listdir('.'):
tf.add(f)
"""%dict(home='/mnt/hgfs/giskard/', tarfile=tbz2, project=PROJECT)
os.chdir(os.path.expanduser('~/build/pyinstaller'))
open('calibre/calibre.spec', 'wb').write(SPEC)
try:
subprocess.check_call(('/usr/bin/python', '-O', 'Build.py', 'calibre/calibre.spec'))
finally:
os.chdir(cwd)
return os.path.basename(tbz2)
def build_linux():
installer = installer_name('tar.bz2')
vm = '/vmware/linux/libprs500-gentoo.vmx'
vmware = ('vmware', '-q', '-x', '-n', vm)
subprocess.Popen(vmware)
print 'Waiting for linux to boot up...'
time.sleep(75)
check_call('ssh linux make -C /mnt/hgfs/giskard/work/%s all egg linux_binary'%PROJECT)
check_call('ssh linux sudo poweroff')
start_vm(vm, 'linux', BUILD_SCRIPT%('sudo python setup.py develop', 'python','linux_installer.py'))
subprocess.check_call(('scp', 'linux:/tmp/%s'%os.path.basename(installer), 'dist'))
if not os.path.exists(installer):
raise Exception('Failed to build installer '+installer)
subprocess.Popen(('ssh', 'linux', 'sudo', '/sbin/poweroff'))
return os.path.basename(installer)
def build_installers():
return build_linux(), build_windows(), build_osx()
@ -267,18 +140,14 @@ def upload_user_manual():
finally:
os.chdir(cwd)
def build_tarball():
cwd = os.getcwd()
def build_src_tarball():
check_call('bzr export dist/calibre-%s.tar.bz2'%__version__)
def upload_tarball():
def upload_src_tarball():
check_call('ssh divok rm -f %s/calibre-\*.tar.bz2'%DOWNLOADS)
check_call('scp dist/calibre-*.tar.bz2 divok:%s/'%DOWNLOADS)
def main():
upload = len(sys.argv) < 2
def stage_one():
shutil.rmtree('build')
os.mkdir('build')
shutil.rmtree('docs')
@ -288,17 +157,32 @@ def main():
check_call('make', shell=True)
tag_release()
upload_demo()
def stage_two():
subprocess.check_call('rm -rf dist/*', shell=True)
build_installers()
build_tarball()
if upload:
print 'Uploading installers...'
upload_installers()
print 'Uploading to PyPI'
upload_tarball()
upload_docs()
upload_user_manual()
check_call('python setup.py register bdist_egg --exclude-source-files upload')
check_call('''rm -rf dist/* build/*''')
build_src_tarball()
def stage_three():
print 'Uploading installers...'
upload_installers()
print 'Uploading to PyPI'
upload_src_tarball()
upload_docs()
upload_user_manual()
check_call('python setup.py register bdist_egg --exclude-source-files upload')
check_call('''rm -rf dist/* build/*''')
def main(args=sys.argv):
print 'Starting stage one...'
stage_one()
print 'Starting stage two...'
stage_two()
print 'Starting stage three...'
stage_three()
print 'Finished'
return 0
if __name__ == '__main__':
main()
sys.exit(main())

View File

@ -1,7 +1,7 @@
__license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
''' Create a windows installer '''
import sys, re, os, shutil, subprocess
import sys, re, os, shutil, subprocess, zipfile
from setup import VERSION, APPNAME, entry_points, scripts, basenames
from distutils.core import setup
from distutils.filelist import FileList
@ -508,7 +508,11 @@ class BuildEXE(build_exe):
shutil.rmtree(tg)
shutil.copytree(imfd, tg)
print
print 'Adding GUI main.py'
f = zipfile.ZipFile(os.path.join('build', 'py2exe', 'library.zip'), 'a', zipfile.ZIP_DEFLATED)
f.write('src\\calibre\\gui2\\main.py', 'calibre\\gui2\\main.py')
f.close()
print
print
@ -525,19 +529,10 @@ class BuildEXE(build_exe):
def main():
auto = '--auto' in sys.argv
if auto:
sys.argv.remove('--auto')
sys.argv[1:2] = ['py2exe']
if '--verbose' not in sys.argv:
sys.argv.append('--quiet') #py2exe produces too much output by default
subprocess.check_call('python setup.py develop', shell=True)
if auto and not os.path.exists('dist\\auto'):
print os.path.abspath('dist\\auto'), 'does not exist'
return 1
console = [dict(dest_base=basenames['console'][i], script=scripts['console'][i])
for i in range(len(scripts['console']))]
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src'))
setup(
cmdclass = {'py2exe': BuildEXE},
windows = [
@ -573,8 +568,6 @@ def main():
},
)
if auto:
subprocess.call(('shutdown', '-s', '-f', '-t', '01'))
return 0
if __name__ == '__main__':