diff --git a/installer/linux/freeze.py b/installer/linux/freeze.py index 3b272551ff..6e5bb0592b 100644 --- a/installer/linux/freeze.py +++ b/installer/linux/freeze.py @@ -9,7 +9,7 @@ Create linux binary. ''' def freeze(): - import glob, sys, tarfile, os, textwrap, shutil + import glob, sys, tarfile, os, textwrap, shutil, platform from contextlib import closing from cx_Freeze import Executable, setup from calibre.constants import __version__, __appname__ @@ -19,6 +19,9 @@ def freeze(): from calibre.ebooks.lrf.fonts import FONT_MAP import calibre + is64bit = platform.architecture()[0] == '64bit' + arch = 'x86_64' if is64bit else 'i686' + QTDIR = '/usr/lib/qt4' QTDLLS = ('QtCore', 'QtGui', 'QtNetwork', 'QtSvg', 'QtXml', @@ -47,7 +50,8 @@ def freeze(): '/usr/lib/libxslt.so.1', '/usr/lib/libxslt.so.1', '/usr/lib/libgthread-2.0.so.0', - '/usr/lib/gcc/i686-pc-linux-gnu/4.3.3/libstdc++.so.6', + '/usr/lib/gcc/***-pc-linux-gnu/4.4.1/libstdc++.so.6'.replace('***', + arch).replace('i686', 'i486'), '/usr/lib/libpng12.so.0', '/usr/lib/libexslt.so.0', '/usr/lib/libMagickWand.so', @@ -89,7 +93,7 @@ def freeze(): includes = [x[0] for x in executables.values()] includes += ['calibre.ebooks.lrf.fonts.prs500.'+x for x in FONT_MAP.values()] - includes += ['email.iterators', 'email.generator'] + includes += ['email.iterators', 'email.generator', 'sqlite3.dump'] excludes = ['matplotlib', "Tkconstants", "Tkinter", "tcl", "_imagingtk", @@ -228,7 +232,8 @@ def freeze(): open(os.path.join(FREEZE_DIR, 'manifest'), 'wb').write('\n'.join(exes)) print 'Creating archive...' - dist = open(os.path.join(DIST_DIR, 'calibre-%s-i686.tar.bz2'%__version__), 'wb') + dist = open(os.path.join(DIST_DIR, 'calibre-%s-%s.tar.bz2'%(__version__, + arch)), 'wb') with closing(tarfile.open(fileobj=dist, mode='w:bz2', format=tarfile.PAX_FORMAT)) as tf: for f in walk(FREEZE_DIR): diff --git a/installer/osx/freeze.py b/installer/osx/freeze.py index b9dc2ebe39..7791668a09 100644 --- a/installer/osx/freeze.py +++ b/installer/osx/freeze.py @@ -353,7 +353,7 @@ def main(): 'keyword', 'codeop', 'pydoc', 'readline', 'BeautifulSoup', 'calibre.ebooks.lrf.fonts.prs500.*', 'dateutil', 'email.iterators', - 'email.generator', + 'email.generator', 'sqlite3.dump', 'calibre.ebooks.metadata.amazon', ], 'packages' : ['PIL', 'Authorization', 'lxml', 'dns'], diff --git a/installer/windows/build_installer.py b/installer/windows/build_installer.py index 01720bb2c0..24a8768ded 100644 --- a/installer/windows/build_installer.py +++ b/installer/windows/build_installer.py @@ -36,7 +36,7 @@ def run_install_jammer(installer_name='<%AppName%>-<%Version%><%Ext%>', build_fo compression = 'zlib' if build_for_release: cmdline += ['--build-for-release'] - #compression = 'lzma (solid)' + compression = 'lzma (solid)' cmdline += ['-DCompressionMethod', compression] cmdline += ['--build', mpi] #print 'Running installjammer with cmdline:' diff --git a/installer/windows/calibre/calibre.mpi b/installer/windows/calibre/calibre.mpi index a371bb9587..42f30224b0 100644 --- a/installer/windows/calibre/calibre.mpi +++ b/installer/windows/calibre/calibre.mpi @@ -232,12 +232,10 @@ test } FileGroup ::BEF8D398-58BA-1F66-39D6-D4A63D5BEEF9 -setup Install -active Yes -platforms {AIX-ppc FreeBSD-4-x86 FreeBSD-x86 HPUX-hppa Linux-x86 Solaris-sparc Windows TarArchive ZipArchive FreeBSD-5-x86 FreeBSD-6-x86 FreeBSD-7-x86 Linux-x86_64 Solaris-x86} -name {Program Files} -parent FileGroups File ::8E5D85A4-7608-47A1-CF7C-309060D5FF40 -filemethod {Always overwrite files} -type dir -directory <%InstallDir%> -name /home/kovid/work/calibre/build/py2exe -parent BEF8D398-58BA-1F66-39D6-D4A63D5BEEF9 -File ::FC870EE7-667B-481F-113B-B4504DFCCFA5 -type dir -name bin -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 File ::377C588B-B324-CA09-ED49-4DB5F82A15ED -type dir -name etc -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 File ::55DE4B9F-0881-FF51-E2BA-EC72B5D3425C -type dir -name fonts -parent 377C588B-B324-CA09-ED49-4DB5F82A15ED File ::A27B68D9-43A6-B994-3091-E829AFBA340D -type dir -name conf.d -parent 55DE4B9F-0881-FF51-E2BA-EC72B5D3425C File ::974ADD48-88E5-BC7A-1963-928A245F133A -type dir -name conf.avail -parent 55DE4B9F-0881-FF51-E2BA-EC72B5D3425C -File ::5E5273D8-3423-8DC8-83C4-BE000069A803 -name fonts.dtd -parent 55DE4B9F-0881-FF51-E2BA-EC72B5D3425C File ::32D7DBE0-E0B1-5BDD-66C5-2A13D8BC8F90 -name fonts.conf -parent 55DE4B9F-0881-FF51-E2BA-EC72B5D3425C File ::B95D03D4-EA59-F00E-59E1-BA05758879DA -type dir -name imageformats -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 File ::A624029D-AE0F-49A5-4DAC-7720CDCAB271 -name qmng4.dll -parent B95D03D4-EA59-F00E-59E1-BA05758879DA @@ -337,7 +335,6 @@ File ::2F90282D-B59F-B6BA-090B-45858AF7F3B2 -name IM_MOD_RL_clipboard_.dll -pare File ::B512D139-B295-D7C3-F0B4-43775849CF58 -name numpy.core._sort.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 File ::A2C063AC-2F12-9260-501A-0E8BD0B8A932 -name calibre.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 File ::439B6D05-7DE6-061D-4BCC-3F04F4FA2FA2 -name IM_MOD_RL_png_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 -File ::BA464D11-BBCE-DEDA-C354-0C7BE60FAA05 -name IM_MOD_RL_braille_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 File ::8F9FF823-AF6D-A288-8AE6-7D74F55DCE29 -name CORE_RL_bzlib_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 File ::57A3F5D5-BFC8-CB38-5A57-548EE0DB033B -name QtNetwork4.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 File ::4DB7E8DE-905A-822A-AF14-17BD5ACEF915 -name IM_MOD_RL_wmf_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 @@ -361,7 +358,6 @@ File ::A6419A84-6C22-784E-6D84-D09972770770 -name unicodedata.pyd -parent 8E5D85 File ::E658FBE0-5860-D041-12D3-76ADD18F804B -name servicemanager.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 File ::C98A6FC4-E341-7FD4-005C-DA2B384E11D8 -name win32api.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 File ::ADA36EEA-7DE1-447C-B1AB-A4908E65E2CD -name IM_MOD_RL_ipl_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 -File ::53C2EC15-850F-8F49-6425-C228FB6E6D0E -name libfontconfig-1.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 File ::EDE6F457-C83F-C5FA-9AF4-38FDFF17D929 -name PIL._imagingtk.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 File ::09D0906E-3611-3DB7-32CF-A140585694A7 -name win32pdh.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 File ::4C84F0DC-7157-0C90-2062-180139B03E25 -name IM_MOD_RL_rgb_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 @@ -384,17 +380,14 @@ File ::404A98F1-84FD-B6D0-B130-354EECD9253C -name IM_MOD_RL_emf_.dll -parent 8E5 File ::17034C34-403E-B405-99C1-F80B7F00E27C -name log.xml -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 File ::34E63A2C-65C5-0A84-ACF1-BD6A844D4579 -name pythoncom26.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 File ::2F20484B-53B8-B08E-B691-C5B2D49A9CB4 -name QtWebKit4.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 -File ::8AF134C8-9189-3F9A-A081-9143FFD44C45 -name freetype6.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 File ::E8A4442D-D0D3-31CD-997A-3CEB641CF5B7 -name IM_MOD_RL_mtv_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 File ::0CA87D0B-5A04-1439-AEE8-C97072D47BA7 -name CORE_RL_tiff_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 File ::AC24F520-88D4-D1CF-5797-27C715CE8ACA -name pyexpat.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 File ::16848F38-71CD-55B8-4D96-1537F6773744 -name IM_MOD_RL_dps_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 -File ::33A46CC5-BAC4-5863-C83D-303DCCA0CAA1 -name tk85.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 File ::81116DD3-1715-AA87-472F-544FC616EDAF -name IM_MOD_RL_dcm_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 File ::335A4CFB-5C2D-44E4-C438-7018E8244C3D -name ebook-viewer.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 File ::62A16C3B-ED9C-5187-2807-58857DF3A990 -name calibre-debug.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 File ::55ECA7B7-279A-F51D-81C2-C8DC44CF0E22 -name select.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 -File ::A6AF5ECC-A981-4CBD-DBEE-303A9340C603 -name IM_MOD_RL_xps_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 File ::5BCBF71F-18E7-5C52-E3F5-7D7F3028AD46 -name locale.xml -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 File ::5C9FA94C-B8B0-A94B-548D-1D24FDEA5770 -name CORE_RL_wand_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 File ::E39E60BE-DE77-AB8C-42C6-5A7D7DC073E3 -name IM_MOD_RL_ttf_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 @@ -443,12 +436,10 @@ File ::396B4F78-FB45-C0B2-ACB3-97769CF5CD5D -name msvcr90.dll -parent 8E5D85A4-7 File ::1DE767EE-4891-4E54-422D-67A4DFF8C3B5 -name lrfviewer.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 File ::AFFEC28A-615C-E3E6-0026-CCE2594A6D25 -name calibre-server.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 File ::2C64F86B-9366-B52D-F7B2-5BBD51F6982A -name IM_MOD_RL_pwp_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 -File ::F4B2EF9C-EB18-B865-6E99-75CFB9B60D87 -name IM_MOD_RL_dds_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 File ::38770D87-6CA9-9E3E-FBA1-A8CCFCD88FB5 -name IM_MOD_RL_fpx_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 File ::BE2D7BC3-D294-AF3F-65E7-3B372DEFDE36 -name PIL._imaging.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 File ::200B9AEC-809F-75B7-DC12-A51BFC2A6F93 -name PyQt4.QtSvg.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 File ::52132A31-D3AE-C617-7568-BF2AF46B5D74 -name IM_MOD_RL_pcl_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 -File ::F94472C3-C8D0-950F-5ED9-1611D1CE30E5 -name IM_MOD_RL_inline_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 File ::055ADB4B-20C5-E071-442F-4DA0A8D6F3C5 -name english.xml -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 File ::B10B6B91-0C03-642D-90D8-37B607B164AD -name IM_MOD_RL_wpg_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 File ::BFBB144B-1794-8304-9772-F103A42F2CA4 -name IM_MOD_RL_pdb_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 @@ -503,7 +494,6 @@ File ::9BA85EE5-1754-67AF-736D-481CDCC72DD2 -name _imagingft.pyd -parent 8E5D85A File ::6254DD0C-8F2C-D4AE-2107-2597D542C181 -name IM_MOD_RL_matte_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 File ::F159D566-88D6-C347-3E3C-55C2DDFC5FD0 -name IM_MOD_RL_mono_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 File ::B873CAA2-011F-94C3-7977-FF344E53C44F -name CORE_RL_jbig_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 -File ::7004FCB8-C6F4-C7AF-08E4-B6151B2F7050 -name tcl85.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 File ::6921F62A-4015-4C9F-98A6-BCBBC43B698E -name msvcm90.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 File ::7276E0CA-C205-4B18-19A3-157F1B8523FB -name IM_MOD_RL_xtrn_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 File ::7B9624A9-88B4-C61E-6771-9A34FB6CA3B5 -name PyQt4.QtGui.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 @@ -567,6 +557,15 @@ File ::9E4E5E8F-30C0-E631-9516-2AE01A5CA0E9 -name ebook-device.exe.local -parent File ::7BE6B538-70D5-A7EB-5F91-E14CE57B394B -name calibre-complete.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 File ::C4E40030-3EE0-8B05-E6B9-89E81433EE1F -name phonon4.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 File ::9E84342F-36ED-7ED3-8F90-1EC55267BCFC -name poppler-qt4.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 +File ::86BA442C-90C9-A4E6-1D3E-D144E5F326C1 -name msvcp71.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 +File ::11FBAD0B-A2DB-C28A-85B8-D6A22706864F -name mfc71.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 +File ::4B9FB3E6-B807-65CC-826D-A398E964D00C -name IM_MOD_RL_hdf_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 +File ::3E201C0C-C7CC-5785-74F6-A6CC7F50A15A -name msvcr71.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 +File ::2EE42149-1C12-CCA9-9089-AE1809098D0A -name jpeg62.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 +File ::B1FD37B4-E91B-DC1C-1C69-FB2E10EB93AE -name libtiff3.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 +File ::15E09D95-97D6-92A9-CC4D-120885E4DDAD -name freetype.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 +File ::D954BC75-8166-EC1B-D91B-C9779248AA14 -name fontconfig.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 +File ::1F3C052A-A5E0-5C65-8D42-EBF44FBE138D -name podofo.dll.manifest -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40 Component ::F6829AB7-9F66-4CEE-CA0E-21F54C6D3609 -setup Install -active Yes -platforms {AIX-ppc FreeBSD-4-x86 FreeBSD-x86 HPUX-hppa Linux-x86 Solaris-sparc Windows FreeBSD-5-x86 FreeBSD-6-x86 FreeBSD-7-x86 Linux-x86_64 Solaris-x86} -name Main -parent Components SetupType ::D9ADE41C-B744-690C-2CED-CF826BF03D2E -setup Install -active Yes -platforms {AIX-ppc FreeBSD-4-x86 FreeBSD-x86 HPUX-hppa Linux-x86 Solaris-sparc Windows FreeBSD-5-x86 FreeBSD-6-x86 FreeBSD-7-x86 Linux-x86_64 Solaris-x86} -name Typical -parent SetupTypes diff --git a/installer/windows/freeze.py b/installer/windows/freeze.py index 4dadc8875c..0b4f18ff4a 100644 --- a/installer/windows/freeze.py +++ b/installer/windows/freeze.py @@ -7,15 +7,12 @@ __docformat__ = 'restructuredtext en' Freeze app into executable using py2exe. ''' QT_DIR = 'C:\\Qt\\4.5.2' -LIBUSB_DIR = 'C:\\libusb' +LIBUSB_DIR = r'C:\cygwin\home\kovid\win32\libusb' LIBUNRAR = 'C:\\Program Files\\UnrarDLL\\unrar.dll' -PDFTOHTML = 'C:\\cygwin\\home\\kovid\\poppler-0.10.6\\rel\\pdftohtml.exe' -POPPLER = 'C:\\cygwin\\home\\kovid\\poppler' -IMAGEMAGICK_DIR = 'C:\\ImageMagick' -PDFTK = 'C:\\pdftk.exe' -PODOFO = 'C:\\podofo' -FONTCONFIG_DIR = 'C:\\fontconfig' -VC90 = r'C:\VC90.CRT' +BINARIES = r'C:\cygwin\home\kovid\win32\bin' +IMAGEMAGICK_DIR = r'C:\cygwin\home\kovid\win32\imagemagick' +FONTCONFIG_DIR = r'C:\cygwin\home\kovid\win32\etc' +VC90 = r'C:\Program Files\Microsoft Visual Studio 9.0\VC\redist\x86\Microsoft.VC90.CRT' # ModuleFinder can't handle runtime changes to __path__, but win32com uses them import sys @@ -98,25 +95,17 @@ class BuildEXE(py2exe.build_exe.py2exe): shutil.copyfile(f, os.path.join(tdir, os.path.basename(f))) print '\tAdding unrar' shutil.copyfile(LIBUNRAR, os.path.join(PY2EXE_DIR, os.path.basename(LIBUNRAR))) - print '\tAdding poppler' - for x in ('bin\\pdftohtml.exe', 'bin\\poppler-qt4.dll', - 'bin\\freetype.dll', 'bin\\jpeg62.dll'): - shutil.copyfile(os.path.join(POPPLER, x), - os.path.join(PY2EXE_DIR, os.path.basename(x))) - print '\tAdding podofo' - for f in glob.glob(os.path.join(PODOFO, '*.dll')): - shutil.copyfile(f, os.path.join(PY2EXE_DIR, os.path.basename(f))) - + print '\tAdding Binaries' + for x in glob.glob(os.path.join(BINARIES, '*.dll')) + \ + [os.path.join(BINARIES, 'pdftohtml.exe')] + \ + glob.glob(os.path.join(BINARIES, '*.manifest')): + shutil.copyfile(x, os.path.join(PY2EXE_DIR, os.path.basename(x))) print '\tAdding ImageMagick' for f in os.listdir(IMAGEMAGICK_DIR): shutil.copyfile(os.path.join(IMAGEMAGICK_DIR, f), os.path.join(PY2EXE_DIR, f)) print '\tCopying fontconfig' - for f in glob.glob(os.path.join(FONTCONFIG_DIR, '*')): - tgt = os.path.join(PY2EXE_DIR, os.path.basename(f)) - if os.path.isdir(f): - shutil.copytree(f, tgt) - else: - shutil.copyfile(f, tgt) + tgt = os.path.join(PY2EXE_DIR, 'etc') + shutil.copytree(FONTCONFIG_DIR, tgt) print print 'Doing DLL redirection' # See http://msdn.microsoft.com/en-us/library/ms682600(VS.85).aspx @@ -169,8 +158,7 @@ def main(args=sys.argv): 'email.iterators', 'email.generator', 'win32process', 'win32api', 'msvcrt', - 'win32event', 'calibre.ebooks.lrf.any.*', - 'calibre.ebooks.lrf.feeds.*', + 'win32event', 'sqlite3.dump', 'BeautifulSoup', 'pyreadline', 'pydoc', 'IPython.Extensions.*', 'calibre.web.feeds.recipes.*', @@ -183,7 +171,8 @@ def main(args=sys.argv): 'excludes' : ["Tkconstants", "Tkinter", "tcl", "_imagingtk", "ImageTk", "FixTk" ], - 'dll_excludes' : ['mswsock.dll'], + 'dll_excludes' : ['mswsock.dll', 'tcl85.dll', + 'MSVCP90.dll', 'tk85.dll'], }, }, diff --git a/setup.py b/setup.py index 439eef6ce2..a7d5b009ab 100644 --- a/setup.py +++ b/setup.py @@ -71,7 +71,7 @@ if __name__ == '__main__': tag_release, upload_demo, build_linux, build_windows, \ build_osx, upload_installers, upload_user_manual, \ upload_to_pypi, stage3, stage2, stage1, upload, \ - upload_rss, betas + upload_rss, betas, build_linux32, build_linux64 entry_points['console_scripts'].append( 'calibre_postinstall = calibre.linux:post_install') @@ -94,8 +94,8 @@ if __name__ == '__main__': sources=['src/calibre/utils/windows/winutil.c'], libraries=['shell32', 'setupapi'], include_dirs=os.environ.get('INCLUDE', - 'C:/WinDDK/6001.18001/inc/api/;' - 'C:/WinDDK/6001.18001/inc/crt/').split(';'), + 'C:/WinDDK/7600.16385.0/inc/api/;' + 'C:/WinDDK/7600.16385.0/inc/crt/').split(';'), extra_compile_args=['/X'] )) @@ -103,8 +103,8 @@ if __name__ == '__main__': poppler_lib = '/usr/lib' poppler_libs = [] if iswindows: - poppler_inc = r'C:\cygwin\home\kovid\poppler\include\poppler\qt4' - poppler_lib = r'C:\cygwin\home\kovid\poppler\lib' + poppler_inc = r'C:\cygwin\home\kovid\win32\include\poppler\qt4' + poppler_lib = r'C:\cygwin\home\kovid\win32\lib' poppler_libs = ['QtCore4', 'QtGui4'] if isosx: poppler_inc = '/Volumes/sw/build/poppler-0.10.7/qt4/src' @@ -124,9 +124,10 @@ if __name__ == '__main__': print 'POPPLER_LIB_DIR environment variables.' podofo_inc = '/usr/include/podofo' if islinux else \ - 'C:\\podofo\\include\\podofo' if iswindows else \ + r'C:\cygwin\home\kovid\win32\include\podofo' if iswindows else \ '/usr/local/include/podofo' - podofo_lib = '/usr/lib' if islinux else r'C:\podofo' if iswindows else \ + podofo_lib = '/usr/lib' if islinux else \ + r'C:\cygwin\home\kovid\win32\lib' if iswindows else \ '/usr/local/lib' podofo_inc = os.environ.get('PODOFO_INC_DIR', podofo_inc) if os.path.exists(os.path.join(podofo_inc, 'podofo.h')): @@ -141,10 +142,10 @@ if __name__ == '__main__': print 'PODOFO_LIB_DIR environment variables.' fc_inc = '/usr/include/fontconfig' if islinux else \ - r'C:\cygwin\home\kovid\fontconfig\include\fontconfig' if iswindows else \ + r'C:\cygwin\home\kovid\win32\include\fontconfig' if iswindows else \ '/Users/kovid/fontconfig/include/fontconfig' fc_lib = '/usr/lib' if islinux else \ - r'C:\cygwin\home\kovid\fontconfig\lib' if iswindows else \ + r'C:\cygwin\home\kovid\win32\lib' if iswindows else \ '/Users/kovid/fontconfig/lib' @@ -258,6 +259,8 @@ if __name__ == '__main__': 'tag_release' : tag_release, 'upload_demo' : upload_demo, 'build_linux' : build_linux, + 'build_linux32' : build_linux32, + 'build_linux64' : build_linux64, 'build_windows' : build_windows, 'build_osx' : build_osx, 'upload_installers': upload_installers, diff --git a/src/calibre/__init__.py b/src/calibre/__init__.py index 2932d71064..bfe101ffa7 100644 --- a/src/calibre/__init__.py +++ b/src/calibre/__init__.py @@ -69,7 +69,7 @@ def osx_version(): return int(m.group(1)), int(m.group(2)), int(m.group(3)) -_filename_sanitize = re.compile(r'[\xae\0\\|\?\*<":>\+\[\]/]') +_filename_sanitize = re.compile(r'[\xae\0\\|\?\*<":>\+/]') def sanitize_file_name(name, substitute='_', as_unicode=False): ''' diff --git a/src/calibre/debug.py b/src/calibre/debug.py index b7fb55f4aa..0b17920cd8 100644 --- a/src/calibre/debug.py +++ b/src/calibre/debug.py @@ -135,7 +135,8 @@ def debug_device_driver(): print 'failed' continue success = True - print 'Main memory:', repr(dev._main_prefix) + if hasattr(dev, '_main_prefix'): + print 'Main memory:', repr(dev._main_prefix) print 'Total space:', dev.total_space() break if not success and errors: @@ -144,6 +145,10 @@ def debug_device_driver(): print dev print msg print + if isosx and os.path.exists('/tmp/ioreg.txt'): + print + print + print "Don't forget to send the file /tmp/ioreg.txt as well" def add_simple_plugin(path_to_plugin): diff --git a/src/calibre/ebooks/conversion/plumber.py b/src/calibre/ebooks/conversion/plumber.py index e2bd1128f5..2261d071ed 100644 --- a/src/calibre/ebooks/conversion/plumber.py +++ b/src/calibre/ebooks/conversion/plumber.py @@ -46,7 +46,8 @@ class Plumber(object): 'tags', 'book_producer', 'language' ] - def __init__(self, input, output, log, report_progress=DummyReporter(), dummy=False): + def __init__(self, input, output, log, report_progress=DummyReporter(), + dummy=False, merge_plugin_recs=True): ''' :param input: Path to input file. :param output: Path to output file/directory @@ -483,7 +484,8 @@ OptionRecommendation(name='language', for x in getattr(self, w): temp.add(x.clone()) setattr(self, w, temp) - self.merge_plugin_recommendations() + if merge_plugin_recs: + self.merge_plugin_recommendations() @classmethod def unarchive(self, path, tdir): diff --git a/src/calibre/ebooks/epub/output.py b/src/calibre/ebooks/epub/output.py index 8955906501..6c38a7d4d3 100644 --- a/src/calibre/ebooks/epub/output.py +++ b/src/calibre/ebooks/epub/output.py @@ -78,7 +78,7 @@ class EPUBOutput(OutputFormatPlugin): ), OptionRecommendation(name='no_default_epub_cover', recommended_value=False, - help=_('Normally, if the input file ahs no cover and you don\'t' + help=_('Normally, if the input file has no cover and you don\'t' ' specify one, a default cover is generated with the title, ' 'authors, etc. This option disables the generation of this cover.')), diff --git a/src/calibre/ebooks/fb2/output.py b/src/calibre/ebooks/fb2/output.py index 67ea135c1b..2bb49318af 100644 --- a/src/calibre/ebooks/fb2/output.py +++ b/src/calibre/ebooks/fb2/output.py @@ -18,10 +18,10 @@ class FB2Output(OutputFormatPlugin): options = set([ OptionRecommendation(name='inline_toc', recommended_value=False, level=OptionRecommendation.LOW, - help=_('Add Table of Contents to begenning of the book.')), + help=_('Add Table of Contents to beginning of the book.')), ]) - def convert(self, oeb_book, output_path, input_plugin, opts, log): + def convert(self, oeb_book, output_path, input_plugin, opts, log): fb2mlizer = FB2MLizer(log) fb2_content = fb2mlizer.extract_content(oeb_book, opts) @@ -33,11 +33,11 @@ class FB2Output(OutputFormatPlugin): out_stream = open(output_path, 'wb') else: out_stream = output_path - + out_stream.seek(0) out_stream.truncate() out_stream.write(fb2_content.encode('utf-8', 'replace')) - + if close: out_stream.close() diff --git a/src/calibre/ebooks/metadata/google_books.py b/src/calibre/ebooks/metadata/google_books.py index 82f11c2010..374c4a8a5f 100644 --- a/src/calibre/ebooks/metadata/google_books.py +++ b/src/calibre/ebooks/metadata/google_books.py @@ -6,6 +6,7 @@ __docformat__ = 'restructuredtext en' import sys, textwrap from urllib import urlencode from functools import partial +from datetime import datetime from lxml import etree from dateutil import parser @@ -151,7 +152,9 @@ class ResultList(list): try: d = date(entry) if d: - d = parser.parse(d[0].text) + default = datetime.utcnow() + default = datetime(default.year, default.month, 1) + d = parser.parse(d[0].text, default=default) else: d = None except: diff --git a/src/calibre/ebooks/oeb/stylizer.py b/src/calibre/ebooks/oeb/stylizer.py index 0c387e3e84..b5d351f886 100644 --- a/src/calibre/ebooks/oeb/stylizer.py +++ b/src/calibre/ebooks/oeb/stylizer.py @@ -92,7 +92,10 @@ class CSSSelector(etree.XPath): def __init__(self, css, namespaces=XPNSMAP): css = self.MIN_SPACE_RE.sub(r'\1', css) - path = css_to_xpath(css) + try: + path = css_to_xpath(css) + except UnicodeEncodeError: # Bug in css_to_xpath + path = '/' path = self.LOCAL_NAME_RE.sub(r"local-name() = '", path) etree.XPath.__init__(self, path, namespaces=namespaces) self.css = css diff --git a/src/calibre/ebooks/pdb/output.py b/src/calibre/ebooks/pdb/output.py index 9a844d2136..06f2f20d10 100644 --- a/src/calibre/ebooks/pdb/output.py +++ b/src/calibre/ebooks/pdb/output.py @@ -29,7 +29,7 @@ class PDBOutput(OutputFormatPlugin): 'formats.')), OptionRecommendation(name='inline_toc', recommended_value=False, level=OptionRecommendation.LOW, - help=_('Add Table of Contents to begenning of the book.')), + help=_('Add Table of Contents to beginning of the book.')), ]) def convert(self, oeb_book, output_path, input_plugin, opts, log): diff --git a/src/calibre/ebooks/pml/output.py b/src/calibre/ebooks/pml/output.py index 970bcd3594..360e63c98e 100644 --- a/src/calibre/ebooks/pml/output.py +++ b/src/calibre/ebooks/pml/output.py @@ -34,7 +34,7 @@ class PMLOutput(OutputFormatPlugin): 'The default is cp1252.')), OptionRecommendation(name='inline_toc', recommended_value=False, level=OptionRecommendation.LOW, - help=_('Add Table of Contents to begenning of the book.')), + help=_('Add Table of Contents to beginning of the book.')), ]) def convert(self, oeb_book, output_path, input_plugin, opts, log): diff --git a/src/calibre/ebooks/rb/output.py b/src/calibre/ebooks/rb/output.py index 0e0bd27326..a16e408b0f 100644 --- a/src/calibre/ebooks/rb/output.py +++ b/src/calibre/ebooks/rb/output.py @@ -18,7 +18,7 @@ class RBOutput(OutputFormatPlugin): options = set([ OptionRecommendation(name='inline_toc', recommended_value=False, level=OptionRecommendation.LOW, - help=_('Add Table of Contents to begenning of the book.')), + help=_('Add Table of Contents to beginning of the book.')), ]) def convert(self, oeb_book, output_path, input_plugin, opts, log): diff --git a/src/calibre/ebooks/txt/output.py b/src/calibre/ebooks/txt/output.py index 38d1b3ebb7..6f0a768b8f 100644 --- a/src/calibre/ebooks/txt/output.py +++ b/src/calibre/ebooks/txt/output.py @@ -32,13 +32,13 @@ class TXTOutput(OutputFormatPlugin): 'formats.')), OptionRecommendation(name='inline_toc', recommended_value=False, level=OptionRecommendation.LOW, - help=_('Add Table of Contents to begenning of the book.')), + help=_('Add Table of Contents to beginning of the book.')), ]) def convert(self, oeb_book, output_path, input_plugin, opts, log): writer = TXTMLizer(log) txt = writer.extract_content(oeb_book, opts) - + log.debug('\tReplacing newlines with selected type...') txt = specified_newlines(TxtNewlines(opts.newline).newline, txt) diff --git a/src/calibre/ebooks/txt/txtml.py b/src/calibre/ebooks/txt/txtml.py index 09f9d5d50c..206dff50ed 100644 --- a/src/calibre/ebooks/txt/txtml.py +++ b/src/calibre/ebooks/txt/txtml.py @@ -32,7 +32,7 @@ BLOCK_STYLES = [ ] class TXTMLizer(object): - + def __init__(self, log): self.log = log @@ -91,7 +91,7 @@ class TXTMLizer(object): # Remove excessive newlines. text = re.sub('\n[ ]+\n', '\n\n', text) - text = re.sub('\n{5,}', '\n\n\n\n', text) + text = re.sub('\n{3,}', '\n\n', text) # Replace spaces at the beginning and end of lines text = re.sub('(?imu)^[ ]+', '', text) diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index eb93602fd1..8564fda328 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -450,12 +450,13 @@ class FileDialog(QObject): if os.path.exists(f): self.selected_files.append(f) if self.selected_files: - self.selected_files = [qstring_to_unicode(q) for q in self.selected_files] - dynamic[self.dialog_name] = os.path.dirname(self.selected_files[0]) + self.selected_files = [unicode(q) for q in self.selected_files] + saved_loc = self.selected_files[0] + if os.path.isfile(saved_loc): + saved_loc = os.path.dirname(saved_loc) + dynamic[self.dialog_name] = saved_loc self.accepted = bool(self.selected_files) - - def get_files(self): if islinux and self.fd.result() != self.fd.Accepted: return tuple() diff --git a/src/calibre/gui2/add.py b/src/calibre/gui2/add.py index 56ed9b661a..93bebf593f 100644 --- a/src/calibre/gui2/add.py +++ b/src/calibre/gui2/add.py @@ -5,7 +5,8 @@ import os, shutil, time from Queue import Queue, Empty from threading import Thread -from PyQt4.Qt import QThread, SIGNAL, QObject, QTimer, Qt +from PyQt4.Qt import QThread, SIGNAL, QObject, QTimer, Qt, \ + QProgressDialog from calibre.gui2.dialogs.progress import ProgressDialog from calibre.gui2 import question_dialog, error_dialog @@ -13,6 +14,25 @@ from calibre.ebooks.metadata.opf2 import OPF from calibre.ebooks.metadata import MetaInformation from calibre.constants import preferred_encoding +class DuplicatesAdder(QThread): + + def __init__(self, parent, db, duplicates, db_adder): + QThread.__init__(self, parent) + self.db, self.db_adder = db, db_adder + self.duplicates = duplicates + + def run(self): + count = 1 + for mi, cover, formats in self.duplicates: + id = self.db.create_book_entry(mi, cover=cover, + add_duplicates=True) + self.db_adder.add_formats(id, formats) + self.db_adder.number_of_books_added += 1 + self.emit(SIGNAL('added(PyQt_PyObject)'), count) + count += 1 + self.emit(SIGNAL('adding_done()')) + + class RecursiveFind(QThread): def __init__(self, parent, db, root, single): @@ -196,15 +216,19 @@ class Adder(QObject): self.callback(self.paths, self.names, self.infos) self.callback_called = True + def duplicates_processed(self): + self.db_adder.end = True + if not self.callback_called: + self.callback(self.paths, self.names, self.infos) + self.callback_called = True + if hasattr(self, '__p_d'): + self.__p_d.hide() + def update(self): if self.entry_count <= 0: self.timer.stop() - self.process_duplicates() self.pd.hide() - self.db_adder.end = True - if not self.callback_called: - self.callback(self.paths, self.names, self.infos) - self.callback_called = True + self.process_duplicates() return try: @@ -240,18 +264,28 @@ class Adder(QObject): def process_duplicates(self): duplicates = self.db_adder.duplicates if not duplicates: - return + return self.duplicates_processed() self.pd.hide() files = [x[0].title for x in duplicates] if question_dialog(self._parent, _('Duplicates found!'), _('Books with the same title as the following already ' 'exist in the database. Add them anyway?'), '\n'.join(files)): - for mi, cover, formats in duplicates: - id = self.db.create_book_entry(mi, cover=cover, - add_duplicates=True) - self.db_adder.add_formats(id, formats) - self.db_adder.number_of_books_added += 1 + pd = QProgressDialog(_('Adding duplicates...'), '', 0, len(duplicates), + self._parent) + pd.setCancelButton(None) + pd.setValue(0) + pd.show() + self.__p_d = pd + self.__d_a = DuplicatesAdder(self._parent, self.db, duplicates, + self.db_adder) + self.connect(self.__d_a, SIGNAL('added(PyQt_PyObject)'), + pd.setValue) + self.connect(self.__d_a, SIGNAL('adding_done()'), + self.duplicates_processed) + self.__d_a.start() + else: + return self.duplicates_processed() def cleanup(self): if hasattr(self, 'pd'): diff --git a/src/calibre/gui2/convert/__init__.py b/src/calibre/gui2/convert/__init__.py index 70223acb70..bbab3a1edd 100644 --- a/src/calibre/gui2/convert/__init__.py +++ b/src/calibre/gui2/convert/__init__.py @@ -34,7 +34,7 @@ class Widget(QWidget): def initialize_options(self, get_option, get_help, db=None, book_id=None): ''' :param get_option: A callable that takes one argument: the option name - and returns the correspoing OptionRecommendation. + and returns the corresponding OptionRecommendation. :param get_help: A callable that takes the option name and return a help string. ''' diff --git a/src/calibre/gui2/convert/structure_detection.py b/src/calibre/gui2/convert/structure_detection.py index 506ee37c45..00c5e30d6b 100644 --- a/src/calibre/gui2/convert/structure_detection.py +++ b/src/calibre/gui2/convert/structure_detection.py @@ -28,11 +28,12 @@ class StructureDetectionWidget(Widget, Ui_Form): 'remove_footer', 'footer_regex'] ) self.db, self.book_id = db, book_id + for x in ('pagebreak', 'rule', 'both', 'none'): + self.opt_chapter_mark.addItem(x) self.initialize_options(get_option, get_help, db, book_id) self.opt_chapter.set_msg(_('Detect chapters at (XPath expression):')) self.opt_page_breaks_before.set_msg(_('Insert page breaks before ' '(XPath expression):')) - def pre_commit_check(self): for x in ('header_regex', 'footer_regex'): x = getattr(self, 'opt_'+x) diff --git a/src/calibre/gui2/convert/structure_detection.ui b/src/calibre/gui2/convert/structure_detection.ui index eebc0f0d53..6952abce96 100644 --- a/src/calibre/gui2/convert/structure_detection.ui +++ b/src/calibre/gui2/convert/structure_detection.ui @@ -29,26 +29,6 @@ - - - pagebreak - - - - - rule - - - - - both - - - - - none - - diff --git a/src/calibre/gui2/dialogs/add_save.py b/src/calibre/gui2/dialogs/add_save.py deleted file mode 100644 index 7bd6273b4a..0000000000 --- a/src/calibre/gui2/dialogs/add_save.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env python -# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai -from __future__ import with_statement - -__license__ = 'GPL v3' -__copyright__ = '2009, Kovid Goyal ' -__docformat__ = 'restructuredtext en' - -import textwrap - -from PyQt4.Qt import QTabWidget - -from calibre.gui2.dialogs.add_save_ui import Ui_TabWidget -from calibre.library.save_to_disk import config, FORMAT_ARG_DESCS - - -class AddSave(QTabWidget, Ui_TabWidget): - - def __init__(self, parent=None): - QTabWidget.__init__(self, parent) - self.setupUi(self) - c = config() - opts = c.parse() - for x in ('asciiize', 'update_metadata', 'save_cover', 'write_opf'): - g = getattr(self, 'opt_'+x) - g.setChecked(getattr(opts, x)) - help = '\n'.join(textwrap.wrap(c.get_option(x).help, 75)) - g.setToolTip(help) - g.setWhatsThis(help) - - for x in ('formats', 'timefmt'): - g = getattr(self, 'opt_'+x) - g.setText(getattr(opts, x)) - help = '\n'.join(textwrap.wrap(c.get_option(x).help, 75)) - g.setToolTip(help) - g.setWhatsThis(help) - - help = '\n'.join(textwrap.wrap(c.get_option('template').help, 75)) - self.opt_template.initialize('save_to_disk_template_history', - opts.template, help=help) - - variables = sorted(FORMAT_ARG_DESCS.keys()) - rows = [] - for var in variables: - rows.append(u'%s%s'% - (var, FORMAT_ARG_DESCS[var])) - table = u'%s
'%(u'\n'.join(rows)) - self.template_variables.setText(table) - - def save_settings(self): - c = config() - for x in ('asciiize', 'update_metadata', 'save_cover', 'write_opf'): - c.set(x, getattr(self, 'opt_'+x).isChecked()) - for x in ('formats', 'template', 'timefmt'): - c.set(x, unicode(getattr(self, 'opt_'+x).text()).strip()) - - - -if __name__ == '__main__': - from PyQt4.Qt import QApplication - app=QApplication([]) - a = AddSave() - a.show() - app.exec_() - a.save_settings() - diff --git a/src/calibre/gui2/dialogs/config/__init__.py b/src/calibre/gui2/dialogs/config/__init__.py index d719f24d66..6cc77725e8 100644 --- a/src/calibre/gui2/dialogs/config/__init__.py +++ b/src/calibre/gui2/dialogs/config/__init__.py @@ -41,7 +41,8 @@ class ConfigTabs(QTabWidget): log = Log() log.outputs = [] - self.plumber = Plumber('dummy.epub', 'dummy.epub', log, dummy=True) + self.plumber = Plumber('dummy.epub', 'dummy.epub', log, dummy=True, + merge_plugin_recs=False) def widget_factory(cls): return cls(self, self.plumber.get_option_by_name, @@ -298,7 +299,8 @@ class EmailAccounts(QAbstractTableModel): while y in self.accounts: c += 1 y = x + str(c) - self.accounts[y] = ['MOBI, EPUB', True, + auto_send = len(self.accounts) < 1 + self.accounts[y] = ['MOBI, EPUB', auto_send, len(self.account_order) == 0] self.account_order = sorted(self.accounts.keys()) self.reset() @@ -756,7 +758,8 @@ class CheckIntegrity(QProgressDialog): def __init__(self, db, parent=None): QProgressDialog.__init__(self, parent) - self.setCancelButtonText('') + self.db = db + self.setCancelButton(None) self.setMinimum(0) self.setMaximum(100) self.setWindowTitle(_('Checking database integrity')) diff --git a/src/calibre/gui2/dialogs/config/add_save.py b/src/calibre/gui2/dialogs/config/add_save.py index 6d91745044..71e7c83a62 100644 --- a/src/calibre/gui2/dialogs/config/add_save.py +++ b/src/calibre/gui2/dialogs/config/add_save.py @@ -26,7 +26,8 @@ class AddSave(QTabWidget, Ui_TabWidget): self.removeTab(2) c = config() opts = c.parse() - for x in ('asciiize', 'update_metadata', 'save_cover', 'write_opf'): + for x in ('asciiize', 'update_metadata', 'save_cover', 'write_opf', + 'replace_whitespace', 'to_lowercase'): g = getattr(self, 'opt_'+x) g.setChecked(getattr(opts, x)) help = '\n'.join(textwrap.wrap(c.get_option(x).help, 75)) @@ -74,7 +75,8 @@ class AddSave(QTabWidget, Ui_TabWidget): if not self.validate(): return False c = config() - for x in ('asciiize', 'update_metadata', 'save_cover', 'write_opf'): + for x in ('asciiize', 'update_metadata', 'save_cover', 'write_opf', + 'replace_whitespace', 'to_lowercase'): c.set(x, getattr(self, 'opt_'+x).isChecked()) for x in ('formats', 'template', 'timefmt'): c.set(x, unicode(getattr(self, 'opt_'+x).text()).strip()) diff --git a/src/calibre/gui2/dialogs/config/add_save.ui b/src/calibre/gui2/dialogs/config/add_save.ui index 6faec64626..513be73e54 100644 --- a/src/calibre/gui2/dialogs/config/add_save.ui +++ b/src/calibre/gui2/dialogs/config/add_save.ui @@ -77,14 +77,14 @@
- + Save &cover separately - + Update &metadata in saved copies @@ -163,6 +163,20 @@ + + + + Replace space with &underscores + + + + + + + Change paths to &lowercase + + + diff --git a/src/calibre/gui2/images/news/axxon_news.png b/src/calibre/gui2/images/news/axxon_news.png new file mode 100644 index 0000000000..c8b62364ef Binary files /dev/null and b/src/calibre/gui2/images/news/axxon_news.png differ diff --git a/src/calibre/gui2/images/news/darknet.png b/src/calibre/gui2/images/news/darknet.png new file mode 100644 index 0000000000..a64867c3e7 Binary files /dev/null and b/src/calibre/gui2/images/news/darknet.png differ diff --git a/src/calibre/gui2/images/news/livemint.png b/src/calibre/gui2/images/news/livemint.png new file mode 100644 index 0000000000..5fb3cf7e89 Binary files /dev/null and b/src/calibre/gui2/images/news/livemint.png differ diff --git a/src/calibre/gui2/main.py b/src/calibre/gui2/main.py index 98ae6a3796..8d2abd77d7 100644 --- a/src/calibre/gui2/main.py +++ b/src/calibre/gui2/main.py @@ -14,8 +14,9 @@ from PyQt4.Qt import Qt, SIGNAL, QObject, QCoreApplication, QUrl, QTimer, \ QMessageBox, QStackedLayout from PyQt4.QtSvg import QSvgRenderer -from calibre import __version__, __appname__, \ - iswindows, isosx, prints, patheq +from calibre import prints, patheq +from calibre.constants import __version__, __appname__, \ + iswindows, isosx, filesystem_encoding from calibre.utils.filenames import ascii_filename from calibre.ptempfile import PersistentTemporaryFile from calibre.utils.config import prefs, dynamic @@ -424,9 +425,18 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI): error_dialog(self, _('Bad database location'), _('Bad database location')+':'+self.library_path, det_msg=traceback.format_exc()).exec_() + fname = _('Calibre Library') + if isinstance(fname, unicode): + try: + fname = fname.encode(filesystem_encoding) + except: + fname = 'Calibre Library' + x = os.path.expanduser('~'+os.sep+fname) + if not os.path.exists(x): + os.makedirs(x) dir = unicode(QFileDialog.getExistingDirectory(self, _('Choose a location for your ebook library.'), - os.path.expanduser('~'))) + x)) if not dir: QCoreApplication.exit(1) raise SystemExit(1) @@ -1493,6 +1503,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI): self.search.clear_to_help() self.status_bar.reset_info() self.library_view.sortByColumn(3, Qt.DescendingOrder) + self.library_view.model().count_changed() ############################################################################ @@ -1565,14 +1576,23 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI): self.device_error_dialog.show() def job_exception(self, job): + if not hasattr(self, '_modeless_dialogs'): + self._modeless_dialogs = [] + if self.isVisible(): + for x in list(self._modeless_dialogs): + if not x.isVisible(): + self._modeless_dialogs.remove(x) try: if 'calibre.ebooks.DRMError' in job.details: - error_dialog(self, _('Conversion Error'), + d = error_dialog(self, _('Conversion Error'), _('

Could not convert: %s

It is a ' 'DRMed book. You must first remove the ' 'DRM using 3rd party tools.')%\ (job.description.split(':')[-1], - 'http://wiki.mobileread.com/wiki/DRM')).exec_() + 'http://wiki.mobileread.com/wiki/DRM')) + d.setModal(False) + d.show() + self._modeless_dialogs.append(d) return except: pass @@ -1582,9 +1602,12 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI): prints(job.details, file=sys.stderr) except: pass - error_dialog(self, _('Conversion Error'), + d = error_dialog(self, _('Conversion Error'), _('Failed')+': '+unicode(job.description), - det_msg=job.details).exec_() + det_msg=job.details) + d.setModal(False) + d.show() + self._modeless_dialogs.append(d) def initialize_database(self): diff --git a/src/calibre/gui2/tag_view.py b/src/calibre/gui2/tag_view.py index 2e8bf3c910..9adbb5aad6 100644 --- a/src/calibre/gui2/tag_view.py +++ b/src/calibre/gui2/tag_view.py @@ -93,7 +93,7 @@ class TagsModel(QStandardItemModel): QIcon(':/images/minus.svg')] QStandardItemModel.__init__(self) self.db = db - self.ignore_next_search = False + self.ignore_next_search = 0 self._data = {} self.bold_font = QFont() self.bold_font.setBold(True) @@ -129,19 +129,20 @@ class TagsModel(QStandardItemModel): self.refresh() def reinit(self, *args, **kwargs): - if not self.ignore_next_search: + if self.ignore_next_search == 0: for category in self._data.values(): for tag in category: tag.state = 0 self.reset() - self.ignore_next_search = False + else: + self.ignore_next_search -= 1 def toggle(self, index): if index.parent().isValid(): category = self.row_map[index.parent().row()] tag = self._data[category][index.row()] self.invisibleRootItem().child(index.parent().row()).child(index.row()).toggle() - self.ignore_next_search = True + self.ignore_next_search = 2 self.emit(SIGNAL('dataChanged(QModelIndex,QModelIndex)'), index, index) return True return False diff --git a/src/calibre/gui2/wizard/__init__.py b/src/calibre/gui2/wizard/__init__.py index 68e866e7e1..7045f6334d 100644 --- a/src/calibre/gui2/wizard/__init__.py +++ b/src/calibre/gui2/wizard/__init__.py @@ -17,6 +17,7 @@ from calibre import __appname__, patheq from calibre.library.database2 import LibraryDatabase2 from calibre.library.move import MoveLibrary from calibre.resources import server_resources +from calibre.constants import filesystem_encoding from calibre.gui2.wizard.send_email import smtp_prefs from calibre.gui2.wizard.device_ui import Ui_WizardPage as DeviceUI from calibre.gui2.wizard.library_ui import Ui_WizardPage as LibraryUI @@ -423,7 +424,8 @@ def move_library(oldloc, newloc, parent, callback_on_complete): callback) else: rq = Queue() - m = MoveLibrary(oldloc, newloc, db.data.count(), rq) + m = MoveLibrary(oldloc, newloc, + len(db.get_top_level_move_items()[0]), rq) global _mm _mm = MoveMonitor(m, rq, callback, parent) return @@ -473,7 +475,18 @@ class LibraryPage(QWizardPage, LibraryUI): def initializePage(self): lp = prefs['library_path'] if not lp: - lp = os.path.expanduser('~') + fname = _('Calibre Library') + if isinstance(fname, unicode): + try: + fname = fname.encode(filesystem_encoding) + except: + fname = 'Calibre Library' + lp = os.path.expanduser('~'+os.sep+fname) + if not os.path.exists(lp): + try: + os.makedirs(lp) + except: + lp = os.path.expanduser('~') self.location.setText(lp) def isComplete(self): diff --git a/src/calibre/library/cli.py b/src/calibre/library/cli.py index 81d293a1cb..0a392c1b8c 100644 --- a/src/calibre/library/cli.py +++ b/src/calibre/library/cli.py @@ -183,9 +183,7 @@ def do_list(db, fields, sort_by, ascending, search_text, line_width, separator, return template.generate(id="urn:calibre:main", data=data, subtitle=subtitle, sep=os.sep, quote=quote, updated=db.last_modified()).render('xml') - - -def command_list(args, dbpath): +def list_option_parser(): parser = get_parser(_( '''\ %prog list [options] @@ -208,6 +206,11 @@ List the books available in the calibre database. of = ['text', 'xml', 'stanza'] parser.add_option('--output-format', choices=of, default='text', help=_('The format in which to output the data. Available choices: %s. Defaults is text.')%of) + return parser + + +def command_list(args, dbpath): + parser = list_option_parser() opts, args = parser.parse_args(sys.argv[:1] + args) fields = [str(f.strip().lower()) for f in opts.fields.split(',')] if 'all' in fields: @@ -316,9 +319,7 @@ def do_add(db, paths, one_book_per_directory, recurse, add_duplicates): finally: sys.stdout = orig - - -def command_add(args, dbpath): +def add_option_parser(): parser = get_parser(_( '''\ %prog add [options] file1 file2 file3 ... @@ -333,6 +334,11 @@ the directory related options below. help=_('Process directories recursively')) parser.add_option('-d', '--duplicates', action='store_true', default=False, help=_('Add books to database even if they already exist. Comparison is done based on book titles.')) + return parser + + +def command_add(args, dbpath): + parser = add_option_parser() opts, args = parser.parse_args(sys.argv[:1] + args) if len(args) < 2: parser.print_help() @@ -353,9 +359,8 @@ def do_remove(db, ids): if send_message is not None: send_message('refreshdb:', 'calibre GUI') - -def command_remove(args, dbpath): - parser = get_parser(_( +def remove_option_parser(): + return get_parser(_( '''\ %prog remove ids @@ -363,6 +368,9 @@ Remove the books identified by ids from the database. ids should be a comma sepa list of id numbers (you can get id numbers by using the list command). For example, \ 23,34,57-85 ''')) + +def command_remove(args, dbpath): + parser = remove_option_parser() opts, args = parser.parse_args(sys.argv[:1] + args) if len(args) < 2: parser.print_help() @@ -385,15 +393,18 @@ list of id numbers (you can get id numbers by using the list command). For examp def do_add_format(db, id, fmt, path): db.add_format_with_hooks(id, fmt.upper(), path, index_is_id=True) - -def command_add_format(args, dbpath): - parser = get_parser(_( +def add_format_option_parser(): + return get_parser(_( '''\ %prog add_format [options] id ebook_file Add the ebook in ebook_file to the available formats for the logical book identified \ by id. You can get id by using the list command. If the format already exists, it is replaced. ''')) + + +def command_add_format(args, dbpath): + parser = add_format_option_parser() opts, args = parser.parse_args(sys.argv[:1] + args) if len(args) < 3: parser.print_help() @@ -410,8 +421,8 @@ by id. You can get id by using the list command. If the format already exists, i def do_remove_format(db, id, fmt): db.remove_format(id, fmt, index_is_id=True) -def command_remove_format(args, dbpath): - parser = get_parser(_( +def remove_format_option_parser(): + return get_parser(_( ''' %prog remove_format [options] id fmt @@ -420,6 +431,10 @@ You can get id 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. ''')) + + +def command_remove_format(args, dbpath): + parser = remove_format_option_parser() opts, args = parser.parse_args(sys.argv[:1] + args) if len(args) < 3: parser.print_help() @@ -441,7 +456,7 @@ def do_show_metadata(db, id, as_opf): else: print unicode(mi).encode(preferred_encoding) -def command_show_metadata(args, dbpath): +def show_metadata_option_parser(): parser = get_parser(_( ''' %prog show_metadata [options] id @@ -451,6 +466,10 @@ 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)')) + return parser + +def command_show_metadata(args, dbpath): + parser = show_metadata_option_parser() opts, args = parser.parse_args(sys.argv[1:]+args) if len(args) < 2: parser.print_help() @@ -468,8 +487,8 @@ def do_set_metadata(db, id, stream): if send_message is not None: send_message('refreshdb:', 'calibre GUI') -def command_set_metadata(args, dbpath): - parser = get_parser(_( +def set_metadata_option_parser(): + return get_parser(_( ''' %prog set_metadata [options] id /path/to/metadata.opf @@ -478,6 +497,9 @@ 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. ''')) + +def command_set_metadata(args, dbpath): + parser = set_metadata_option_parser() opts, args = parser.parse_args(sys.argv[1:]+args) if len(args) < 3: parser.print_help() @@ -501,7 +523,7 @@ def do_export(db, ids, dir, opts): prints('\t'+'\n\t'.join(tb.splitlines())) prints(' ') -def command_export(args, dbpath): +def export_option_parser(): parser = get_parser(_('''\ %prog export [options] ids @@ -530,6 +552,16 @@ an opf file). You can get id numbers from the list command. parser.add_option(switch, default=opt.default, help=opt.help, dest=pref) + for pref in ('replace_whitespace', 'to_lowercase'): + opt = c.get_option(pref) + switch = '--'+pref.replace('_', '-') + parser.add_option(switch, default=False, action='store_true', + help=opt.help) + + return parser + +def command_export(args, dbpath): + parser = export_option_parser() opts, args = parser.parse_args(sys.argv[1:]+args) if (len(args) < 2 and not opts.all): parser.print_help() @@ -541,9 +573,11 @@ an opf file). You can get id numbers from the list command. do_export(get_db(dbpath, opts), ids, dir, opts) 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', 'export') + + +def option_parser(): parser = OptionParser(_( '''\ %%prog command [options] [arguments] @@ -555,11 +589,16 @@ command is one of: For help on an individual command: %%prog command --help ''' - )%'\n '.join(commands)) + )%'\n '.join(COMMANDS)) + return parser + + +def main(args=sys.argv): + parser = option_parser() if len(args) < 2: parser.print_help() return 1 - if args[1] not in commands: + if args[1] not in COMMANDS: if args[1] == '--version': parser.print_version() return 0 diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index c605be0879..54607d1d11 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -1444,14 +1444,33 @@ class LibraryDatabase2(LibraryDatabase): if notify: self.notify('add', [id]) + def get_top_level_move_items(self): + items = set(os.listdir(self.library_path)) + paths = set([]) + for x in self.data.universal_set(): + path = self.path(x, index_is_id=True) + path = path.split(os.sep)[0] + paths.add(path) + paths.add('metadata.db') + path_map = {} + for x in paths: + path_map[x] = x + if not self.is_case_sensitive: + for x in items: + path_map[x.lower()] = x + items = set(path_map) + paths = set([x.lower() for x in paths]) + items = items.intersection(paths) + return items, path_map + def move_library_to(self, newloc, progress=lambda x: x): if not os.path.exists(newloc): os.makedirs(newloc) - items = os.listdir(self.library_path) old_dirs = set([]) - for i, x in enumerate(items): + items, path_map = self.get_top_level_move_items() + for x in items: src = os.path.join(self.library_path, x) - dest = os.path.join(newloc, x) + dest = os.path.join(newloc, path_map[x]) if os.path.isdir(src): if os.path.exists(dest): shutil.rmtree(dest) @@ -1461,6 +1480,7 @@ class LibraryDatabase2(LibraryDatabase): if os.path.exists(dest): os.remove(dest) shutil.copyfile(src, dest) + x = path_map[x] if not isinstance(x, unicode): x = x.decode(filesystem_encoding, 'replace') progress(x) @@ -1675,13 +1695,12 @@ books_series_link feeds user_version = self.user_version sql = self.conn.dump() self.conn.close() - dest = self.dbpath+'.old' + dest = self.dbpath+'.tmp' if os.path.exists(dest): os.remove(dest) - shutil.copyfile(self.dbpath, dest) + conn = None try: - os.remove(self.dbpath) - ndb = DBThread(self.dbpath, None) + ndb = DBThread(dest, None) ndb.connect() conn = ndb.conn conn.executescript(sql) @@ -1690,15 +1709,21 @@ books_series_link feeds conn.commit() conn.close() except: - if os.path.exists(self.dbpath): - os.remove(self.dbpath) - shutil.copyfile(dest, self.dbpath) - os.remove(dest) + if conn is not None: + try: + conn.close() + except: + pass + if os.path.exists(dest): + os.remove(dest) raise else: - os.remove(dest) + os.remove(self.dbpath) + shutil.copyfile(dest, self.dbpath) self.connect() self.refresh() + if os.path.exists(dest): + os.remove(dest) callback(0.1, _('Checking for missing files.')) bad = {} us = self.data.universal_set() diff --git a/src/calibre/library/save_to_disk.py b/src/calibre/library/save_to_disk.py index cafe8addc8..c404bd9223 100644 --- a/src/calibre/library/save_to_disk.py +++ b/src/calibre/library/save_to_disk.py @@ -6,7 +6,7 @@ __license__ = 'GPL v3' __copyright__ = '2009, Kovid Goyal ' __docformat__ = 'restructuredtext en' -import os, traceback, cStringIO +import os, traceback, cStringIO, re from calibre.utils.config import Config, StringConfig from calibre.utils.filenames import shorten_components_to, supports_long_names, \ @@ -17,7 +17,7 @@ from calibre.constants import preferred_encoding, filesystem_encoding from calibre import strftime -DEFAULT_TEMPLATE = '{author_sort}/{title} - {authors}' +DEFAULT_TEMPLATE = '{author_sort}/{title}/{title} - {authors}' FORMAT_ARG_DESCS = dict( title=_('The title'), authors=_('The authors'), @@ -71,6 +71,10 @@ def config(defaults=None): x('timefmt', default='%b, %Y', help=_('The format in which to display dates. %d - day, %b - month, ' '%Y - year. Default is: %b, %Y')) + x('to_lowercase', default=False, + help=_('Convert paths to lowercase.')) + x('replace_whitespace', default=False, + help=_('Replace whitespace with underscores.')) return c def preprocess_template(template): @@ -81,7 +85,9 @@ def preprocess_template(template): template = template.decode(preferred_encoding, 'replace') return template -def get_components(template, mi, id, timefmt='%b %Y', length=250, sanitize_func=ascii_filename): +def get_components(template, mi, id, timefmt='%b %Y', length=250, + sanitize_func=ascii_filename, replace_whitespace=False, + to_lowercase=False): format_args = dict(**FORMAT_ARGS) if mi.title: format_args['title'] = mi.title @@ -113,6 +119,11 @@ def get_components(template, mi, id, timefmt='%b %Y', length=250, sanitize_func= components = [str(id)] components = [x.encode(filesystem_encoding, 'replace') if isinstance(x, unicode) else x for x in components] + if to_lowercase: + components = [x.lower() for x in components] + if replace_whitespace: + components = [re.sub(r'\s', '_', x) for x in components] + return shorten_components_to(length, components) @@ -134,7 +145,9 @@ def save_book_to_disk(id, db, root, opts, length): return True, id, mi.title components = get_components(opts.template, mi, id, opts.timefmt, length, - ascii_filename if opts.asciiize else sanitize_file_name) + ascii_filename if opts.asciiize else sanitize_file_name, + to_lowercase=opts.to_lowercase, + replace_whitespace=opts.replace_whitespace) base_path = os.path.join(root, *components) base_name = os.path.basename(base_path) dirpath = os.path.dirname(base_path) diff --git a/src/calibre/library/server.py b/src/calibre/library/server.py index 10656a6df8..4ab254555e 100644 --- a/src/calibre/library/server.py +++ b/src/calibre/library/server.py @@ -110,7 +110,7 @@ class LibraryServer(object): ${authors} urn:calibre:${record[FM['id']]} ${timestamp} - + ''')) @@ -120,7 +120,7 @@ class LibraryServer(object): calibre Library $id ${updated.strftime('%Y-%m-%dT%H:%M:%S+00:00')} - + calibre http://calibre.kovidgoyal.net @@ -140,7 +140,7 @@ class LibraryServer(object): calibre Library $id ${updated.strftime('%Y-%m-%dT%H:%M:%S+00:00')} - + calibre http://calibre.kovidgoyal.net @@ -152,19 +152,19 @@ class LibraryServer(object): By Author urn:uuid:fc000fa0-8c23-11de-a31d-0002a5d5c51b ${updated.strftime('%Y-%m-%dT%H:%M:%S+00:00')} - + By Title urn:uuid:1df4fe40-8c24-11de-b4c6-0002a5d5c51b ${updated.strftime('%Y-%m-%dT%H:%M:%S+00:00')} - + By Newest urn:uuid:3c6d4940-8c24-11de-a4d7-0002a5d5c51b ${updated.strftime('%Y-%m-%dT%H:%M:%S+00:00')} - + ''')) @@ -460,8 +460,11 @@ class LibraryServer(object): @expose def index(self, **kwargs): 'The / URL' - want_opds = cherrypy.request.headers.get('Stanza-Device-Name', 919) != \ - 919 or cherrypy.request.headers.get('Want-OPDS-Catalog', 919) != 919 + ua = cherrypy.request.headers.get('User-Agent', '').strip() + want_opds = \ + cherrypy.request.headers.get('Stanza-Device-Name', 919) != 919 or \ + cherrypy.request.headers.get('Want-OPDS-Catalog', 919) != 919 or \ + ua.startswith('Stanza') return self.stanza(search=kwargs.get('search', None), sortby=kwargs.get('sortby',None), authorid=kwargs.get('authorid',None)) if want_opds else self.static('index.html') diff --git a/src/calibre/library/sqlite.py b/src/calibre/library/sqlite.py index a1caf506d9..3c23dd67cc 100644 --- a/src/calibre/library/sqlite.py +++ b/src/calibre/library/sqlite.py @@ -157,6 +157,8 @@ class DatabaseException(Exception): def proxy(fn): ''' Decorator to call methods on the database connection in the proxy thread ''' def run(self, *args, **kwargs): + if self.closed: + raise DatabaseException('Connection closed', '') with global_lock: if self.proxy.unhandled_error[0] is not None: raise DatabaseException(*self.proxy.unhandled_error) @@ -174,10 +176,12 @@ class ConnectionProxy(object): def __init__(self, proxy): self.proxy = proxy + self.closed = False def close(self): - if self.proxy.unhandled_error is None: + if self.proxy.unhandled_error[0] is None: self.proxy.requests.put((self.proxy.CLOSE, [], {})) + self.closed = True @proxy def get(self, query, all=True): pass diff --git a/src/calibre/manual/custom.py b/src/calibre/manual/custom.py index d5bb7efcec..6e519c9b2b 100644 --- a/src/calibre/manual/custom.py +++ b/src/calibre/manual/custom.py @@ -69,6 +69,46 @@ CLI_PREAMBLE='''\ {usage} ''' +def generate_calibredb_help(preamble, info): + from calibre.library.cli import COMMANDS, get_parser + import calibre.library.cli as cli + preamble = preamble[:preamble.find('\n\n\n', preamble.find('code-block'))] + preamble += textwrap.dedent(''' + + :command:`calibredb` is the command line interface to the |app| database. It has + several sub-commands, documented below: + + ''') + + global_parser = get_parser('') + groups = [] + for grp in global_parser.option_groups: + groups.append((grp.title.capitalize(), grp.description, grp.option_list)) + + global_options = '\n'.join(render_options('calibredb', groups, False, False)) + + + lines, toc = [], [] + for cmd in COMMANDS: + parser = getattr(cli, cmd+'_option_parser')() + toc.append(' * :ref:`calibredb-%s`'%cmd) + lines += ['.. _calibredb-'+cmd+':', ''] + lines += [cmd, '~'*20, ''] + usage = parser.usage.strip() + usage = [i for i in usage.replace('%prog', 'calibredb').splitlines()] + cmdline = ' '+usage[0] + usage = usage[1:] + usage = [i.replace(cmd, ':command:`%s`'%cmd) for i in usage] + lines += ['.. code-block:: none', '', cmdline, ''] + lines += usage + groups = [(None, None, parser.option_list)] + lines += [''] + lines += render_options('calibredb '+cmd, groups, False) + lines += [''] + + toc = '\n'.join(toc) + raw = preamble + '\n\n'+toc + '\n\n' + global_options+'\n\n'+'\n'.join(lines) + update_cli_doc(os.path.join('cli', 'calibredb.rst'), raw, info) def generate_ebook_convert_help(preamble, info): from calibre.ebooks.conversion.cli import create_option_parser @@ -125,11 +165,12 @@ def update_cli_doc(path, raw, info): info('creating '+os.path.splitext(os.path.basename(path))[0]) open(path, 'wb').write(raw) -def render_options(cmd, groups, options_header=True): - lines = [] +def render_options(cmd, groups, options_header=True, add_program=True): + lines = [''] if options_header: lines = ['[options]', '-'*15, ''] - lines += ['.. program:: '+cmd, ''] + if add_program: + lines += ['.. program:: '+cmd, ''] for title, desc, options in groups: if title: lines.extend([title, '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~']) @@ -153,6 +194,7 @@ def cli_docs(app): for script in entry_points['console_scripts']: module = script[script.index('=')+1:script.index(':')].strip() cmd = script[:script.index('=')].strip() + if cmd in ('calibre-complete', 'calibre-parallel'): continue module = __import__(module, fromlist=[module.split('.')[-1]]) if hasattr(module, 'option_parser'): documented_cmds.append((cmd, getattr(module, 'option_parser')())) @@ -180,6 +222,8 @@ def cli_docs(app): preamble = CLI_PREAMBLE.format(cmd=cmd, cmdline=cmdline, usage=usage) if cmd == 'ebook-convert': generate_ebook_convert_help(preamble, info) + elif cmd == 'calibredb': + generate_calibredb_help(preamble, info) else: groups = [(None, None, parser.option_list)] for grp in parser.option_groups: diff --git a/src/calibre/manual/faq.rst b/src/calibre/manual/faq.rst index 8b437f174a..36a262ab5c 100644 --- a/src/calibre/manual/faq.rst +++ b/src/calibre/manual/faq.rst @@ -54,11 +54,11 @@ In order to convert a collection of HTML files in a specific oder, you have to c Then just add this HTML file to the GUI and use the convert button to create your ebook. -How do I convert my file containing non-English characters? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +How do I convert my file containing non-English characters, or smart quotes? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ There are two aspects to this problem: 1. Knowing the encoding of the source file: |app| tries to guess what character encoding your source files use, but often, this is impossible, so you need to tell it what encoding to use. This can be done in the GUI via the :guilabel:`Input character encoding` field in the :guilabel:`Look & Feel` section. The command-line tools all have an :option:`--input-encoding` option. - 2. When adding HTML files to |app|, you may need to tell |app| what encoding the files are in. To do this go to Preferences->Plugins->File Type plugins and customize the HTML2Zip plugin, telling it what encoding your HTML files are in. |app| will then automatically convert the HTML files into the UTF-8 encoding when adding them. + 2. When adding HTML files to |app|, you may need to tell |app| what encoding the files are in. To do this go to Preferences->Plugins->File Type plugins and customize the HTML2Zip plugin, telling it what encoding your HTML files are in. Now when you add HTML files to |app| they will be correctly processed. HTML files from different sources often have different encodings, so you may have to change this setting repeatedly. A common encoding for many files from the web is ``cp1252`` and I would suggest you try that first. 3. Embedding fonts: If you are generating an LRF file to read on your SONY Reader, you are limited by the fact that the Reader only supports a few non-English characters in the fonts it comes pre-loaded with. You can work around this problem by embedding a unicode-aware font that supports the character set your file uses into the LRF file. You should embed atleast a serif and a sans-serif font. Be aware that embedding fonts significantly slows down page-turn speed on the reader. @@ -95,13 +95,17 @@ turned into a collection on the reader. Note that the PRS-500 does not support c How do I use |app| with my iPhone? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -First install the Stanza reader on your iPhone from http://www.lexcycle.com . Then, +First install the Stanza reader on your iPhone using iTunes. - * Set the output format for calibre to EPUB (The output format can be set next to the big red heart) + * Set the Preferred Output Format in |app| to EPUB (The output format can be set under Preferences->General) * Convert the books you want to read on your iPhone to EPUB format by selecting them and clicking the Convert button. * Turn on the Content Server in |app|'s preferences and leave |app| running. -Now you should be able to access your books on your iPhone. +Now you should be able to access your books on your iPhone by opening Stanza and going to "Shared Books". Under Shared Books you will see an entry "Book in calibre". If you don't, make sure your iPhone is connected using the WiFi network in your house, not 3G. If the |app| catalog is still not detected in Stanza, you can add it manually in Stanza, by clicking "Online Catalog" and the clicking the plus icon in the lower right corner to add a new catalog. In the Add Catalog screen enter whatever name you like and in the URL field, enter the following:: + + http://192.168.1.2:8080/ + +Replace ``192.168.1.2`` with the local IP address of the computer running |app|. If you have changed the port the |app| content server is running on, you will have to change ``8080`` as well to the new port. The local IP address is the IP address you computer is assigned on your home network. A quick Google search will tell you how to find out your local IP address. Why is my device not detected in linux? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -136,6 +140,12 @@ Why doesn't |app| have a column for foo? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |app| is designed to have columns for the most frequently and widely used fields. If it does not have a coulmn for your favorite field, you can always add a tag to the book for that piece of information. |app| also supports a general purpose "comments" fields for longer items. +How do I move my |app| library from one computer to another? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Simply copy the |app| library folder from the old to the new computer. You can find out what the library folder is by clicking Preferences. The very first item is the path tot he library folder. Now on the new computer, start |app| for the first time. It will run the Welcome Wizard asking you for the location of the |app| library. Point it to the previously copied folder. + +Note that if you are transferring between different types of computers (for example Windows to OS X) then after doing the above you should also go to Preferences->Advanced and click the Check database integrity button. It will warn you about missing files, if any, which you should then transfer by hand. + Content From The Web --------------------- diff --git a/src/calibre/translations/calibre.pot b/src/calibre/translations/calibre.pot index 64111480cd..27084860a1 100644 --- a/src/calibre/translations/calibre.pot +++ b/src/calibre/translations/calibre.pot @@ -5,8 +5,8 @@ msgid "" msgstr "" "Project-Id-Version: calibre 0.6.8\n" -"POT-Creation-Date: 2009-08-21 15:14+MDT\n" -"PO-Revision-Date: 2009-08-21 15:14+MDT\n" +"POT-Creation-Date: 2009-08-25 14:07+MDT\n" +"PO-Revision-Date: 2009-08-25 14:07+MDT\n" "Last-Translator: Automatically generated\n" "Language-Team: LANGUAGE\n" "MIME-Version: 1.0\n" @@ -72,7 +72,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/transforms/jacket.py:86 #: /home/kovid/work/calibre/src/calibre/ebooks/pdb/ereader/writer.py:105 #: /home/kovid/work/calibre/src/calibre/ebooks/pdb/ereader/writer.py:106 -#: /home/kovid/work/calibre/src/calibre/ebooks/pdb/input.py:26 +#: /home/kovid/work/calibre/src/calibre/ebooks/pdb/input.py:33 #: /home/kovid/work/calibre/src/calibre/ebooks/pdb/palmdoc/writer.py:29 #: /home/kovid/work/calibre/src/calibre/ebooks/pdb/ztxt/writer.py:27 #: /home/kovid/work/calibre/src/calibre/ebooks/pdf/manipulate/crop.py:81 @@ -126,7 +126,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/library/database2.py:1093 #: /home/kovid/work/calibre/src/calibre/library/database2.py:1423 #: /home/kovid/work/calibre/src/calibre/library/database2.py:1425 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:1514 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:1525 #: /home/kovid/work/calibre/src/calibre/library/server.py:373 #: /home/kovid/work/calibre/src/calibre/library/server.py:446 #: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:45 @@ -693,234 +693,234 @@ msgstr "" msgid "Output saved to" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:68 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:69 msgid "Level of verbosity. Specify multiple times for greater verbosity." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:75 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:76 msgid "Specify the input profile. The input profile gives the conversion system information on how to interpret various information in the input document. For example resolution dependent lengths (i.e. lengths in pixels). Choices are:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:86 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:87 msgid "Specify the output profile. The output profile tells the conversion system how to optimize the created document for the specified device. In some cases, an output profile is required to produce documents that will work on a device. For example EPUB on the SONY reader. Choices are:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:97 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:98 msgid "The base font size in pts. All font sizes in the produced book will be rescaled based on this size. By choosing a larger size you can make the fonts in the output bigger and vice versa. By default, the base font size is chosen based on the output profile you chose." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:107 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:108 msgid "Mapping from CSS font names to font sizes in pts. An example setting is 12,12,14,16,18,20,22,24. These are the mappings for the sizes xx-small to xx-large, with the final size being for huge fonts. The font rescaling algorithm uses these sizes to intelligently rescale fonts. The default is to use a mapping based on the output profile you chose." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:119 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:120 msgid "Disable all rescaling of font sizes." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:126 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:127 msgid "The line height in pts. Controls spacing between consecutive lines of text. By default no line height manipulation is performed." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:134 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:135 msgid "Some badly designed documents use tables to control the layout of text on the page. When converted these documents often have text that runs off the page and other artifacts. This option will extract the content from the tables and present it in a linear fashion." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:144 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:145 msgid "XPath expression that specifies all tags that should be added to the Table of Contents at level one. If this is specified, it takes precedence over other forms of auto-detection." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:153 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:154 msgid "XPath expression that specifies all tags that should be added to the Table of Contents at level two. Each entry is added under the previous level one entry." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:161 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:162 msgid "XPath expression that specifies all tags that should be added to the Table of Contents at level three. Each entry is added under the previous level two entry." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:169 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:170 msgid "Normally, if the source file already has a Table of Contents, it is used in preference to the auto-generated one. With this option, the auto-generated one is always used." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:177 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:178 msgid "Don't add auto-detected chapters to the Table of Contents." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:184 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:185 msgid "If fewer than this number of chapters is detected, then links are added to the Table of Contents. Default: %default" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:191 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:192 msgid "Maximum number of links to insert into the TOC. Set to 0 to disable. Default is: %default. Links are only added to the TOC if less than the threshold number of chapters were detected." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:199 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:200 msgid "Remove entries from the Table of Contents whose titles match the specified regular expression. Matching entries and all their children are removed." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:210 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:211 msgid "An XPath expression to detect chapter titles. The default is to consider

or

tags that contain the words \"chapter\",\"book\",\"section\" or \"part\" as chapter titles as well as any tags that have class=\"chapter\". The expression used must evaluate to a list of elements. To disable chapter detection, use the expression \"/\". See the XPath Tutorial in the calibre User Manual for further help on using this feature." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:224 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:225 msgid "Specify how to mark detected chapters. A value of \"pagebreak\" will insert page breaks before chapters. A value of \"rule\" will insert a line before chapters. A value of \"none\" will disable chapter marking and a value of \"both\" will use both page breaks and lines to mark chapters." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:234 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:235 msgid "Either the path to a CSS stylesheet or raw CSS. This CSS will be appended to the style rules from the source file, so it can be used to override those rules." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:243 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:244 msgid "An XPath expression. Page breaks are inserted before the specified elements." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:249 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:250 msgid "Set the top margin in pts. Default is %default. Note: 72 pts equals 1 inch" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:254 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:255 msgid "Set the bottom margin in pts. Default is %default. Note: 72 pts equals 1 inch" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:259 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:260 msgid "Set the left margin in pts. Default is %default. Note: 72 pts equals 1 inch" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:264 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:265 msgid "Set the right margin in pts. Default is %default. Note: 72 pts equals 1 inch" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:269 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:270 msgid "Do not force text to be justified in output. Whether text is actually displayed justified or not depends on whether the ebook format and reading device support justification." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:276 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:277 msgid "Remove spacing between paragraphs. Also sets an indent on paragraphs of 1.5em. Spacing removal will not work if the source file does not use paragraphs (

or

tags)." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:283 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:284 msgid "Use the cover detected from the source file in preference to the specified cover." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:289 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:290 msgid "Insert a blank line between paragraphs. Will not work if the source file does not use paragraphs (

or

tags)." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:296 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:297 msgid "Remove the first image from the input ebook. Useful if the first image in the source file is a cover and you are specifying an external cover." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:304 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:305 msgid "Insert the book metadata at the start of the book. This is useful if your ebook reader does not support displaying/searching metadata directly." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:312 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:313 msgid "Attempt to detect and correct hard line breaks and other problems in the source file. This may make things worse, so use with care." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:320 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:321 msgid "Use a regular expression to try and remove the header." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:327 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:328 msgid "The regular expression to use to remove the header." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:333 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:334 msgid "Use a regular expression to try and remove the footer." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:340 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:341 msgid "The regular expression to use to remove the footer." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:347 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:348 msgid "Read metadata from the specified OPF file. Metadata read from this file will override any metadata in the source file." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:354 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:355 msgid "Transliterate unicode characters to an ASCII representation. Use with care because this will replace unicode characters with ASCII. For instance it will replace \"%s\" with \"Mikhail Gorbachiov\". Also, note that in cases where there are multiple representations of a character (characters shared by Chinese and Japanese for instance) the representation used by the largest number of people will be used (Chinese in the previous example)." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:369 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:370 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/cli.py:37 msgid "Set the title." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:373 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:374 msgid "Set the authors. Multiple authors should be separated by ampersands." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:378 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:379 msgid "The version of the title to be used for sorting. " msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:382 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:383 msgid "String to be used when sorting by author. " msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:386 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:387 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/cli.py:51 msgid "Set the cover to the specified file." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:390 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:391 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/cli.py:53 msgid "Set the ebook description." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:394 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:395 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/cli.py:55 msgid "Set the ebook publisher." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:398 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:399 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/cli.py:59 msgid "Set the series this ebook belongs to." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:402 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:403 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/cli.py:61 msgid "Set the index of the book in this series." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:406 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:407 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/cli.py:63 msgid "Set the rating. Should be a number between 1 and 5." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:410 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:411 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/cli.py:65 msgid "Set the ISBN of the book." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:414 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:415 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/cli.py:67 msgid "Set the tags for the book. Should be a comma separated list." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:418 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:419 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/cli.py:69 msgid "Set the book producer." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:422 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:423 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/cli.py:71 msgid "Set the language." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:513 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:515 msgid "Could not find an ebook inside the archive" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:651 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:653 msgid "Converting input to HTML..." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:668 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:670 msgid "Running transforms on ebook..." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:743 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:745 msgid "Creating" msgstr "" @@ -937,7 +937,7 @@ msgid "Split all HTML files larger than this size (in KB). This is necessary as msgstr "" #: /home/kovid/work/calibre/src/calibre/ebooks/epub/output.py:81 -msgid "Normally, if the input file ahs no cover and you don't specify one, a default cover is generated with the title, authors, etc. This option disables the generation of this cover." +msgid "Normally, if the input file has no cover and you don't specify one, a default cover is generated with the title, authors, etc. This option disables the generation of this cover." msgstr "" #: /home/kovid/work/calibre/src/calibre/ebooks/fb2/fb2ml.py:123 @@ -956,7 +956,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/ebooks/pml/output.py:37 #: /home/kovid/work/calibre/src/calibre/ebooks/rb/output.py:21 #: /home/kovid/work/calibre/src/calibre/ebooks/txt/output.py:35 -msgid "Add Table of Contents to begenning of the book." +msgid "Add Table of Contents to beginning of the book." msgstr "" #: /home/kovid/work/calibre/src/calibre/ebooks/html/input.py:242 @@ -1352,27 +1352,27 @@ msgstr "" msgid "Set the BookID in LRF files" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/cli.py:143 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/cli.py:144 msgid "No file specified" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/cli.py:158 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/cli.py:159 msgid "Original metadata" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/cli.py:175 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/cli.py:176 msgid "Changed metadata" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/cli.py:187 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/cli.py:188 msgid "OPF created in" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/cli.py:193 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/cli.py:194 msgid "Cover saved to" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/cli.py:195 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/cli.py:196 msgid "No cover found" msgstr "" @@ -1590,6 +1590,11 @@ msgstr "" msgid "Sidebar" msgstr "" +#: /home/kovid/work/calibre/src/calibre/ebooks/pdb/input.py:22 +#: /home/kovid/work/calibre/src/calibre/ebooks/txt/input.py:21 +msgid "Normally calibre treats blank lines as paragraph markers. With this option it will assume that every line represents a paragraph instead." +msgstr "" + #: /home/kovid/work/calibre/src/calibre/ebooks/pdb/output.py:23 msgid "Format to use inside the pdb container. Choices are:" msgstr "" @@ -1804,10 +1809,6 @@ msgstr "" msgid "This RTF file has a feature calibre does not support. Convert it to HTML first and then try it." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/txt/input.py:21 -msgid "Normally calibre treats blank lines as paragraph markers. With this option it will assume that every line represents a paragraph instead." -msgstr "" - #: /home/kovid/work/calibre/src/calibre/ebooks/txt/output.py:24 msgid "Type of newline to use. Options are %s. Default is 'system'. Use 'old_mac' for compatibility with Mac OS 9 and earlier. For Mac OS X use 'unix'. 'system' will default to the newline type used by this OS." msgstr "" @@ -1906,7 +1907,7 @@ msgid "Default action to perform when send to device button is clicked" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:124 -#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:397 +#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:398 msgid "Copied" msgstr "" @@ -1977,6 +1978,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/convert/fb2_output.py:15 #: /home/kovid/work/calibre/src/calibre/gui2/convert/lrf_output.py:20 #: /home/kovid/work/calibre/src/calibre/gui2/convert/mobi_output.py:16 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/pdb_input.py:13 #: /home/kovid/work/calibre/src/calibre/gui2/convert/pdb_output.py:17 #: /home/kovid/work/calibre/src/calibre/gui2/convert/pdf_input.py:13 #: /home/kovid/work/calibre/src/calibre/gui2/convert/pdf_output.py:18 @@ -1988,6 +1990,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/convert/comic_input.py:16 #: /home/kovid/work/calibre/src/calibre/gui2/convert/fb2_input.py:13 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/pdb_input.py:13 #: /home/kovid/work/calibre/src/calibre/gui2/convert/pdf_input.py:13 #: /home/kovid/work/calibre/src/calibre/gui2/convert/txt_input.py:13 msgid "input" @@ -2002,11 +2005,12 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata_ui.py:165 #: /home/kovid/work/calibre/src/calibre/gui2/convert/mobi_output_ui.py:44 #: /home/kovid/work/calibre/src/calibre/gui2/convert/page_setup_ui.py:110 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/pdb_input_ui.py:28 #: /home/kovid/work/calibre/src/calibre/gui2/convert/pdb_output_ui.py:35 #: /home/kovid/work/calibre/src/calibre/gui2/convert/pdf_input_ui.py:38 #: /home/kovid/work/calibre/src/calibre/gui2/convert/pdf_output_ui.py:39 #: /home/kovid/work/calibre/src/calibre/gui2/convert/rb_output_ui.py:28 -#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection_ui.py:71 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection_ui.py:67 #: /home/kovid/work/calibre/src/calibre/gui2/convert/toc_ui.py:61 #: /home/kovid/work/calibre/src/calibre/gui2/convert/txt_input_ui.py:28 #: /home/kovid/work/calibre/src/calibre/gui2/convert/txt_output_ui.py:35 @@ -2250,6 +2254,7 @@ msgid "Set the metadata. The output file will contain as much of this metadata a msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata.py:134 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:95 msgid "Choose cover for " msgstr "" @@ -2452,6 +2457,15 @@ msgstr "" msgid "&Bottom:" msgstr "" +#: /home/kovid/work/calibre/src/calibre/gui2/convert/pdb_input.py:12 +msgid "PDB Input" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/convert/pdb_input_ui.py:29 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/txt_input_ui.py:29 +msgid "Treat each &line as a paragraph" +msgstr "" + #: /home/kovid/work/calibre/src/calibre/gui2/convert/pdb_output.py:16 msgid "PDB Output" msgstr "" @@ -2522,91 +2536,71 @@ msgstr "" msgid "Fine tune the detection of chapter headings and other document structure." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection.py:32 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection.py:34 msgid "Detect chapters at (XPath expression):" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection.py:33 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection.py:35 msgid "Insert page breaks before (XPath expression):" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection.py:43 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection.py:44 #: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:72 msgid "Invalid regular expression" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection.py:44 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection.py:45 #: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:73 msgid "Invalid regular expression: %s" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection.py:49 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection.py:50 #: /home/kovid/work/calibre/src/calibre/gui2/convert/toc.py:38 msgid "Invalid XPath" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection.py:50 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection.py:51 #: /home/kovid/work/calibre/src/calibre/gui2/convert/toc.py:39 msgid "The XPath expression %s is invalid." msgstr "" #: -#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection_ui.py:72 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection_ui.py:68 msgid "Chapter &mark:" msgstr "" #: -#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection_ui.py:73 -msgid "pagebreak" -msgstr "" - -#: -#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection_ui.py:74 -msgid "rule" -msgstr "" - -#: -#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection_ui.py:75 -msgid "both" -msgstr "" - -#: -#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection_ui.py:76 -msgid "none" -msgstr "" - -#: -#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection_ui.py:77 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection_ui.py:69 msgid "Remove first &image" msgstr "" #: -#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection_ui.py:78 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection_ui.py:70 msgid "Insert &metadata as page at start of book" msgstr "" #: -#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection_ui.py:79 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection_ui.py:71 msgid "&Footer regular expression:" msgstr "" #: -#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection_ui.py:80 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection_ui.py:72 msgid "&Preprocess input file to possibly improve structure detection" msgstr "" #: -#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection_ui.py:81 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection_ui.py:73 msgid "&Header regular expression:" msgstr "" #: -#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection_ui.py:82 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection_ui.py:74 msgid "Remove F&ooter" msgstr "" #: -#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection_ui.py:83 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection_ui.py:75 msgid "Remove H&eader" msgstr "" @@ -2656,10 +2650,6 @@ msgstr "" msgid "TXT Input" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/convert/txt_input_ui.py:29 -msgid "Treat each &line as a paragraph" -msgstr "" - #: /home/kovid/work/calibre/src/calibre/gui2/convert/txt_output.py:16 msgid "TXT Output" msgstr "" @@ -2870,7 +2860,7 @@ msgid "Attached, you will find the e-book" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/device.py:550 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:175 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:177 msgid "by" msgstr "" @@ -2970,7 +2960,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:88 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:89 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:92 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:214 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:216 #: /home/kovid/work/calibre/src/calibre/gui2/library.py:344 #: /home/kovid/work/calibre/src/calibre/gui2/status.py:57 #: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:83 @@ -3009,166 +2999,173 @@ msgstr "" msgid "&Profile:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:168 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:170 msgid "%(plugin_type)s %(plugins)s" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:169 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:171 msgid "plugins" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:194 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:196 msgid "Conversion" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:194 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:196 msgid "General" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:194 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:196 msgid "Interface" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:195 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:197 msgid "Add/Save" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:195 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:197 msgid "" "Email\n" "Delivery" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:196 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:198 msgid "Advanced" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:196 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:198 msgid "" "Content\n" "Server" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:196 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:198 msgid "Plugins" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:214 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:216 msgid "Auto send" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:214 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:216 msgid "Email" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:219 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:221 msgid "Formats to email. The first matching format will be sent." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:220 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:222 msgid "If checked, downloaded news will be automatically mailed
to this email address (provided it is in one of the listed formats)." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:294 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:296 msgid "new email address" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:505 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:508 msgid "No valid plugin path" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:506 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:509 msgid "%s is not a valid plugin path" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:509 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:512 msgid "Choose plugin" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:521 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:524 msgid "Plugin cannot be disabled" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:522 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:525 msgid "The plugin: %s cannot be disabled" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:531 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:534 msgid "Plugin not customizable" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:532 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:535 msgid "Plugin: %s does not need customization" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:556 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:559 msgid "Customize %s" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:566 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:569 msgid "Cannot remove builtin plugin" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:567 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:570 msgid " cannot be removed. It is a builtin plugin. Try disabling it instead." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:600 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:603 msgid "Error log:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:607 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:610 msgid "Access log:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:632 -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:564 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:635 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:574 msgid "Failed to start content server" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:656 -#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:469 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:659 +#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:470 msgid "Select location for books" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:673 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:676 msgid "Invalid size" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:674 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:677 msgid "The size %s is invalid. must be of the form widthxheight" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:718 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:723 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:721 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:726 msgid "Invalid database location" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:719 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:722 msgid "Invalid database location " msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:720 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:723 msgid "
Must be a directory." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:724 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:727 msgid "Invalid database location.
Cannot write to " msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:749 -msgid "Checking..." +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:764 +msgid "Checking database integrity" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:750 -msgid "Checking database integrity. This may take a while." +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:783 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:142 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1005 +#: /home/kovid/work/calibre/src/calibre/utils/ipc/job.py:52 +msgid "Error" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:763 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:784 +msgid "Failed to check database integrity" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:789 msgid "Some inconsistencies found" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:764 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:790 msgid "The following books had formats listed in the database that are not actually available. The entries for the formats have been removed. You should check them manually. This can happen if you manipulate the files in the library folder directly." msgstr "" @@ -4452,150 +4449,149 @@ msgstr "" msgid "Configure" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:58 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:59 msgid "Save single format to disk..." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:125 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:126 msgid "Search (For Advanced Search click the button to the left)" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:141 -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:995 -#: /home/kovid/work/calibre/src/calibre/utils/ipc/job.py:52 -msgid "Error" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:142 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:143 msgid "Error communicating with device" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:156 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:157 msgid "&Restore" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:158 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:159 msgid "&Donate to support calibre" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:163 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:164 msgid "&Restart" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:202 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:203 msgid "

For help visit %s.kovidgoyal.net
" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:205 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:206 msgid "%s: %s by Kovid Goyal %%(version)s
%%(device)s

" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:225 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:226 msgid "Edit metadata individually" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:227 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:228 msgid "Edit metadata in bulk" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:229 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:230 msgid "Download metadata and covers" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:230 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:231 msgid "Download only metadata" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:231 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:232 msgid "Download only covers" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:234 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:235 msgid "Add books from a single directory" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:235 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:236 msgid "Add books from directories, including sub-directories (One book per directory, assumes every ebook file is the same book in a different format)" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:238 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:239 msgid "Add books from directories, including sub directories (Multiple books per directory, assumes every ebook file is a different book)" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:241 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:242 msgid "Add Empty book. (Book entry with no formats)" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:278 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:279 #: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:353 msgid "Save to disk" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:279 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:280 msgid "Save to disk in a single directory" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:280 -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1472 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:281 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1484 msgid "Save only %s format to disk" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:288 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:289 #: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:359 msgid "View" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:289 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:290 msgid "View specific format" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:315 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:316 msgid "Convert individually" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:316 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:317 msgid "Bulk convert" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:331 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:332 msgid "Run welcome wizard" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:365 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:366 msgid "Similar books..." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:424 #: /home/kovid/work/calibre/src/calibre/gui2/main.py:425 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:426 msgid "Bad database location" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/main.py:428 -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1600 +#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:477 +msgid "Calibre Library" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:438 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1612 msgid "Choose a location for your ebook library." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:607 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:617 msgid "Browse by covers" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:714 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:724 msgid "Device: " msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:716 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:726 msgid " detected." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:738 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:748 msgid "Connected " msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:750 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:760 msgid "Device database corrupted" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:751 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:761 msgid "" "\n" "

The database of books on the reader is corrupted. Try the following:\n" @@ -4606,297 +4602,297 @@ msgid "" " " msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:827 -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:870 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:837 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:880 msgid "Uploading books to device." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:835 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:845 msgid "Books" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:836 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:846 msgid "EPUB Books" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:837 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:847 msgid "LRF Books" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:838 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:848 msgid "HTML Books" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:839 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:849 msgid "LIT Books" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:840 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:850 msgid "MOBI Books" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:841 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:851 msgid "Text books" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:842 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:852 msgid "PDF Books" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:843 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:853 msgid "Comics" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:844 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:854 msgid "Archives" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:879 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:889 msgid "Failed to read metadata" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:880 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:890 msgid "Failed to read metadata from the following" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:899 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:909 msgid "The selected books will be permanently deleted and the files removed from your computer. Are you sure?" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:926 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:936 msgid "Deleting books from device." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:957 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:967 msgid "Cannot download metadata" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:958 -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1006 -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1036 -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1061 -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1196 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:968 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1016 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1046 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1071 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1208 msgid "No books selected" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:967 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:977 msgid "covers" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:967 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:977 msgid "metadata" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:969 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:979 msgid "Downloading %s for %d book(s)" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:990 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1000 msgid "Failed to download some metadata" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:991 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1001 msgid "Failed to download metadata for the following:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:994 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1004 msgid "Failed to download metadata:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1005 -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1035 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1015 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1045 msgid "Cannot edit metadata" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1060 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1070 msgid "Cannot save to disk" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1063 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1073 msgid "Choose destination directory" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1088 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1100 msgid "Error while saving" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1089 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1101 msgid "There was an error while saving." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1096 -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1097 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1108 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1109 msgid "Could not save some books" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1098 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1110 msgid "Click the show details button to see which ones." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1117 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1129 msgid "Fetching news from " msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1130 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1142 msgid " fetched." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1195 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1207 msgid "Cannot convert" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1365 -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1384 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1377 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1396 msgid "No book selected" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1365 -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1417 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1377 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1429 msgid "Cannot view" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1371 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1383 msgid "Choose the format to view" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1383 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1395 msgid "Cannot open folder" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1400 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1412 msgid "Multiple Books Selected" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1401 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1413 msgid "You are attempting to open %d books. Opening too many books at once can be slow and have a negative effect on the responsiveness of your computer. Once started the process cannot be stopped until complete. Do you wish to continue?" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1418 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1430 msgid "%s has no available formats." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1456 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1468 msgid "Cannot configure" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1457 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1469 msgid "Cannot configure while there are running jobs." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1501 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1513 msgid "No detailed info available" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1502 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1514 msgid "No detailed information is available for books on the device." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1550 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1562 msgid "Error talking to device" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1551 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1563 msgid "There was a temporary error talking to the device. Please unplug and reconnect the device and or reboot." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1568 -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1583 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1580 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1595 msgid "Conversion Error" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1569 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1581 msgid "

Could not convert: %s

It is a DRMed book. You must first remove the DRM using 3rd party tools." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1584 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1596 msgid "Failed" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1609 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1621 msgid "Invalid library location" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1610 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1622 msgid "Could not access %s. Using %s as the library." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1658 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1670 msgid "is the result of the efforts of many volunteers from all over the world. If you find it useful, please consider donating to support its development." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1682 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1694 msgid "There are active jobs. Are you sure you want to quit?" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1685 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1697 msgid "" " is communicating with the device!
\n" " Quitting may cause corruption on the device.
\n" " Are you sure you want to quit?" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1689 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1701 msgid "WARNING: Active jobs" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1740 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1752 msgid "will keep running in the system tray. To close it, choose Quit in the context menu of the system tray." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1759 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1771 msgid "Latest version: %s" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1767 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1779 msgid "Update available" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1768 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1780 msgid "%s has been updated to version %s. See the new features. Visit the download page?" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1786 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1798 msgid "Use the library located at the specified path." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1788 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1800 msgid "Start minimized to system tray." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1790 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1802 msgid "Log debugging information to console" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1838 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1850 msgid "If you are sure it is not running" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1840 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1852 msgid "Cannot Start " msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1841 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1853 msgid "%s is already running." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1844 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1856 msgid "may be running in the system tray, in the" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1846 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1858 msgid "upper right region of the screen." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1848 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1860 msgid "lower right region of the screen." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1851 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1863 msgid "try rebooting your computer." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1853 -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1873 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1865 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1885 msgid "try deleting the file" msgstr "" @@ -5505,47 +5501,47 @@ msgstr "" msgid "Title Case" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:36 #: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:37 -#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:149 +#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:38 #: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:150 +#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:151 msgid "Default" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:288 +#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:289 msgid "If you use the WordPlayer e-book app on your Android phone, you can access your calibre book collection directly on the device. To do this you have to turn on the content server." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:292 +#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:293 msgid "Remember to leave calibre running as the server only runs as long as calibre is running." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:294 +#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:295 msgid "You have to add the URL http://myhostname:8080 as your calibre library in WordPlayer. Here myhostname should be the fully qualified hostname or the IP address of the computer calibre is running on." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:371 +#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:372 msgid "Moving library..." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:387 #: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:388 +#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:389 msgid "Failed to move library" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:441 +#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:442 msgid "Invalid database" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:442 +#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:443 msgid "

An invalid library already exists at %s, delete it before trying to move the existing library.
Error: %s" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:453 +#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:454 msgid "Could not move library" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:515 +#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:527 msgid "welcome wizard" msgstr "" @@ -5958,18 +5954,30 @@ msgid "" "For help on an individual command: %%prog command --help\n" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/database2.py:1540 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:1551 msgid "

Migrating old database to ebook library in %s

" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/database2.py:1569 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:1580 msgid "Copying %s" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/database2.py:1586 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:1597 msgid "Compacting database" msgstr "" +#: /home/kovid/work/calibre/src/calibre/library/database2.py:1685 +msgid "Checking SQL integrity..." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/library/database2.py:1713 +msgid "Checking for missing files." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/library/database2.py:1737 +msgid "Checked id" +msgstr "" + #: /home/kovid/work/calibre/src/calibre/library/save_to_disk.py:22 msgid "The title" msgstr "" @@ -6058,17 +6066,13 @@ msgstr "" msgid "Password to access your calibre library. Username is " msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/server.py:523 +#: /home/kovid/work/calibre/src/calibre/library/server.py:526 msgid "" "[options]\n" "\n" "Start the calibre content server." msgstr "" -#: /home/kovid/work/calibre/src/calibre/manual/qthelp.py:163 -msgid "Global Module Index" -msgstr "" - #: /home/kovid/work/calibre/src/calibre/utils/config.py:43 msgid "" "%sUsage%s: %s\n" @@ -6209,7 +6213,7 @@ msgid "\tFailed links:" msgstr "" #: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:682 -msgid "Could not fetch article. Run with --debug to see the reason" +msgid "Could not fetch article. Run with -vv to see the reason" msgstr "" #: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:703 @@ -6258,7 +6262,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_instapaper.py:59 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_lamujerdemivida.py:59 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_laprensa_ni.py:63 -#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_monitor.py:65 +#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_monitor.py:78 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_pobjeda.py:83 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_republika.py:66 msgid "Fetching feed" @@ -6299,6 +6303,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_7dias.py:23 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_ambito.py:61 +#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_axxon_news.py:21 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_buenosaireseconomico.py:23 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_clarin.py:26 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_criticadigital.py:17 @@ -6367,6 +6372,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_economist.py:17 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_endgadget.py:16 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_espn.py:17 +#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_esquire.py:25 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_exiled.py:23 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_fastcompany.py:25 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_financial_times.py:16 diff --git a/src/calibre/utils/fonts/__init__.py b/src/calibre/utils/fonts/__init__.py index bc6b117147..f4b4e0fc99 100644 --- a/src/calibre/utils/fonts/__init__.py +++ b/src/calibre/utils/fonts/__init__.py @@ -9,7 +9,7 @@ __docformat__ = 'restructuredtext en' import os, sys from threading import Thread -from calibre.constants import plugins +from calibre.constants import plugins, iswindows _fc, _fc_err = plugins['fontconfig'] @@ -31,6 +31,12 @@ class FontConfig(Thread): if isinstance(config_dir, unicode): config_dir = config_dir.encode(sys.getfilesystemencoding()) config = os.path.join(config_dir, 'fonts.conf') + if iswindows and getattr(sys, 'frozen', False): + config_dir = os.path.join(os.path.dirname(sys.executable), + 'etc', 'fonts') + if isinstance(config_dir, unicode): + config_dir = config_dir.encode(sys.getfilesystemencoding()) + config = os.path.join(config_dir, 'fonts.conf') try: _fc.initialize(config) except: diff --git a/src/calibre/utils/poppler/__init__.py b/src/calibre/utils/poppler/__init__.py index 5c44a99d8b..47d357b933 100644 --- a/src/calibre/utils/poppler/__init__.py +++ b/src/calibre/utils/poppler/__init__.py @@ -22,6 +22,7 @@ def get_metadata(stream, cover=True): raw = stream.read() doc = poppler.PDFDoc() doc.load(raw) + del raw title = doc.title if not title or not title.strip(): title = _('Unknown') @@ -55,7 +56,6 @@ def get_metadata(stream, cover=True): if cdata is not None: mi.cover_data = ('jpg', cdata) del doc - del raw return mi diff --git a/src/calibre/utils/poppler/poppler.cpp b/src/calibre/utils/poppler/poppler.cpp index db14bf9a55..b64536c85d 100644 --- a/src/calibre/utils/poppler/poppler.cpp +++ b/src/calibre/utils/poppler/poppler.cpp @@ -176,15 +176,16 @@ poppler_PDFDoc_render_page(poppler_PDFDoc *self, PyObject *args, PyObject *kwarg int num; if (!PyArg_ParseTuple(args, "i|ff", &num, &xdpi, &ydpi)) return ans; - if ( num < 0 || num >= self->doc->numPages()) { - PyErr_SetString(PyExc_ValueError, "Invalid page number"); - return ans; - } if ( self->doc->isLocked()) { PyErr_SetString(PyExc_ValueError, "This document is copyrighted."); return ans; } + if ( num < 0 || num >= self->doc->numPages()) { + PyErr_SetString(PyExc_ValueError, "Invalid page number"); + return ans; + } + page = self->doc->page(num); img = page->renderToImage(xdpi, ydpi); if (img.isNull()) { diff --git a/src/calibre/utils/windows/winutil.c b/src/calibre/utils/windows/winutil.c index 88091ffc52..be504b7fcf 100644 --- a/src/calibre/utils/windows/winutil.c +++ b/src/calibre/utils/windows/winutil.c @@ -929,6 +929,7 @@ initwinutil(void) { PyModule_AddIntConstant(m, "CSIDL_COOKIES", CSIDL_COOKIES); PyModule_AddIntConstant(m, "CSIDL_FLAG_CREATE", CSIDL_FLAG_CREATE); PyModule_AddIntConstant(m, "CSIDL_FLAG_DONT_VERIFY", CSIDL_FLAG_DONT_VERIFY); + PyModule_AddIntConstant(m, "CSIDL_FONTS", CSIDL_FONTS); PyModule_AddIntConstant(m, "CSIDL_HISTORY", CSIDL_HISTORY); PyModule_AddIntConstant(m, "CSIDL_INTERNET_CACHE", CSIDL_INTERNET_CACHE); PyModule_AddIntConstant(m, "CSIDL_LOCAL_APPDATA", CSIDL_LOCAL_APPDATA); diff --git a/src/calibre/web/feeds/news.py b/src/calibre/web/feeds/news.py index 05c62af725..849a5be1ab 100644 --- a/src/calibre/web/feeds/news.py +++ b/src/calibre/web/feeds/news.py @@ -679,7 +679,7 @@ class BasicNewsRecipe(Recipe): fetcher.browser_lock = fetcher.DUMMY_LOCK res, path, failures = fetcher.start_fetch(url), fetcher.downloaded_paths, fetcher.failed_links if not res or not os.path.exists(res): - raise Exception(_('Could not fetch article. Run with --debug to see the reason')) + raise Exception(_('Could not fetch article. Run with -vv to see the reason')) return res, path, failures def fetch_article(self, url, dir, f, a, num_of_feeds): @@ -741,6 +741,9 @@ class BasicNewsRecipe(Recipe): url = self.print_version(article.url) except NotImplementedError: url = article.url + except: + self.log.exception('Failed to find print version for: '+article.url) + url = None if not url: continue func, arg = (self.fetch_embedded_article, article) if self.use_embedded_content else \ diff --git a/src/calibre/web/feeds/recipes/__init__.py b/src/calibre/web/feeds/recipes/__init__.py index 061c5746f4..3a2b7a4e13 100644 --- a/src/calibre/web/feeds/recipes/__init__.py +++ b/src/calibre/web/feeds/recipes/__init__.py @@ -42,7 +42,7 @@ recipe_modules = ['recipe_' + r for r in ( 'moneynews', 'der_standard', 'diepresse', 'nzz_ger', 'hna', 'seattle_times', 'scott_hanselman', 'coding_horror', 'twitchfilms', 'stackoverflow', 'telepolis_artikel', 'zaobao', 'usnews', - 'straitstimes', 'index_hu', 'pcworld_hu', 'hrt', 'rts', + 'straitstimes', 'index_hu', 'pcworld_hu', 'hrt', 'rts', 'axxon_news', 'h1', 'h2', 'h3', 'phd_comics', 'woz_die', 'elektrolese', 'climate_progress', 'carta', 'slashdot', 'publico', 'the_budget_fashionista', 'elperiodico_catalan', @@ -55,7 +55,7 @@ recipe_modules = ['recipe_' + r for r in ( 'eltiempo_hn', 'slate', 'tnxm', 'bbcvietnamese', 'vnexpress', 'volksrant', 'theeconomictimes_india', 'ourdailybread', 'monitor', 'republika', 'beta', 'beta_en', 'glasjavnosti', - 'esquire', + 'esquire', 'livemint', 'thedgesingapore', 'darknet', )] diff --git a/src/calibre/web/feeds/recipes/recipe_axxon_news.py b/src/calibre/web/feeds/recipes/recipe_axxon_news.py new file mode 100644 index 0000000000..cb9f5fca51 --- /dev/null +++ b/src/calibre/web/feeds/recipes/recipe_axxon_news.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python + +__license__ = 'GPL v3' +__copyright__ = '2009, Darko Miletic ' +''' +axxon.com.ar +''' +from calibre.web.feeds.news import BasicNewsRecipe +from calibre.ebooks.BeautifulSoup import Tag + +class Axxon_news(BasicNewsRecipe): + title = 'Axxon noticias' + __author__ = 'Darko Miletic' + description = 'Axxon, Ciencia Ficcion en Bits' + publisher = 'Axxon' + category = 'news, SF, Argentina, science, movies' + oldest_article = 7 + max_articles_per_feed = 100 + no_stylesheets = False + use_embedded_content = False + language = _('Spanish') + lang = 'es-AR' + + conversion_options = { + 'comment' : description + , 'tags' : category + , 'publisher' : publisher + , 'language' : lang + , 'pretty_print' : True + } + + + keep_only_tags = [dict(name='div', attrs={'class':'post'})] + + remove_tags = [dict(name=['object','link','iframe','embed'])] + + feeds = [(u'Noticias', u'http://axxon.com.ar/noticias/feed/')] + + remove_attributes = ['style','width','height','font','border','align'] + + + def adeify_images2(cls, soup): + for item in soup.findAll('img'): + for attrib in ['height','width','border','align','style']: + if item.has_key(attrib): + del item[attrib] + oldParent = item.parent + if oldParent.name == 'a': + oldParent.name == 'p' + myIndex = oldParent.contents.index(item) + brtag = Tag(soup,'br') + oldParent.insert(myIndex+1,brtag) + return soup + + def preprocess_html(self, soup): + soup.html['xml:lang'] = self.lang + soup.html['lang'] = self.lang + mlang = Tag(soup,'meta',[("http-equiv","Content-Language"),("content",self.lang)]) + soup.html.insert(0,mlang) + return self.adeify_images2(soup) + diff --git a/src/calibre/web/feeds/recipes/recipe_darknet.py b/src/calibre/web/feeds/recipes/recipe_darknet.py new file mode 100644 index 0000000000..e86be1655a --- /dev/null +++ b/src/calibre/web/feeds/recipes/recipe_darknet.py @@ -0,0 +1,43 @@ +__license__ = 'GPL v3' +__copyright__ = '2008, Kovid Goyal ' + +''' +Fetch darknet. +''' + +from calibre.web.feeds.news import BasicNewsRecipe + + +class darknet(BasicNewsRecipe): + + title = 'darknet' + description = 'Ethical hacking and security news' + __author__ = 'Oliver Niesner' + language = _('English') + use_embedded_content = False + timefmt = ' [%b %d %Y]' + max_articles_per_feed = 40 + no_stylesheets = True + oldest_article = 180 + + remove_tags = [dict(id='navi_top'), + dict(id='navi_bottom'), + dict(id='logo'), + dict(id='login_suche'), + dict(id='navi_login'), + dict(id='breadcrumb'), + dict(id='subtitle'), + dict(id='bannerzone'), + dict(name='span', attrs={'class':'rsaquo'}), + dict(name='span', attrs={'class':'next'}), + dict(name='span', attrs={'class':'prev'}), + dict(name='div', attrs={'class':'news_logo'}), + dict(name='div', attrs={'class':'nextprev'}), + dict(name='p', attrs={'class':'news_option'}), + dict(name='p', attrs={'class':'news_foren'})] + remove_tags_after = [dict(name='div', attrs={'class':'entrybody'})] + + feeds = [ ('darknet', 'http://feedproxy.google.com/darknethackers') ] + + + diff --git a/src/calibre/web/feeds/recipes/recipe_elektrolese.py b/src/calibre/web/feeds/recipes/recipe_elektrolese.py index 622190a286..07eef95bbc 100644 --- a/src/calibre/web/feeds/recipes/recipe_elektrolese.py +++ b/src/calibre/web/feeds/recipes/recipe_elektrolese.py @@ -32,7 +32,6 @@ class elektrolese(BasicNewsRecipe): - feeds = [ (u'electrolese', u'http://elektrolese.blogspot.com/feeds/posts/default?alt=rss') ] - + feeds = [ (u'elektrolese', u'http://elektrolese.blogspot.com/feeds/posts/default?alt=rss') ] diff --git a/src/calibre/web/feeds/recipes/recipe_hna.py b/src/calibre/web/feeds/recipes/recipe_hna.py index c4faec94ba..928d3f032b 100644 --- a/src/calibre/web/feeds/recipes/recipe_hna.py +++ b/src/calibre/web/feeds/recipes/recipe_hna.py @@ -19,16 +19,24 @@ class hnaDe(BasicNewsRecipe): timefmt = ' [%d %b %Y]' max_articles_per_feed = 40 no_stylesheets = True + remove_javascript = True encoding = 'iso-8859-1' remove_tags = [dict(id='topnav'), dict(id='nav_main'), + dict(id='teaser'), dict(id='suchen'), + dict(id='superbanner'), + dict(id='navigation'), + dict(id='skyscraper'), dict(id=''), dict(name='span'), dict(name='ul', attrs={'class':'linklist'}), dict(name='a', attrs={'href':'#'}), + dict(name='div', attrs={'class':'hlist'}), + dict(name='div', attrs={'class':'subc noprint'}), dict(name='p', attrs={'class':'breadcrumb'}), + dict(name='a', attrs={'style':'cursor:hand'}), dict(name='p', attrs={'class':'h5'})] #remove_tags_after = [dict(name='div', attrs={'class':'rahmenbreaking'})] remove_tags_after = [dict(name='a', attrs={'href':'#'})] @@ -38,3 +46,4 @@ class hnaDe(BasicNewsRecipe): + diff --git a/src/calibre/web/feeds/recipes/recipe_linuxdevices.py b/src/calibre/web/feeds/recipes/recipe_linuxdevices.py index 5f2ef3529b..4c32bad80c 100644 --- a/src/calibre/web/feeds/recipes/recipe_linuxdevices.py +++ b/src/calibre/web/feeds/recipes/recipe_linuxdevices.py @@ -8,7 +8,7 @@ import re from calibre.web.feeds.news import BasicNewsRecipe -class Sueddeutsche(BasicNewsRecipe): +class LinuxDevices(BasicNewsRecipe): title = u'Linuxdevices' description = 'News about Linux driven Hardware' @@ -16,22 +16,22 @@ class Sueddeutsche(BasicNewsRecipe): use_embedded_content = False timefmt = ' [%a %d %b %Y]' max_articles_per_feed = 50 - language = _('English') no_stylesheets = True - html2epub_options = 'linearize_tables = True\nbase_font_size2=14' - html2lrf_options = ['--ignore-tables'] + language = _('English') + remove_javascript = True + conversion_options = { 'linearize_tables' : True} encoding = 'latin1' - remove_tags_after = [dict(id='nointelliTXT')] + remove_tags_after = [dict(id='intelliTxt')] filter_regexps = [r'ad\.doubleclick\.net'] remove_tags = [dict(name='div', attrs={'class':'bannerSuperBanner'}), dict(name='div', attrs={'class':'bannerSky'}), + dict(name='div', attrs={'border':'0'}), dict(name='div', attrs={'class':'footerLinks'}), dict(name='div', attrs={'class':'seitenanfang'}), dict(name='td', attrs={'class':'mar5'}), - dict(name='td', attrs={'class':'mar5'}), dict(name='table', attrs={'class':'pageAktiv'}), dict(name='table', attrs={'class':'xartable'}), dict(name='table', attrs={'class':'wpnavi'}), @@ -40,24 +40,26 @@ class Sueddeutsche(BasicNewsRecipe): dict(name='table', attrs={'class':'artikelBox'}), dict(name='table', attrs={'class':'kommentare'}), dict(name='table', attrs={'class':'pageBoxBot'}), + dict(name='table', attrs={'td':'height="3"'}), + dict(name='table', attrs={'class':'contentpaneopen'}), dict(name='td', attrs={'nowrap':'nowrap'}), - dict(name='td', attrs={'valign':'middle'}), dict(name='td', attrs={'align':'left'}), - dict(name='td', attrs={'align':'center'}), dict(name='td', attrs={'height':'5'}), + dict(name='td', attrs={'class':'ArticleWidgetsHeadline'}), dict(name='div', attrs={'class':'artikelBox navigatorBox'}), dict(name='div', attrs={'class':'similar-article-box'}), dict(name='div', attrs={'class':'videoBigHack'}), dict(name='td', attrs={'class':'artikelDruckenRight'}), dict(name='td', attrs={'class':'width="200"'}), + dict(name='span', attrs={'class':'content_rating'}), + dict(name='a', attrs={'href':'http://www.addthis.com/bookmark.php'}), dict(name='a', attrs={'href':'/news'}), - dict(name='a', attrs={'href':'/'}), - dict(name='a', attrs={'href':'/articles'}), dict(name='a', attrs={'href':'/cgi-bin/survey/survey.cgi'}), dict(name='a', attrs={'href':'/cgi-bin/board/UltraBoard.pl'}), dict(name='iframe'), dict(name='form'), dict(name='span', attrs={'class':'hidePrint'}), + dict(id='ArticleWidgets'), dict(id='headerLBox'), dict(id='nointelliTXT'), dict(id='rechteSpalte'), @@ -69,27 +71,18 @@ class Sueddeutsche(BasicNewsRecipe): dict(id='nnav-headerteaser'), dict(id='nnav-head'), dict(id='nnav-top'), - dict(id='nnav-logodiv'), - dict(id='nnav-logo'), - dict(id='nnav-oly'), dict(id='readcomment')] - feeds = [ (u'Linuxdevices', u'http://www.linuxdevices.com/backend/headlines.rss') ] + feeds = [ (u'Linuxdevices', u'http://www.linuxfordevices.com/rss.xml') ] def preprocess_html(self, soup): - for item in soup.findAll(re.compile('^a')): - item.extract() match = re.compile(r"^Related") for item in soup.findAll('b', text=match): item.extract() - for item in soup.findAll(re.compile('^li')): - item.extract() for item in soup.findAll(re.compile('^ul')): item.extract() - for item in soup.find(re.compile('^br')): - item.extract() for item in soup.findAll('br', limit=10): item.extract() return soup @@ -101,4 +94,3 @@ class Sueddeutsche(BasicNewsRecipe): return soup - diff --git a/src/calibre/web/feeds/recipes/recipe_livemint.py b/src/calibre/web/feeds/recipes/recipe_livemint.py new file mode 100644 index 0000000000..2461dde404 --- /dev/null +++ b/src/calibre/web/feeds/recipes/recipe_livemint.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python + +__license__ = 'GPL v3' +__copyright__ = '2009, Darko Miletic ' +''' +www.livemint.com +''' + +from calibre.web.feeds.news import BasicNewsRecipe + +class LiveMint(BasicNewsRecipe): + title = u'Livemint' + __author__ = 'Darko Miletic' + description = 'The Wall Street Journal' + publisher = 'The Wall Street Journal' + category = 'news, games, adventure, technology' + language = _('English') + oldest_article = 15 + max_articles_per_feed = 100 + no_stylesheets = True + encoding = 'utf-8' + use_embedded_content = False + extra_css = ' #dvArtheadline{font-size: x-large} #dvArtAbstract{font-size: large} ' + + keep_only_tags = [dict(name='div', attrs={'class':'innercontent'})] + + remove_tags = [dict(name=['object','link','embed','form','iframe'])] + + feeds = [(u'Articles', u'http://www.livemint.com/SectionRssfeed.aspx?Mid=1')] + + def print_version(self, url): + link = url + msoup = self.index_to_soup(link) + mlink = msoup.find(attrs={'id':'ctl00_bodyplaceholdercontent_cntlArtTool_printUrl'}) + if mlink: + link = 'http://www.livemint.com/Articles/' + mlink['href'].rpartition('/Articles/')[2] + return link + + def preprocess_html(self, soup): + return self.adeify_images(soup) diff --git a/src/calibre/web/feeds/recipes/recipe_outlook_india.py b/src/calibre/web/feeds/recipes/recipe_outlook_india.py index 8576b41f39..e931c89b4a 100644 --- a/src/calibre/web/feeds/recipes/recipe_outlook_india.py +++ b/src/calibre/web/feeds/recipes/recipe_outlook_india.py @@ -1,87 +1,132 @@ #!/usr/bin/env python __license__ = 'GPL v3' -__copyright__ = '2008, Kovid Goyal ' -''' -outlookindia.com -''' - -from calibre.web.feeds.news import BasicNewsRecipe +__copyright__ = '2009, Kovid Goyal ' import re +from calibre import strftime +from calibre.ebooks.BeautifulSoup import BeautifulSoup, Tag +from calibre.web.feeds.news import BasicNewsRecipe class OutlookIndia(BasicNewsRecipe): - - title = 'Outlook India' - __author__ = 'Kovid Goyal' - description = 'Weekly news magazine focused on India.' + + title = 'Outlook India' + __author__ = 'Kovid Goyal and Sujata Raman' + description = 'Weekly news and current affairs in India' + no_stylesheets = True + encoding = 'utf-8' language = _('English') recursions = 1 - match_regexp = r'full.asp.*&pn=\d+' - - remove_tags = [ - dict(name='img', src="images/space.gif"), - dict(name=lambda tag: tag.name == 'tr' and tag.find('img', src="image/tl.gif") is not None ), - dict(name=lambda tag: tag.name == 'table' and tag.find('font', attrs={'class':'fontemailfeed'}) is not None), - ] - - preprocess_regexps = [ - (re.compile(r'', re.DOTALL|re.IGNORECASE), - lambda match: ''), - - (re.compile(r'>More Stories:.*', re.DOTALL), - lambda match: '>'), - - (re.compile(r'.*', re.DOTALL), - lambda match: ''), - ] - - def parse_index(self): - soup = self.index_to_soup('http://www.outlookindia.com/archivecontents.asp') - feeds = [] - title = None - bogus = True - for table in soup.findAll('table'): - if title is None: - td = table.find('td', background="images/content_band1.jpg") - if td is not None: - title = self.tag_to_string(td, False) - title = title.replace(u'\xa0', u'').strip() - if 'Cover Story' in title and bogus: - bogus = False - title = None - else: - articles = [] - for a in table.findAll('a', href=True): - if a.find('img') is not None: - continue - atitle = self.tag_to_string(a, use_alt=False) - desc = a.findNextSibling('font', attrs={'class':'fontintro'}) - if desc is not None: - desc = self.tag_to_string(desc) - if not desc: - desc = '' - articles.append({ - 'title':atitle, - 'description': desc, - 'content': '', - 'url':'http://www.outlookindia.com/'+a['href'], - 'date': '', - }) - feeds.append((title, articles)) - title = None - - - return feeds + extra_css = ''' + body{font-family:Arial,Helvetica,sans-serif; font-size:xx-small;} + .fspheading{color:#AF0E25 ; font-family:"Times New Roman",Times,serif; font-weight:bold ; font-size:large; } + .fspauthor{color:#AF0E25; font-family:Arial,Helvetica,sans-serif; font-size:xx-small;} + .fspintro{color:#666666; font-family:Arial,Helvetica,sans-serif; font-size:xx-small;} + .fspchannelhome{font-family:Arial,Helvetica,sans-serif; font-size:x-small;} + .fspphotocredit{color:##999999; font-family:Arial,Helvetica,sans-serif; font-size:xx-small;} + ''' + keep_only_tags = [ + dict(name='div', attrs={'id':["ctl00_cphpagemiddle_reparticle_ctl00_divfullstorytext","ctl00_cphpagemiddle_reparticle_ctl00_divartpic","ctl00_cphpagemiddle_reparticle_ctl00_divfspheading", "ctl00_cphpagemiddle_reparticle_ctl00_divartpiccaption", "ctl00_cphpagemiddle_reparticle_ctl00_divartpiccredit","ctl00_cphpagemiddle_reparticle_ctl00_divfspintro", "ctl00_cphpagemiddle_reparticle_ctl00_divartbyline", "ctl00_cphpagemiddle_divglitteratiregulars","ctl00_cphpagemiddle_divcartoon","feedbackslatestfirst","ctl00_cphpagemiddle_divregulars","ctl00_cphpagemiddle_divquotes"]}), + ] + remove_tags = [dict(name=['script','object','hr']),] + + def get_browser(self): + br = BasicNewsRecipe.get_browser(self) + # This site sends article titles in the cookie which occasionally + # contain non ascii characters causing httplib to fail. Instead just + # disable cookies as they're not needed for download. Proper solution + # would be to implement a unicode aware cookie jar + br.set_cookiejar(None) + return br + + def parse_index(self): + + + soup = self.index_to_soup('http://www.outlookindia.com/issues.aspx') + # find cover pic + div = soup.find('div', attrs={'class':re.compile('cententcellpadding')}) + + if div is None: return None + a = div.find('a') + + if a is not None: + href = 'http://www.outlookindia.com/' + a['href'] + + soup = self.index_to_soup(href) + cover = soup.find('img', attrs={'id':"ctl00_cphpagemiddle_dlissues_ctl00_imgcoverpic"}, src=True) + if cover is not None: + + self.cover_url = cover['src'] + + # end find cover pic + #find current issue + div = soup.find('table', attrs={'id':re.compile('ctl00_cphpagemiddle_dlissues')}) + + if div is None: return None + a = div.find('a') + + if a is not None: + href = 'http://www.outlookindia.com/' + a['href'] + + soup = self.index_to_soup(href) + #find current issue + + #find the articles in the current issue + articles = [] + + for a in soup.findAll('a', attrs={'class':['contentpgsubheadinglink',"contentpgtext6",]}): + + if a and a.has_key('href'): + + url = 'http://www.outlookindia.com/' + a['href'] + else: + url ='' + + title = self.tag_to_string(a) + + desc = '' + date = '' + articles.append({ + 'title':title, + 'date':date, + 'url':url, + 'description':desc, + }) + for a in soup.findAll('a', attrs={'id':["ctl00_cphpageleft_hlglitterati","ctl00_cphpageleft_hlposcape",]}): + + if a and a.has_key('href'): + + url = 'http://www.outlookindia.com/' + a['href'] + else: + url ='' + + title = self.tag_to_string(a) + + desc = '' + date = '' + articles.append({ + 'title':title, + 'date':date, + 'url':url, + 'description':desc, + }) + + + return [('Current Issue', articles)] + + def preprocess_html(self, soup): + for item in soup.findAll(style=True): + del item['style'] + return self.adeify_images(soup) + + + + def postrocess_html(self, soup, first): + + for item in soup.findAll(align = "left"): + del item['align'] + + for tag in soup.findAll(name=['table', 'tr','td','tbody','ul','li','font','span']): + tag.name = 'div' + + return soup - def postprocess_html(self, soup, first_fetch): - bad = [] - for table in soup.findAll('table'): - if table.find(text=re.compile(r'\(\d+ of \d+\)')): - bad.append(table) - for b in bad: - b.extract() - soup = soup.findAll('html')[0] - for t in soup.findAll(['table', 'tr', 'td']): - t.name = 'div' - return soup - diff --git a/src/calibre/web/feeds/recipes/recipe_pagina12.py b/src/calibre/web/feeds/recipes/recipe_pagina12.py index 039e8a8e15..e0836a14c6 100644 --- a/src/calibre/web/feeds/recipes/recipe_pagina12.py +++ b/src/calibre/web/feeds/recipes/recipe_pagina12.py @@ -10,11 +10,11 @@ from calibre import strftime from calibre.web.feeds.news import BasicNewsRecipe class Pagina12(BasicNewsRecipe): - title = 'Pagina/12' + title = 'Pagina - 12' __author__ = 'Darko Miletic' description = 'Noticias de Argentina y el resto del mundo' publisher = 'La Pagina S.A.' - category = 'news, politics, Argentina' + category = 'news, politics, Argentina' oldest_article = 2 max_articles_per_feed = 100 no_stylesheets = True @@ -22,16 +22,16 @@ class Pagina12(BasicNewsRecipe): cover_url = strftime('http://www.pagina12.com.ar/fotos/%Y%m%d/diario/tapagn.jpg') remove_javascript = True use_embedded_content = False - language = _('Spanish') - - + language = _('Spanish') + + html2lrf_options = [ '--comment', description , '--category', category , '--publisher', publisher ] - - html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"' + + html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"' remove_tags = [ @@ -39,7 +39,7 @@ class Pagina12(BasicNewsRecipe): ,dict(name='div', attrs={'id':'logo' }) ] - + feeds = [(u'Pagina/12', u'http://www.pagina12.com.ar/diario/rss/principal.xml')] def print_version(self, url): @@ -47,7 +47,7 @@ class Pagina12(BasicNewsRecipe): def preprocess_html(self, soup): mtag = '' - soup.head.insert(0,mtag) + soup.head.insert(0,mtag) for item in soup.findAll(style=True): del item['style'] return soup diff --git a/src/calibre/web/feeds/recipes/recipe_slate.py b/src/calibre/web/feeds/recipes/recipe_slate.py index 93c37affd4..09c6c2fa5a 100644 --- a/src/calibre/web/feeds/recipes/recipe_slate.py +++ b/src/calibre/web/feeds/recipes/recipe_slate.py @@ -16,7 +16,7 @@ class PeriodicalNameHere(BasicNewsRecipe): title = 'Slate' description = 'A general-interest publication offering analysis and commentary about politics, news and culture.' __author__ = 'GRiker' - max_articles_per_feed = 40 + max_articles_per_feed = 20 oldest_article = 7.0 recursions = 0 delay = 0 @@ -106,11 +106,15 @@ class PeriodicalNameHere(BasicNewsRecipe): older_section_dates = soup.findAll(True, attrs={'class':'maindateline'}) for older_section in older_section_dates : self.section_dates.append(self.tag_to_string(older_section,use_alt=False)) - - headline_stories = soup_top_stories.find('ul') + + if soup_top_stories: + headline_stories = soup_top_stories.find('ul') + else: + headline_stories = None section_lists = soup.findAll('ul') # Prepend the headlines to the first section - section_lists[0].insert(0,headline_stories) + if headline_stories: + section_lists[0].insert(0,headline_stories) sections = [] for section in section_lists : @@ -290,7 +294,8 @@ class PeriodicalNameHere(BasicNewsRecipe): excluded = re.compile('|'.join(self.excludedContentKeywords)) found_excluded = excluded.search(str(soup)) if found_excluded : - return None + print "no allowed content found, removing article" + raise StringError # Articles from www.thebigmoney.com use different tagging for byline, dateline and body head = soup.find('head') @@ -423,4 +428,3 @@ class PeriodicalNameHere(BasicNewsRecipe): if article.description is None : article.description = extract_description(article.href) - \ No newline at end of file diff --git a/src/calibre/web/feeds/recipes/recipe_thedgesingapore.py b/src/calibre/web/feeds/recipes/recipe_thedgesingapore.py new file mode 100644 index 0000000000..248255bee5 --- /dev/null +++ b/src/calibre/web/feeds/recipes/recipe_thedgesingapore.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python + +__license__ = 'GPL v3' +__copyright__ = '2009, Darko Miletic ' +''' +www.livemint.com +''' + +from calibre.web.feeds.news import BasicNewsRecipe + +class Edgesingapore(BasicNewsRecipe): + title = 'The Edge Singapore' + __author__ = 'Darko Miletic' + description = 'Financial news from Singapore' + publisher = 'The Edge Singapore' + category = 'news, finances, singapore' + language = _('English') + lang = 'en' + oldest_article = 15 + max_articles_per_feed = 100 + no_stylesheets = True + encoding = 'utf-8' + use_embedded_content = False + extra_css = ' .contentheading{font-size: x-large} .small{font-size: small} .createdate{font-size: small; font-weight: bold} ' + + + conversion_options = { + 'comment' : description + , 'tags' : category + , 'publisher' : publisher + , 'author' : publisher + , 'language' : lang + , 'pretty_print' : True + , 'linearize_tables' : True + } + + + remove_tags = [ + dict(name=['object','link','embed','form','iframe']) + ,dict(name='div',attrs={'id':'toolbar-article'}) + ,dict(name='div',attrs={'class':'backtotop'}) + ,dict(name='img',attrs={'alt':'Print'}) + ] + + remove_tags_after = dict(name='div',attrs={'class':'backtotop'}) + + feeds = [(u'Articles', u'http://feeds.feedburner.com/edgesg')] + + def print_version(self, url): + return url + '?tmpl=component&print=1' + + def preprocess_html(self, soup): + attribs = [ 'style','font','valign' + ,'colspan','width','height' + ,'rowspan','summary','align' + ,'cellspacing','cellpadding' + ,'frames','rules','border' + ] + for item in soup.body.findAll(name=['table','td','tr','th','caption','thead','tfoot','tbody','colgroup','col']): + item.name = 'div' + for attrib in attribs: + if item.has_key(attrib): + del item[attrib] + return self.adeify_images(soup) diff --git a/upload.py b/upload.py index 8dac5af79c..ffd702ca5d 100644 --- a/upload.py +++ b/upload.py @@ -4,7 +4,7 @@ __copyright__ = '2009, Kovid Goyal ' __docformat__ = 'restructuredtext en' import shutil, os, glob, re, cStringIO, sys, tempfile, time, textwrap, socket, \ - struct, subprocess + struct, subprocess, platform from datetime import datetime from setuptools.command.build_py import build_py as _build_py, convert_path from distutils.core import Command @@ -24,6 +24,7 @@ HTML2LRF = "src/calibre/ebooks/lrf/html/demo" TXT2LRF = "src/calibre/ebooks/lrf/txt/demo" MOBILEREAD = 'ftp://dev.mobileread.com/calibre/' +is64bit = platform.architecture()[0] == '64bit' def get_ip_address(ifname): import fcntl @@ -35,7 +36,7 @@ def get_ip_address(ifname): )[20:24]) try: - HOST=get_ip_address('eth0') + HOST=get_ip_address('br0') except: try: HOST=get_ip_address('wlan0') @@ -481,12 +482,12 @@ class upload_demo(OptionlessCommand): def installer_name(ext): if ext in ('exe', 'dmg'): return 'dist/%s-%s.%s'%(__appname__, __version__, ext) - return 'dist/%s-%s-i686.%s'%(__appname__, __version__, ext) + ans = 'dist/%s-%s-i686.%s'%(__appname__, __version__, ext) + if is64bit: + ans = ans.replace('i686', 'x86_64') + return ans - -class build_linux(OptionlessCommand): - description = 'Build linux installer' - def run(self): +def _build_linux(): installer = installer_name('tar.bz2') locals = {} exec open('installer/linux/freeze.py') in locals @@ -495,9 +496,15 @@ class build_linux(OptionlessCommand): raise Exception('Failed to build installer '+installer) return os.path.basename(installer) +class build_linux64(OptionlessCommand): + description = 'Build linux 64bit installer' + + def run(self): + return _build_linux() + class VMInstaller(OptionlessCommand): - user_options = [('dont-shutdown', 'd', 'Dont shutdown Vm after build')] + user_options = [('dont-shutdown', 'd', 'Dont shutdown VM after build')] boolean_options = ['dont-shutdown'] def initialize_options(self): @@ -521,10 +528,34 @@ class VMInstaller(OptionlessCommand): def get_build_script(self, subs): return self.BUILD_SCRIPT%subs - def start_vm(self, ssh_host, build_script, sleep=75): - build_script = self.get_build_script(build_script) + def vmware_started(self): + return 'started' in subprocess.Popen('/etc/init.d/vmware status', shell=True, stdout=subprocess.PIPE).stdout.read() + + def start_vmware(self): + if not self.vmware_started(): + if os.path.exists('/dev/kvm'): + check_call('sudo rmmod -w kvm-intel kvm', shell=True) + subprocess.Popen('sudo /etc/init.d/vmware start', shell=True) + + def stop_vmware(self): + while True: + try: + check_call('sudo /etc/init.d/vmware stop', shell=True) + break + except: + pass + while 'vmblock' in open('/proc/modules').read(): + check_call('sudo rmmod -f vmblock') + check_call('sudo modprobe kvm-intel', shell=True) + + + def run_vm(self): vmware = ('vmware', '-q', '-x', '-n', self.VM) - Popen(vmware) + self.__p = Popen(vmware) + + def start_vm(self, ssh_host, build_script, sleep=75): + self.run_vm() + build_script = self.get_build_script(build_script) t = tempfile.NamedTemporaryFile(suffix='.sh') t.write(build_script) t.flush() @@ -537,26 +568,55 @@ class VMInstaller(OptionlessCommand): check_call(('scp', t.name, ssh_host+':build-calibre')) check_call('ssh -t %s bash build-calibre'%ssh_host, shell=True) -class build_windows(VMInstaller): +class KVMInstaller(VMInstaller): + + def run_vm(self): + self.stop_vmware() + self.__p = Popen(self.VM) + + + +class build_linux32(KVMInstaller): + + description = 'Build linux 32bit installer' + VM = '/vmware/bin/linux_build' + + def run_vm(self): + self.__p = Popen('/vmware/bin/linux_build') + + def run(self): + if is64bit: + installer = installer_name('tar.bz2').replace('x86_64', 'i686') + self.start_vm('linux_build', ('python setup.py build_ext', + 'python', 'setup.py build_linux32')) + check_call(('scp', 'linux_build:build/calibre/dist/*.tar.bz2', 'dist')) + if not os.path.exists(installer): + raise Exception('Failed to build installer '+installer) + if not self.dont_shutdown: + Popen(('ssh', 'linux_build', 'sudo', '/sbin/poweroff')) + return os.path.basename(installer) + else: + return _build_linux() + + +class build_windows(KVMInstaller): description = 'Build windows installer' - VM = '/mnt/backup/calibre_windows_xp_home/calibre_windows_xp_home.vmx' - if not os.path.exists(VM): - VM = '/home/kovid/calibre_windows_xp_home/calibre_windows_xp_home.vmx' + VM = '/vmware/bin/win_build' def run(self): installer = installer_name('exe') - self.start_vm('windows', ('python setup.py develop', + self.start_vm('win_build', ('python setup.py develop', 'python', r'installer\\windows\\freeze.py')) if os.path.exists('build/py2exe'): shutil.rmtree('build/py2exe') - check_call(('scp', '-rp', 'windows:build/%s/build/py2exe'%__appname__, + check_call(('scp', '-rp', 'win_build:build/%s/build/py2exe'%__appname__, 'build')) if not os.path.exists('build/py2exe'): raise Exception('Failed to run py2exe') - if not self.dont_shutdown: - Popen(('ssh', 'windows', 'shutdown', '-s', '-t', '0')) self.run_windows_install_jammer(installer) + if not self.dont_shutdown: + Popen(('ssh', 'win_build', 'shutdown', '-s', '-t', '0')) return os.path.basename(installer) @classmethod @@ -573,9 +633,7 @@ class build_windows(VMInstaller): class build_osx(VMInstaller): description = 'Build OS X app bundle' - VM = '/mnt/backup/calibre_os_x/Mac OSX.vmx' - if not os.path.exists(VM): - VM = '/home/kovid/calibre_os_x/Mac OSX.vmx' + VM = '/vmware/calibre_os_x/Mac OSX.vmx' def get_build_script(self, subs): return (self.BUILD_SCRIPT%subs).replace('rm ', 'sudo rm ') @@ -583,17 +641,18 @@ class build_osx(VMInstaller): def run(self): installer = installer_name('dmg') python = '/Library/Frameworks/Python.framework/Versions/Current/bin/python' - self.start_vm('osx', ('sudo %s setup.py develop'%python, python, + self.start_vmware() + self.start_vm('osx_build', ('sudo %s setup.py develop'%python, python, 'installer/osx/freeze.py')) - check_call(('scp', 'osx:build/calibre/dist/*.dmg', 'dist')) + check_call(('scp', 'osx_build:build/calibre/dist/*.dmg', 'dist')) if not os.path.exists(installer): raise Exception('Failed to build installer '+installer) if not self.dont_shutdown: - Popen(('ssh', 'osx', 'sudo', '/sbin/shutdown', '-h', 'now')) + Popen(('ssh', 'osx_build', 'sudo', '/sbin/shutdown', '-h', 'now')) + time.sleep(20) + self.stop_vmware() return os.path.basename(installer) - - class upload_installers(OptionlessCommand): description = 'Upload any installers present in dist/' def curl_list_dir(self, url=MOBILEREAD, listonly=1): @@ -661,8 +720,9 @@ class upload_installers(OptionlessCommand): def run(self): print 'Uploading installers...' - for i in ('dmg', 'exe', 'tar.bz2'): - self.upload_installer(installer_name(i)) + installers = list(map(installer_name, ('dmg', 'exe', 'tar.bz2'))) + installers.append(installers[-1].replace('x86_64', 'i686')) + map(self.upload_installer, installers) check_call('''ssh divok echo %s \\> %s/latest_version'''\ %(__version__, DOWNLOADS), shell=True) @@ -715,6 +775,10 @@ class stage3(OptionlessCommand): OptionlessCommand.run(self) self.misc() +class build_linux(OptionlessCommand): + description = 'Build linux installers' + sub_commands = [ ('build_linux64', None), ('build_linux32', None) ] + class stage2(OptionlessCommand): description = 'Stage 2 of the build process' sub_commands = [