diff --git a/installer/windows/build_installer.py b/installer/windows/build_installer.py index 7119d92383..e19bff3e7b 100644 --- a/installer/windows/build_installer.py +++ b/installer/windows/build_installer.py @@ -8,16 +8,18 @@ __docformat__ = 'restructuredtext en' import sys, time, subprocess, os, re from calibre import __appname__, __version__ +INSTALLJAMMER = '/home/kovid/installjammer/installjammer' + sv = re.sub(r'[a-z]\d+', '', __version__) cmdline = [ - '/usr/local/installjammer/installjammer', + INSTALLJAMMER, '--build-dir', '/tmp/calibre-installjammer', '-DAppName', __appname__, '-DShortAppName', __appname__, '-DApplicationURL', 'http://%s.kovidgoyal.net'%__appname__, '-DCopyright', time.strftime('%Y Kovid Goyal'), - '-DPackageDescription', '%s is an e-book library manager. It can view, convert and catalog e-books in most of the major e-book formats. It can also talk to a few e-book reader devices. It can go out to the internet and fetch metadata for your books. It can download newspapers and convert them into e-books for convenient reading.'%__appname__, + '-DPackageDescription', '%s is an e-book library manager. It can view, convert and catalog e-books in most of the major e-book formats. It can also talk to e-book reader devices. It can go out to the internet and fetch metadata for your books. It can download newspapers and convert them into e-books for convenient reading.'%__appname__, '-DPackageSummary', '%s: E-book library management'%__appname__, '-DVersion', __version__, '-DInstallVersion', sv + '.0', diff --git a/installer/windows/calibre/calibre.mpi b/installer/windows/calibre/calibre.mpi index 50dc81dcbd..6916256c81 100644 --- a/installer/windows/calibre/calibre.mpi +++ b/installer/windows/calibre/calibre.mpi @@ -138,7 +138,7 @@ ProjectID DA98A0C6-9102-73EC-2516-B147E972D3F7 ProjectVersion -1.2.7.0 +1.2.12.0 SaveOnlyToplevelDirs No @@ -211,7 +211,8 @@ File ::8E5D85A4-7608-47A1-CF7C-309060D5FF40 -filemethod {Always overwrite files} 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} -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} -name Typical -parent SetupTypes -InstallComponent 3EA07B17-04D8-6508-B535-96CC7173B49A -setup Install -type pane -title {Welcome Screen} -component Welcome -active Yes -parent StandardInstall +InstallComponent 3EA07B17-04D8-6508-B535-96CC7173B49A -setup Install -type pane -conditions D7F585DB-0DEC-A94E-DAB0-94D558D82764 -title {Welcome Screen} -component Welcome -command reorder -active Yes -parent StandardInstall +Condition D7F585DB-0DEC-A94E-DAB0-94D558D82764 -active Yes -parent 3EA07B17-04D8-6508-B535-96CC7173B49A -title {Execute Script Condition} -component ExecuteScriptCondition -TreeObject::id D7F585DB-0DEC-A94E-DAB0-94D558D82764 InstallComponent 7CCDA4BB-861C-C21E-3011-E93DB58F07D6 -setup Install -type action -title {Check for Previous Install} -component CheckForPreviousInstall -active Yes -parent 3EA07B17-04D8-6508-B535-96CC7173B49A InstallComponent 580ACF2C-517F-5E48-9DEF-7DAEFBA59FDD -setup Install -type action -conditions 6DE3B369-9D6B-6BC1-4EA0-2C54ECE159EB -title {Set Virtual Text} -component SetVirtualText -command insert -active Yes -parent 3EA07B17-04D8-6508-B535-96CC7173B49A Condition 6DE3B369-9D6B-6BC1-4EA0-2C54ECE159EB -active Yes -parent 580ACF2C-517F-5E48-9DEF-7DAEFBA59FDD -title {String Is Condition} -component StringIsCondition -TreeObject::id 6DE3B369-9D6B-6BC1-4EA0-2C54ECE159EB @@ -513,7 +514,7 @@ false 1 3EA07B17-04D8-6508-B535-96CC7173B49A,Conditions -{0 conditions} +{1 condition} 3EA07B17-04D8-6508-B535-96CC7173B49A,Message,subst 1 @@ -1100,6 +1101,9 @@ AAFE58A0-2DFB-CA20-1F6E-D3815F885996,Alias AIX-ppc,Active No +AIX-ppc,BuildSeparateArchives +No + AIX-ppc,DefaultDirectoryPermission 0755 @@ -1286,6 +1290,26 @@ CFBE4459-450B-1FAB-3422-609544334AA2,String D79DC0D2-38BC-9D9F-2DF4-3C76D89BF933,ExitType Finish +D7F585DB-0DEC-A94E-DAB0-94D558D82764,CheckCondition +{Before Next Pane is Displayed} + +D7F585DB-0DEC-A94E-DAB0-94D558D82764,Comment +{Check if calibre.exe is still running} + +D7F585DB-0DEC-A94E-DAB0-94D558D82764,FailureMessage +{calibre is still running. Please shut it down before proceeding. You can quit calibre by right clicking on the calibre system tray icon.} + +D7F585DB-0DEC-A94E-DAB0-94D558D82764,ResultVirtualText +CalibreRunning + +D7F585DB-0DEC-A94E-DAB0-94D558D82764,Script +{set pid [::InstallAPI::FindProcesses -name calibre.exe] +if {$pid eq ""} { + ## myapp.exe is not running + return 1 +} +return 0} + D86BBA5C-4903-33BA-59F8-4266A3D45896,Conditions {2 conditions} @@ -1475,6 +1499,9 @@ FBA33088-C809-DD6B-D337-EADBF1CEE966,String FreeBSD-4-x86,Active No +FreeBSD-4-x86,BuildSeparateArchives +No + FreeBSD-4-x86,DefaultDirectoryPermission 0755 @@ -1526,6 +1553,9 @@ FreeBSD-4-x86,RootInstallDir FreeBSD-x86,Active No +FreeBSD-x86,BuildSeparateArchives +No + FreeBSD-x86,DefaultDirectoryPermission 0755 @@ -1577,6 +1607,9 @@ FreeBSD-x86,RootInstallDir HPUX-hppa,Active No +HPUX-hppa,BuildSeparateArchives +No + HPUX-hppa,DefaultDirectoryPermission 0755 @@ -1628,6 +1661,9 @@ HPUX-hppa,RootInstallDir Linux-x86,Active No +Linux-x86,BuildSeparateArchives +No + Linux-x86,DefaultDirectoryPermission 0755 @@ -1679,6 +1715,9 @@ Linux-x86,RootInstallDir Solaris-sparc,Active No +Solaris-sparc,BuildSeparateArchives +No + Solaris-sparc,DefaultDirectoryPermission 0755 @@ -1730,6 +1769,9 @@ Solaris-sparc,RootInstallDir TarArchive,Active No +TarArchive,BuildSeparateArchives +No + TarArchive,CompressionLevel 6 @@ -1790,9 +1832,15 @@ TarArchive,VirtualTextMap Windows,Active Yes +Windows,BuildSeparateArchives +No + Windows,Executable <%AppName%>-<%Version%><%Ext%> +Windows,FileDescription +{<%AppName%> <%Version%> Setup} + Windows,IncludeTWAPI Yes @@ -1829,6 +1877,9 @@ Windows,WindowsIcon ZipArchive,Active No +ZipArchive,BuildSeparateArchives +No + ZipArchive,CompressionLevel 6 diff --git a/src/calibre/ebooks/epub/from_any.py b/src/calibre/ebooks/epub/from_any.py index 51fdcc4e6a..e81821ed53 100644 --- a/src/calibre/ebooks/epub/from_any.py +++ b/src/calibre/ebooks/epub/from_any.py @@ -16,7 +16,7 @@ from calibre.ebooks.epub import config as common_config, process_encryption from calibre.ebooks.epub.from_html import convert as html2epub, find_html_index from calibre.ptempfile import TemporaryDirectory from calibre.ebooks.metadata import MetaInformation -from calibre.ebooks.metadata.opf2 import OPFCreator +from calibre.ebooks.metadata.opf2 import OPFCreator, OPF from calibre.utils.zipfile import ZipFile from calibre.customize.ui import run_plugins_on_preprocess @@ -25,9 +25,36 @@ def lit2opf(path, tdir, opts): print 'Exploding LIT file:', path reader = LitReader(path) reader.extract_content(tdir, False) - for f in walk(tdir): - if f.lower().endswith('.opf'): - return f + opf = None + for opf in walk(tdir): + if opf.lower().endswith('.opf'): + break + if not opf.endswith('.opf'): + opf = None + if opf is not None: # Check for url-quoted filenames + _opf = OPF(opf, os.path.dirname(opf)) + replacements = [] + for item in _opf.itermanifest(): + href = item.get('href', '') + path = os.path.join(os.path.dirname(opf), *(href.split('/'))) + if not os.path.exists(path) and os.path.exists(path.replace('&', '%26')): + npath = path + path = path.replace('&', '%26') + replacements.append((path, npath)) + if replacements: + print 'Fixing quoted filenames...' + for path, npath in replacements: + if os.path.exists(path): + os.rename(path, npath) + for f in walk(tdir): + with open(f, 'r+b') as f: + raw = f.read() + for path, npath in replacements: + raw = raw.replace(os.path.basename(path), os.path.basename(npath)) + f.seek(0) + f.truncate() + f.write(raw) + return opf def mobi2opf(path, tdir, opts): from calibre.ebooks.mobi.reader import MobiReader diff --git a/src/calibre/ebooks/metadata/lit.py b/src/calibre/ebooks/metadata/lit.py index 825fe45cf4..c38450c64c 100644 --- a/src/calibre/ebooks/metadata/lit.py +++ b/src/calibre/ebooks/metadata/lit.py @@ -6,33 +6,28 @@ Support for reading the metadata from a LIT file. import sys, cStringIO, os -from calibre import relpath from calibre.ebooks.metadata import MetaInformation -from calibre.ebooks.metadata.opf import OPFReader +from calibre.ebooks.metadata.opf2 import OPF from calibre.ebooks.lit.reader import LitReader def get_metadata(stream): - try: - litfile = LitReader(stream) - src = litfile.meta.encode('utf-8') - mi = OPFReader(cStringIO.StringIO(src), dir=os.getcwd()) - cover_url, cover_item = mi.cover, None - if cover_url: - cover_url = relpath(cover_url, os.getcwd()) - for item in litfile.manifest.values(): - if item.path == cover_url: - cover_item = item.internal - if cover_item is not None: - ext = cover_url.rpartition('.')[-1] - if not ext: - ext = 'jpg' - else: - ext = ext.lower() - cd = litfile.get_file('/data/' + cover_item) - mi.cover_data = (ext, cd) if cd else (None, None) - except: - title = stream.name if hasattr(stream, 'name') and stream.name else 'Unknown' - mi = MetaInformation(title, ['Unknown']) + litfile = LitReader(stream) + src = litfile.meta.encode('utf-8') + opf = OPF(cStringIO.StringIO(src), os.getcwd()) + mi = MetaInformation(opf) + covers = [] + for item in opf.iterguide(): + if 'cover' not in item.get('type', '').lower(): + continue + href = item.get('href', '') + candidates = [href, href.replace('&', '%26')] + for item in litfile.manifest.values(): + if item.path in candidates: + covers.append(item.internal) + break + covers = [litfile.get_file('/data/' + i) for i in covers] + covers.sort(cmp=lambda x, y:cmp(len(x), len(y))) + mi.cover_data = ('jpg', covers[-1]) return mi def main(args=sys.argv): diff --git a/src/calibre/ebooks/metadata/mobi.py b/src/calibre/ebooks/metadata/mobi.py new file mode 100644 index 0000000000..933cbbdaed --- /dev/null +++ b/src/calibre/ebooks/metadata/mobi.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python +__license__ = 'GPL v3' +__copyright__ = '2009, Kovid Goyal kovid@kovidgoyal.net' +__docformat__ = 'restructuredtext en' + +''' +''' + +import sys, os + +from calibre.ebooks.mobi.reader import get_metadata + +def main(args=sys.argv): + if len(args) != 2: + print >>sys.stderr, 'Usage: %s file.mobi' % args[0] + return 1 + fname = args[1] + mi = get_metadata(open(fname, 'rb')) + print unicode(mi) + if mi.cover_data[1]: + cover = os.path.abspath( + '.'.join((os.path.splitext(os.path.basename(fname))[0], + mi.cover_data[0].lower()))) + open(cover, 'wb').write(mi.cover_data[1]) + print _('Cover saved to'), cover + return 0 + +if __name__ == '__main__': + sys.exit(main()) \ No newline at end of file diff --git a/src/calibre/ebooks/metadata/opf2.py b/src/calibre/ebooks/metadata/opf2.py index 08ed9aacd1..ed0340e4a8 100644 --- a/src/calibre/ebooks/metadata/opf2.py +++ b/src/calibre/ebooks/metadata/opf2.py @@ -435,7 +435,7 @@ class OPF(object): rating = MetadataField('rating', is_dc=False, formatter=int) - def __init__(self, stream, basedir=os.getcwdu()): + def __init__(self, stream, basedir=os.getcwdu(), unquote_urls=True): if not hasattr(stream, 'read'): stream = open(stream, 'rb') self.basedir = self.base_dir = basedir @@ -446,7 +446,8 @@ class OPF(object): if not self.metadata: raise ValueError('Malformed OPF file: No element') self.metadata = self.metadata[0] - self.unquote_urls() + if unquote_urls: + self.unquote_urls() self.manifest = Manifest() m = self.manifest_path(self.root) if m: diff --git a/src/calibre/ebooks/mobi/reader.py b/src/calibre/ebooks/mobi/reader.py index 5d2edd3fe0..51e184a420 100644 --- a/src/calibre/ebooks/mobi/reader.py +++ b/src/calibre/ebooks/mobi/reader.py @@ -311,7 +311,7 @@ class MobiReader(object): opf.cover = 'images/%05d.jpg'%(self.book_header.exth.cover_offset+1) manifest = [(htmlfile, 'text/x-oeb1-document')] bp = os.path.dirname(htmlfile) - for i in self.image_names: + for i in getattr(self, 'image_names', []): manifest.append((os.path.join(bp, 'images/', i), 'image/jpg')) opf.create_manifest(manifest) @@ -451,7 +451,7 @@ class MobiReader(object): image_index += 1 try: im = PILImage.open(buf) - except IOError, e: + except IOError: continue path = os.path.join(output_dir, '%05d.jpg'%image_index) @@ -476,31 +476,23 @@ def get_metadata(stream): if mr.book_header.exth is None: mi = MetaInformation(mr.name, [_('Unknown')]) else: - tdir = tempfile.mkdtemp('_mobi_meta', __appname__+'_') - atexit.register(shutil.rmtree, tdir) - #print tdir - mr.extract_images([], tdir) mi = mr.create_opf('dummy.html') - if mi.cover: - cover = os.path.join(tdir, mi.cover) - if not os.access(cover, os.R_OK): - fname = os.path.basename(cover) - match = re.match(r'(\d+)(.+)', fname) - if match: - num, ext = int(match.group(1), 10), match.group(2) - while num > 0: - num -= 1 - candidate = os.path.join(os.path.dirname(cover), '%05d%s'%(num, ext)) - if os.access(candidate, os.R_OK): - cover = candidate - break - if os.access(cover, os.R_OK): - mi.cover_data = ('JPEG', open(os.path.join(tdir, cover), 'rb').read()) - else: - path = os.path.join(tdir, 'images', '00001.jpg') - if os.access(path, os.R_OK): - mi.cover_data = ('JPEG', open(path, 'rb').read()) - return mi + try: + if hasattr(mr.book_header.exth, 'cover_offset'): + cover_index = mr.book_header.first_image_index + mr.book_header.exth.cover_offset + data = mr.sections[cover_index][0] + else: + data = mr.sections[mr.book_header.first_image_index][0] + buf = cStringIO.StringIO(data) + im = PILImage.open(buf) + obuf = cStringIO.StringIO() + im.convert('RGBA').save(obuf, format='JPEG') + mi.cover_data = ('jpg', obuf.getvalue()) + except: + import traceback + traceback.print_exc() + return mi + def option_parser(): from calibre.utils.config import OptionParser diff --git a/src/calibre/gui2/images/news/amspec.png b/src/calibre/gui2/images/news/amspec.png new file mode 100644 index 0000000000..9c0d13f419 Binary files /dev/null and b/src/calibre/gui2/images/news/amspec.png differ diff --git a/src/calibre/gui2/library.py b/src/calibre/gui2/library.py index 8a737fd608..0e426aaf42 100644 --- a/src/calibre/gui2/library.py +++ b/src/calibre/gui2/library.py @@ -162,7 +162,8 @@ class BooksModel(QAbstractTableModel): def refresh_ids(self, ids, current_row=-1): rows = self.db.refresh_ids(ids) - self.refresh_rows(rows, current_row=current_row) + if rows: + self.refresh_rows(rows, current_row=current_row) def refresh_rows(self, rows, current_row=-1): for row in rows: @@ -261,7 +262,17 @@ class BooksModel(QAbstractTableModel): self.reset() self.sorted_on = (self.column_map[col], order) - + + def refresh(self, reset=True): + try: + col = self.column_map.index(self.sorted_on[0]) + except: + col = 0 + self.db.refresh(field=self.column_map[col], + ascending=self.sorted_on[1]==Qt.AscendingOrder) + if reset: + self.reset() + def resort(self, reset=True): try: col = self.column_map.index(self.sorted_on[0]) diff --git a/src/calibre/gui2/main.py b/src/calibre/gui2/main.py index a7c4c47add..ba59908c9f 100644 --- a/src/calibre/gui2/main.py +++ b/src/calibre/gui2/main.py @@ -478,7 +478,7 @@ class Main(MainWindow, Ui_MainWindow): self.raise_() self.activateWindow() elif msg.startswith('refreshdb:'): - self.library_view.model().resort() + self.library_view.model().refresh() self.library_view.model().research() else: print msg diff --git a/src/calibre/library/__init__.py b/src/calibre/library/__init__.py index 77d02e1354..6cec55c471 100644 --- a/src/calibre/library/__init__.py +++ b/src/calibre/library/__init__.py @@ -29,5 +29,5 @@ def server_config(defaults=None): c.add_opt('develop', ['--develop'], default=False, help='Development mode. Server automatically restarts on file changes and serves code files (html, css, js) from the file system instead of calibre\'s resource system.') c.add_opt('max_cover', ['--max-cover'], default='600x800', - help=_('The maximum size for displayed covers')) + help=_('The maximum size for displayed covers. Default is %default.')) return c diff --git a/src/calibre/library/static/gui.js b/src/calibre/library/static/gui.js index 17f6f7f8fd..edefdbc5ef 100644 --- a/src/calibre/library/static/gui.js +++ b/src/calibre/library/static/gui.js @@ -40,7 +40,7 @@ function create_table_headers() { function format_url(format, id, title) { - return 'get/'+format.toLowerCase() + '/'+title + '_' + id+'.'+format.toLowerCase(); + return 'get/'+format.toLowerCase() + '/'+encodeURIComponent(title) + '_' + id+'.'+format.toLowerCase(); } function render_book(book) { diff --git a/src/calibre/linux.py b/src/calibre/linux.py index acd7e0b1bd..a05a7ea7a8 100644 --- a/src/calibre/linux.py +++ b/src/calibre/linux.py @@ -26,6 +26,7 @@ entry_points = { 'opf-meta = calibre.ebooks.metadata.opf2:main', 'odt-meta = calibre.ebooks.metadata.odt:main', 'epub-meta = calibre.ebooks.metadata.epub:main', + 'mobi-meta = calibre.ebooks.metadata.mobi:main', 'txt2lrf = calibre.ebooks.lrf.txt.convert_from:main', 'html2lrf = calibre.ebooks.lrf.html.convert_from:main', 'html2oeb = calibre.ebooks.html:main', @@ -423,7 +424,7 @@ def install_man_pages(fatal_errors): if prog in ('prs500', 'pdf-meta', 'epub-meta', 'lit-meta', 'markdown-calibre', 'calibre-debug', 'fb2-meta', 'calibre-fontconfig', 'calibre-parallel', 'odt-meta', - 'rb-meta', 'imp-meta'): + 'rb-meta', 'imp-meta', 'mobi-meta'): continue help2man = ('help2man', prog, '--name', 'part of %s'%__appname__, diff --git a/src/calibre/translations/dynamic.py b/src/calibre/translations/dynamic.py index 1c9f53e960..6131a84c8f 100644 --- a/src/calibre/translations/dynamic.py +++ b/src/calibre/translations/dynamic.py @@ -5,9 +5,8 @@ Dynamic language lookup of translations for user-visible strings. __license__ = 'GPL v3' __copyright__ = '2008, Marshall T. Vandegrift ' -import sys from cStringIO import StringIO -from gettext import GNUTranslations, NullTranslations +from gettext import GNUTranslations from calibre.translations.compiled import translations __all__ = ['translate'] @@ -23,5 +22,5 @@ def translate(lang, text): trans = GNUTranslations(buf) _CACHE[lang] = trans if trans is None: - return _(text) + return getattr(__builtins__, '_', lambda x: x)(text) return trans.ugettext(text) diff --git a/src/calibre/web/feeds/recipes/__init__.py b/src/calibre/web/feeds/recipes/__init__.py index 2b5652260d..71c4b71483 100644 --- a/src/calibre/web/feeds/recipes/__init__.py +++ b/src/calibre/web/feeds/recipes/__init__.py @@ -23,7 +23,7 @@ recipe_modules = ['recipe_' + r for r in ( 'spiegel_int', 'themarketticker', 'tomshardware', 'xkcd', 'ftd', 'zdnet', 'joelonsoftware', 'telepolis', 'common_dreams', 'nin', 'tomshardware_de', 'pagina12', 'infobae', 'ambito', 'elargentino', 'sueddeutsche', 'the_age', - 'laprensa', + 'laprensa', 'amspec', )] import re, imp, inspect, time, os diff --git a/src/calibre/web/feeds/recipes/recipe_amspec.py b/src/calibre/web/feeds/recipes/recipe_amspec.py new file mode 100644 index 0000000000..b01fadee77 --- /dev/null +++ b/src/calibre/web/feeds/recipes/recipe_amspec.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python + +__license__ = 'GPL v3' +__copyright__ = '2009, Darko Miletic ' +''' +spectator.org +''' + +from calibre.web.feeds.news import BasicNewsRecipe + +class TheAmericanSpectator(BasicNewsRecipe): + title = 'The American Spectator' + __author__ = 'Darko Miletic' + description = 'news from USA' + oldest_article = 7 + max_articles_per_feed = 100 + no_stylesheets = True + use_embedded_content = False + INDEX = 'http://spectator.org' + + html2lrf_options = [ + '--comment' , description + , '--category' , 'news, politics, USA' + , '--publisher' , title + ] + + keep_only_tags = [ + dict(name='div', attrs={'class':'post inner'}) + ,dict(name='div', attrs={'class':'author-bio'}) + ] + + remove_tags = [ + dict(name='object') + ,dict(name='div', attrs={'class':'col3' }) + ,dict(name='div', attrs={'class':'post-options' }) + ,dict(name='p' , attrs={'class':'letter-editor'}) + ,dict(name='div', attrs={'class':'social' }) + ] + + feeds = [ (u'Articles', u'http://feedproxy.google.com/amspecarticles')] + + def get_cover_url(self): + cover_url = None + soup = self.index_to_soup(self.INDEX) + link_item = soup.find('a',attrs={'class':'cover'}) + if link_item: + soup2 = self.index_to_soup(link_item['href']) + link_item2 = soup2.find('div',attrs={'class':'post inner issues'}) + cover_url = self.INDEX + link_item2.img['src'] + return cover_url + + def print_version(self, url): + return url + '/print'