From 33c1401fd6518c45b3c344aab41a2f46fe68b907 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 15 Jun 2008 00:03:20 -0700 Subject: [PATCH 01/15] Fix rendering of custom delegates in item views to conform to GUI styles --- osx_installer.py | 10 +--- src/calibre/__init__.py | 4 +- src/calibre/gui2/library.py | 5 +- src/calibre/gui2/main.py | 14 +++++- src/calibre/gui2/main.ui | 35 +++++++------- .../gui2/pictureflow/PyQt/configure.py | 4 +- src/calibre/gui2/widgets.py | 48 ++++++++++++------- src/calibre/manual/faq.rst | 2 +- windows_installer.py | 21 ++++---- 9 files changed, 79 insertions(+), 64 deletions(-) diff --git a/osx_installer.py b/osx_installer.py index 79e363618f..22d95f4dba 100644 --- a/osx_installer.py +++ b/osx_installer.py @@ -99,7 +99,7 @@ _check_symlinks_prescript() includes=list(self.includes) + main_modules['console'], packages=self.packages, excludes=self.excludes, - debug=debug, + debug=debug ) @classmethod @@ -277,13 +277,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, diff --git a/src/calibre/__init__.py b/src/calibre/__init__.py index 6145c68f35..73e43264d1 100644 --- a/src/calibre/__init__.py +++ b/src/calibre/__init__.py @@ -447,15 +447,14 @@ 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) _settings.set('LRF conversion defaults', 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): diff --git a/src/calibre/gui2/library.py b/src/calibre/gui2/library.py index afea0aa190..f60ace4c5e 100644 --- a/src/calibre/gui2/library.py +++ b/src/calibre/gui2/library.py @@ -9,7 +9,7 @@ from itertools import repeat from PyQt4.QtGui import QTableView, QProgressDialog, QAbstractItemView, QColor, \ QItemDelegate, QPainterPath, QLinearGradient, QBrush, \ QPen, QStyle, QPainter, QLineEdit, QApplication, \ - QPalette, QImage + QPalette, QImage, QStyleOptionFocusRect, QApplication from PyQt4.QtCore import QAbstractTableModel, QVariant, Qt, QString, \ QCoreApplication, SIGNAL, QObject, QSize, QModelIndex, \ QTimer @@ -55,9 +55,8 @@ class LibraryDelegate(QItemDelegate): painter.restore() painter.save() + QApplication.style().drawControl(QStyle.CE_ItemViewItem, option, painter) 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 diff --git a/src/calibre/gui2/main.py b/src/calibre/gui2/main.py index b3a00c4415..06c4eaff19 100644 --- a/src/calibre/gui2/main.py +++ b/src/calibre/gui2/main.py @@ -4,7 +4,7 @@ import os, sys, textwrap, collections, traceback, shutil, 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 @@ -1190,4 +1190,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) diff --git a/src/calibre/gui2/main.ui b/src/calibre/gui2/main.ui index c53f89b3ec..e38b3833f6 100644 --- a/src/calibre/gui2/main.ui +++ b/src/calibre/gui2/main.ui @@ -27,9 +27,9 @@ 0 - 86 + 79 865 - 712 + 716 @@ -44,7 +44,7 @@ - + 0 0 @@ -52,19 +52,25 @@ 10000 - 100 + 110 Qt::ScrollBarAlwaysOff - Qt::ScrollBarAlwaysOff + Qt::ScrollBarAsNeeded + + + true + + + true - 32 - 32 + 40 + 40 @@ -77,14 +83,11 @@ false - 20 + 10 QListView::IconMode - - true - @@ -332,8 +335,8 @@ 0 0 - 847 - 553 + 857 + 552 @@ -380,7 +383,7 @@ 0 0 865 - 86 + 79 @@ -425,9 +428,9 @@ 0 - 798 + 795 865 - 24 + 27 diff --git a/src/calibre/gui2/pictureflow/PyQt/configure.py b/src/calibre/gui2/pictureflow/PyQt/configure.py index 04c7c8fe91..e24e9a8405 100644 --- a/src/calibre/gui2/pictureflow/PyQt/configure.py +++ b/src/calibre/gui2/pictureflow/PyQt/configure.py @@ -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', '.'] diff --git a/src/calibre/gui2/widgets.py b/src/calibre/gui2/widgets.py index 408b8329e5..efdf4bb237 100644 --- a/src/calibre/gui2/widgets.py +++ b/src/calibre/gui2/widgets.py @@ -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 + QAbstractItemDelegate, QPixmap, QStyle from PyQt4.QtCore import QAbstractListModel, QVariant, Qt, QRect, SIGNAL, \ - QObject, QRegExp, QRectF + QObject, QRegExp, QRectF, QString from calibre.gui2.jobs import DetailView from calibre.gui2 import human_readable, NONE, TableView, qstring_to_unicode, error_dialog @@ -123,28 +123,42 @@ 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() + irect = style.itemPixmapRect(option.rect, Qt.AlignHCenter|Qt.AlignTop, self.pixmap) + trect = style.itemTextRect(option.fontMetrics, 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() + 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) + painter.setFont(option.font) + style.drawItemText(painter, trect, Qt.AlignHCenter|Qt.AlignBottom, + option.palette, True, text) + painter.restore() + return + #font = QFont() + #font.setPointSize(9) + + + text = index.model().data(index, Qt.DisplayRole).toString() irect, trect = self.get_rects(index, option) mode = QIcon.Normal diff --git a/src/calibre/manual/faq.rst b/src/calibre/manual/faq.rst index 34872cbdb3..9a12253b62 100644 --- a/src/calibre/manual/faq.rst +++ b/src/calibre/manual/faq.rst @@ -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 `_. +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 `_. I want some feature added to |app|. What can I do? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/windows_installer.py b/windows_installer.py index da53aa7f7e..c38ee3487b 100644 --- a/windows_installer.py +++ b/windows_installer.py @@ -1,7 +1,7 @@ __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal ' ''' 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,18 +529,11 @@ 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 - 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 = [ @@ -572,8 +569,6 @@ def main(): }, ) - if auto: - subprocess.call(('shutdown', '-s', '-f', '-t', '01')) return 0 if __name__ == '__main__': From 61d5c451430b7b7163acd8f520925b88033684cd Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 15 Jun 2008 11:16:24 -0700 Subject: [PATCH 02/15] Fix #675 --- src/calibre/devices/prs505/driver.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/calibre/devices/prs505/driver.py b/src/calibre/devices/prs505/driver.py index 1a3c346210..0050e091d3 100644 --- a/src/calibre/devices/prs505/driver.py +++ b/src/calibre/devices/prs505/driver.py @@ -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] From 81e418ef0c93ec216f8328505930572fdc4cd716 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 15 Jun 2008 11:38:06 -0700 Subject: [PATCH 03/15] version 0.4.71 --- src/calibre/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/__init__.py b/src/calibre/__init__.py index 73e43264d1..498e58ae21 100644 --- a/src/calibre/__init__.py +++ b/src/calibre/__init__.py @@ -1,7 +1,7 @@ ''' E-book management software''' __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal ' -__version__ = '0.4.70' +__version__ = '0.4.71' __docformat__ = "epytext" __author__ = "Kovid Goyal " __appname__ = 'calibre' From 5bda15bac5d5b0643df40f71f1f992e765f14018 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 15 Jun 2008 11:40:39 -0700 Subject: [PATCH 04/15] IGN:Tag release From 30a01e448495de6290530cfd146c98c04e570eae Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 15 Jun 2008 16:26:48 -0700 Subject: [PATCH 05/15] IGN:... --- src/calibre/gui2/widgets.py | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/src/calibre/gui2/widgets.py b/src/calibre/gui2/widgets.py index efdf4bb237..eccb4a30f8 100644 --- a/src/calibre/gui2/widgets.py +++ b/src/calibre/gui2/widgets.py @@ -8,7 +8,7 @@ from PyQt4.QtGui import QListView, QIcon, QFont, QLabel, QListWidget, \ QListWidgetItem, QTextCharFormat, QApplication, \ QSyntaxHighlighter, QCursor, QColor, QWidget, \ QAbstractItemDelegate, QPixmap, QStyle -from PyQt4.QtCore import QAbstractListModel, QVariant, Qt, QRect, SIGNAL, \ +from PyQt4.QtCore import QAbstractListModel, QVariant, Qt, SIGNAL, \ QObject, QRegExp, QRectF, QString from calibre.gui2.jobs import DetailView @@ -149,28 +149,14 @@ class LocationDelegate(QAbstractItemDelegate): text = index.model().data(index, Qt.DisplayRole).toString() irect, trect = self.rects(option) style.drawItemPixmap(painter, irect, Qt.AlignHCenter|Qt.AlignTop, pixmap) - painter.setFont(option.font) + font = QFont(option.font) + font.setBold(highlight) + painter.setFont(font) style.drawItemText(painter, trect, Qt.AlignHCenter|Qt.AlignBottom, option.palette, True, text) painter.restore() - return - #font = QFont() - #font.setPointSize(9) - text = index.model().data(index, Qt.DisplayRole).toString() - irect, trect = self.get_rects(index, option) - - mode = QIcon.Normal - if highlight: - font.setBold(True) - mode = QIcon.Active - - painter.setFont(font) - icon.paint(painter, irect, Qt.AlignHCenter|Qt.AlignTop, mode, QIcon.On) - painter.drawText(QRectF(trect), Qt.AlignTop|Qt.AlignHCenter, text) - painter.restore() - class LocationModel(QAbstractListModel): def __init__(self, parent): QAbstractListModel.__init__(self, parent) From 908e31a80c571506b817b7481b3670817a1823d1 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 15 Jun 2008 16:30:44 -0700 Subject: [PATCH 06/15] IGN:Rationalize automated build process --- Makefile | 6 +- linux_installer.py | 153 +++++++++++++++++++++ osx_installer.py | 14 +- src/calibre/gui2/widgets.py | 8 +- src/calibre/translations/bg.po | 2 +- src/calibre/translations/ca.po | 2 +- src/calibre/translations/de.po | 2 +- src/calibre/translations/es.po | 60 +++++--- src/calibre/translations/fr.po | 2 +- src/calibre/translations/it.po | 2 +- src/calibre/translations/nds.po | 2 +- src/calibre/translations/nl.po | 2 +- src/calibre/translations/ru.po | 2 +- src/calibre/translations/sl.po | 2 +- upload.py | 234 ++++++++------------------------ 15 files changed, 282 insertions(+), 211 deletions(-) create mode 100644 linux_installer.py diff --git a/Makefile b/Makefile index c9f7ca1db4..1abffcec66 100644 --- a/Makefile +++ b/Makefile @@ -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 : diff --git a/linux_installer.py b/linux_installer.py new file mode 100644 index 0000000000..315b2d8486 --- /dev/null +++ b/linux_installer.py @@ -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) diff --git a/osx_installer.py b/osx_installer.py index 22d95f4dba..cccb46ad93 100644 --- a/osx_installer.py +++ b/osx_installer.py @@ -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'), @@ -294,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', diff --git a/src/calibre/gui2/widgets.py b/src/calibre/gui2/widgets.py index eccb4a30f8..4a855f553a 100644 --- a/src/calibre/gui2/widgets.py +++ b/src/calibre/gui2/widgets.py @@ -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, QStyle + QAbstractItemDelegate, QPixmap, QStyle, QFontMetrics from PyQt4.QtCore import QAbstractListModel, QVariant, Qt, SIGNAL, \ - QObject, QRegExp, QRectF, QString + QObject, QRegExp, QString from calibre.gui2.jobs import DetailView from calibre.gui2 import human_readable, NONE, TableView, qstring_to_unicode, error_dialog @@ -128,8 +128,10 @@ class LocationDelegate(QAbstractItemDelegate): 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(option.fontMetrics, option.rect, + trect = style.itemTextRect(QFontMetrics(font), option.rect, Qt.AlignHCenter|Qt.AlignTop, True, self.text) trect.moveTop(irect.bottom()) return irect, trect diff --git a/src/calibre/translations/bg.po b/src/calibre/translations/bg.po index 6cf334ee75..a8d83a4ce0 100644 --- a/src/calibre/translations/bg.po +++ b/src/calibre/translations/bg.po @@ -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" diff --git a/src/calibre/translations/ca.po b/src/calibre/translations/ca.po index cb5552d3fd..4f6ba82b26 100644 --- a/src/calibre/translations/ca.po +++ b/src/calibre/translations/ca.po @@ -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 "" diff --git a/src/calibre/translations/de.po b/src/calibre/translations/de.po index 2b0290aaf7..8c4a6e9641 100644 --- a/src/calibre/translations/de.po +++ b/src/calibre/translations/de.po @@ -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" diff --git a/src/calibre/translations/es.po b/src/calibre/translations/es.po index ea7b148db3..60e3560a54 100644 --- a/src/calibre/translations/es.po +++ b/src/calibre/translations/es.po @@ -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 \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. " "Visit the download page?" msgstr "" +"%s se ha actualizado a la versión %s. Ver las nuevas " +"características. 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 diff --git a/src/calibre/translations/fr.po b/src/calibre/translations/fr.po index ce9997db8d..7454c5abe2 100644 --- a/src/calibre/translations/fr.po +++ b/src/calibre/translations/fr.po @@ -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" diff --git a/src/calibre/translations/it.po b/src/calibre/translations/it.po index 378142d18c..445a32c0e7 100644 --- a/src/calibre/translations/it.po +++ b/src/calibre/translations/it.po @@ -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" diff --git a/src/calibre/translations/nds.po b/src/calibre/translations/nds.po index 6090676765..9e837a78bd 100644 --- a/src/calibre/translations/nds.po +++ b/src/calibre/translations/nds.po @@ -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" diff --git a/src/calibre/translations/nl.po b/src/calibre/translations/nl.po index a901375f9e..e3ec255958 100644 --- a/src/calibre/translations/nl.po +++ b/src/calibre/translations/nl.po @@ -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 "" diff --git a/src/calibre/translations/ru.po b/src/calibre/translations/ru.po index 348bb5492d..9701545c99 100644 --- a/src/calibre/translations/ru.po +++ b/src/calibre/translations/ru.po @@ -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" diff --git a/src/calibre/translations/sl.po b/src/calibre/translations/sl.po index 6169f2dc89..43ea54d181 100644 --- a/src/calibre/translations/sl.po +++ b/src/calibre/translations/sl.po @@ -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" diff --git a/upload.py b/upload.py index e3241c1417..3ad07aa28a 100644 --- a/upload.py +++ b/upload.py @@ -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()) From a6361f395ce0afec03468a238a7b0b08561e9ba8 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 15 Jun 2008 22:50:19 -0700 Subject: [PATCH 07/15] Fix #789 --- src/calibre/gui2/dialogs/fetch_metadata.py | 72 +++++++++++---------- src/calibre/gui2/dialogs/fetch_metadata.ui | 2 +- src/calibre/gui2/dialogs/metadata_single.py | 2 +- 3 files changed, 40 insertions(+), 36 deletions(-) diff --git a/src/calibre/gui2/dialogs/fetch_metadata.py b/src/calibre/gui2/dialogs/fetch_metadata.py index 9a64c8dc42..f905e72e81 100644 --- a/src/calibre/gui2/dialogs/fetch_metadata.py +++ b/src/calibre/gui2/dialogs/fetch_metadata.py @@ -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() - - 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) - - 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.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() + try: + 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) + + try: + books = create_books(opts, args, self.logger, self.timeout) + except ISBNDBError, err: + error_dialog(self, _('Error fetching metadata'), str(err)).exec_() + return + + 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)) + finally: + self.fetch.setEnabled(True) + self.unsetCursor() + self.matches.resizeColumnsToContents() diff --git a/src/calibre/gui2/dialogs/fetch_metadata.ui b/src/calibre/gui2/dialogs/fetch_metadata.ui index 959fb18d89..e8b4252d8d 100644 --- a/src/calibre/gui2/dialogs/fetch_metadata.ui +++ b/src/calibre/gui2/dialogs/fetch_metadata.ui @@ -47,7 +47,7 @@ - &Access Key; + &Access Key: key diff --git a/src/calibre/gui2/dialogs/metadata_single.py b/src/calibre/gui2/dialogs/metadata_single.py index af2c97a3be..f4fce00482 100644 --- a/src/calibre/gui2/dialogs/metadata_single.py +++ b/src/calibre/gui2/dialogs/metadata_single.py @@ -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 From 5ad2c3c802ec5a7b462b7c2eb5f6f9811a64c61e Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 15 Jun 2008 23:03:07 -0700 Subject: [PATCH 08/15] Fix bug 'Cover not updating after editing metadata' --- src/calibre/gui2/library.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/calibre/gui2/library.py b/src/calibre/gui2/library.py index f60ace4c5e..5984c66dcd 100644 --- a/src/calibre/gui2/library.py +++ b/src/calibre/gui2/library.py @@ -127,6 +127,7 @@ class BooksModel(QAbstractTableModel): def refresh_ids(self, ids, current_row=-1): rows = self.db.refresh_ids(ids) for row in rows: + self.buffer.pop(row, None) if row == current_row: self.emit(SIGNAL('new_bookdisplay_data(PyQt_PyObject)'), self.get_book_display_info(row)) From 8cf8b56bccfff896f6306b4051c8f59bbca3886a Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 16 Jun 2008 11:23:28 -0700 Subject: [PATCH 09/15] Fix #791 --- src/calibre/web/feeds/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/calibre/web/feeds/__init__.py b/src/calibre/web/feeds/__init__.py index 0d869e36d2..003e9af318 100644 --- a/src/calibre/web/feeds/__init__.py +++ b/src/calibre/web/feeds/__init__.py @@ -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) From cf3bb47fc33e908c8ff1f9b1ee728085afc37dea Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 16 Jun 2008 11:45:46 -0700 Subject: [PATCH 10/15] Fix #790 --- src/calibre/gui2/widgets.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/calibre/gui2/widgets.py b/src/calibre/gui2/widgets.py index 4a855f553a..9846d4d46d 100644 --- a/src/calibre/gui2/widgets.py +++ b/src/calibre/gui2/widgets.py @@ -143,7 +143,8 @@ class LocationDelegate(QAbstractItemDelegate): def paint(self, painter, option, index): style = QApplication.style() painter.save() - QApplication.style().drawControl(QStyle.CE_ItemViewItem, option, painter) + 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()) From e5214e3f394ff3f8b4a3dd47fe9b455ec63aa20e Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 16 Jun 2008 12:18:56 -0700 Subject: [PATCH 11/15] Fix #790 --- src/calibre/gui2/library.py | 9 ++++++--- src/calibre/gui2/widgets.py | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/calibre/gui2/library.py b/src/calibre/gui2/library.py index 5984c66dcd..5ede2b49a0 100644 --- a/src/calibre/gui2/library.py +++ b/src/calibre/gui2/library.py @@ -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, QStyleOptionFocusRect, QApplication + QPen, QStyle, QPainter, QLineEdit, \ + QPalette, QImage, QApplication from PyQt4.QtCore import QAbstractTableModel, QVariant, Qt, QString, \ QCoreApplication, SIGNAL, QObject, QSize, QModelIndex, \ QTimer @@ -55,7 +55,10 @@ class LibraryDelegate(QItemDelegate): painter.restore() painter.save() - QApplication.style().drawControl(QStyle.CE_ItemViewItem, option, painter) + 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: painter.setRenderHint(QPainter.Antialiasing) y = option.rect.center().y()-self.SIZE/2. diff --git a/src/calibre/gui2/widgets.py b/src/calibre/gui2/widgets.py index 9846d4d46d..1f9de46c5b 100644 --- a/src/calibre/gui2/widgets.py +++ b/src/calibre/gui2/widgets.py @@ -143,7 +143,7 @@ class LocationDelegate(QAbstractItemDelegate): def paint(self, painter, option, index): style = QApplication.style() painter.save() - if hasattr(QStyle.CE_ItemViewItem): + 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 From a3fa6442eca6a1ade10b222ac9caf888eab573e9 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 16 Jun 2008 12:51:48 -0700 Subject: [PATCH 12/15] Add support for getting and setting metadata to calibredb --- src/calibre/library/cli.py | 60 +++++++++++++++++++++++++++++++-- src/calibre/library/database.py | 23 +++++++++---- 2 files changed, 74 insertions(+), 9 deletions(-) diff --git a/src/calibre/library/cli.py b/src/calibre/library/cli.py index b1824ac278..451e91acc0 100644 --- a/src/calibre/library/cli.py +++ b/src/calibre/library/cli.py @@ -10,11 +10,10 @@ Command line interface to the calibre database. import sys, os from textwrap import TextWrapper -from PyQt4.QtCore import QVariant - from calibre import OptionParser, Settings, terminal_controller, preferred_encoding from calibre.gui2 import SingleApplication from calibre.ebooks.metadata.meta import get_metadata +from calibre.ebooks.metadata.opf import OPFCreator, OPFReader from calibre.library.database import LibraryDatabase, text_to_tokens FIELDS = set(['title', 'authors', 'publisher', 'rating', 'timestamp', 'size', 'tags', 'comments', 'series', 'series_index', 'formats']) @@ -304,9 +303,64 @@ 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) + +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] diff --git a/src/calibre/library/database.py b/src/calibre/library/database.py index 580e4a327d..c357239fa5 100644 --- a/src/calibre/library/database.py +++ b/src/calibre/library/database.py @@ -919,13 +919,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) @@ -937,22 +943,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): @@ -1259,6 +1265,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 = [] @@ -1524,6 +1532,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 = [] From 719a0b2c7ed843fdb530326f2221ec3013878daf Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 16 Jun 2008 13:00:05 -0700 Subject: [PATCH 13/15] version 0.4.72 --- src/calibre/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/__init__.py b/src/calibre/__init__.py index 498e58ae21..12eeb2f625 100644 --- a/src/calibre/__init__.py +++ b/src/calibre/__init__.py @@ -1,7 +1,7 @@ ''' E-book management software''' __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal ' -__version__ = '0.4.71' +__version__ = '0.4.72' __docformat__ = "epytext" __author__ = "Kovid Goyal " __appname__ = 'calibre' From 99c7311c02843f4443a861f72a200c4e34625dfd Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 16 Jun 2008 13:02:16 -0700 Subject: [PATCH 14/15] IGN:Tag release From 5aa463b5e0cb05d3607d2d83e711e2522408e3f3 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 16 Jun 2008 13:39:32 -0700 Subject: [PATCH 15/15] calibredb should ask the GUI to refresh its book list after setting metadata --- src/calibre/library/cli.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/calibre/library/cli.py b/src/calibre/library/cli.py index 451e91acc0..edda1c8502 100644 --- a/src/calibre/library/cli.py +++ b/src/calibre/library/cli.py @@ -337,6 +337,9 @@ 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(_(