From 1770f7bf74cf1c9330327b76778206b89ac4e7e1 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 19 Apr 2009 14:44:37 -0700 Subject: [PATCH 01/16] Ported structure detection code and added plugin for FB2 input. --- src/calibre/customize/builtins.py | 4 +- src/calibre/ebooks/conversion/cli.py | 25 ++- src/calibre/ebooks/conversion/plumber.py | 91 ++++++++++- src/calibre/ebooks/epub/from_any.py | 109 ++----------- src/calibre/ebooks/{lrf => }/fb2/__init__.py | 0 src/calibre/ebooks/{lrf => }/fb2/fb2.xsl | 0 src/calibre/ebooks/fb2/input.py | 74 +++++++++ src/calibre/ebooks/lrf/fb2/convert_from.py | 125 --------------- src/calibre/ebooks/oeb/base.py | 61 ++++++- src/calibre/ebooks/oeb/iterator.py | 8 +- src/calibre/ebooks/oeb/reader.py | 3 +- src/calibre/ebooks/oeb/transforms/split.py | 6 +- .../ebooks/oeb/transforms/structure.py | 151 ++++++++++++++++++ src/calibre/linux.py | 10 -- upload.py | 2 +- 15 files changed, 422 insertions(+), 247 deletions(-) rename src/calibre/ebooks/{lrf => }/fb2/__init__.py (100%) rename src/calibre/ebooks/{lrf => }/fb2/fb2.xsl (100%) create mode 100644 src/calibre/ebooks/fb2/input.py delete mode 100644 src/calibre/ebooks/lrf/fb2/convert_from.py create mode 100644 src/calibre/ebooks/oeb/transforms/structure.py diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index 08824a3591..a56d13fd60 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -281,6 +281,7 @@ from calibre.ebooks.mobi.input import MOBIInput from calibre.ebooks.pdf.input import PDFInput from calibre.ebooks.txt.input import TXTInput from calibre.ebooks.lit.input import LITInput +from calibre.ebooks.fb2.input import FB2Input from calibre.ebooks.html.input import HTMLInput from calibre.ebooks.oeb.output import OEBOutput from calibre.ebooks.txt.output import TXTOutput @@ -288,7 +289,8 @@ from calibre.ebooks.pdf.output import PDFOutput from calibre.customize.profiles import input_profiles, output_profiles plugins = [HTML2ZIP, EPUBInput, MOBIInput, PDFInput, HTMLInput, - TXTInput, OEBOutput, TXTOutput, PDFOutput, LITInput] + TXTInput, OEBOutput, TXTOutput, PDFOutput, LITInput, + FB2Input] plugins += [x for x in list(locals().values()) if isinstance(x, type) and \ x.__name__.endswith('MetadataReader')] plugins += [x for x in list(locals().values()) if isinstance(x, type) and \ diff --git a/src/calibre/ebooks/conversion/cli.py b/src/calibre/ebooks/conversion/cli.py index b7336ab30a..6d5401278a 100644 --- a/src/calibre/ebooks/conversion/cli.py +++ b/src/calibre/ebooks/conversion/cli.py @@ -119,6 +119,24 @@ def add_pipeline_options(parser, plumber): ] ), + 'STRUCTURE DETECTION' : ( + _('Control auto-detection of document structure.'), + [ + 'dont_split_on_page_breaks', 'chapter', 'chapter_mark', + ] + ), + + 'TABLE OF CONTENTS' : ( + _('Control the automatic generation of a Table of Contents. By ' + 'default, if the source file has a Table of Contents, it will ' + 'be used in preference to the automatically generated one.'), + [ + 'level1_toc', 'level2_toc', 'level3_toc', + 'toc_threshold', 'max_toc_links', 'no_chapters_in_toc', + 'use_auto_toc', + ] + ), + 'METADATA' : (_('Options to set metadata in the output'), plumber.metadata_option_names, ), @@ -130,7 +148,8 @@ def add_pipeline_options(parser, plumber): } - group_order = ['', 'LOOK AND FEEL', 'METADATA', 'DEBUG'] + group_order = ['', 'LOOK AND FEEL', 'STRUCTURE DETECTION', + 'TABLE OF CONTENTS', 'METADATA', 'DEBUG'] for group in group_order: desc, options = groups[group] @@ -163,6 +182,10 @@ def main(args=sys.argv): add_pipeline_options(parser, plumber) opts = parser.parse_args(args)[0] + y = lambda q : os.path.abspath(os.path.expanduser(q)) + for x in ('read_metadata_from_opf', 'cover'): + if getattr(opts, x, None) is not None: + setattr(opts, x, y(getattr(opts, x))) recommendations = [(n.dest, getattr(opts, n.dest), OptionRecommendation.HIGH) \ for n in parser.options_iter() diff --git a/src/calibre/ebooks/conversion/plumber.py b/src/calibre/ebooks/conversion/plumber.py index 1edeed8d9c..453591e433 100644 --- a/src/calibre/ebooks/conversion/plumber.py +++ b/src/calibre/ebooks/conversion/plumber.py @@ -121,6 +121,88 @@ OptionRecommendation(name='dont_split_on_page_breaks', ) ), +OptionRecommendation(name='level1_toc', + recommended_value=None, level=OptionRecommendation.LOW, + help=_('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.' + ) + ), + +OptionRecommendation(name='level2_toc', + recommended_value=None, level=OptionRecommendation.LOW, + help=_('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.' + ) + ), + +OptionRecommendation(name='level3_toc', + recommended_value=None, level=OptionRecommendation.LOW, + help=_('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.' + ) + ), + +OptionRecommendation(name='use_auto_toc', + recommended_value=False, level=OptionRecommendation.LOW, + help=_('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.' + ) + ), + +OptionRecommendation(name='no_chapters_in_toc', + recommended_value=False, level=OptionRecommendation.LOW, + help=_("Don't add auto-detected chapters to the Table of " + 'Contents.' + ) + ), + +OptionRecommendation(name='toc_threshold', + recommended_value=6, level=OptionRecommendation.LOW, + help=_( + 'If fewer than this number of chapters is detected, then links ' + 'are added to the Table of Contents. Default: %default') + ), + +OptionRecommendation(name='max_toc_links', + recommended_value=50, level=OptionRecommendation.LOW, + help=_('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.' + ) + ), + +OptionRecommendation(name='chapter', + recommended_value="//*[((name()='h1' or name()='h2') and " + "re:test(., 'chapter|book|section|part', 'i')) or @class " + "= 'chapter']", level=OptionRecommendation.LOW, + help=_('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.' + ) + ), + +OptionRecommendation(name='chapter_mark', + recommended_value='pagebreak', level=OptionRecommendation.LOW, + choices=['pagebreak', 'rule', 'both', 'none'], + help=_('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.') + ), + + OptionRecommendation(name='read_metadata_from_opf', recommended_value=None, level=OptionRecommendation.LOW, @@ -130,6 +212,7 @@ OptionRecommendation(name='read_metadata_from_opf', 'file.') ), + OptionRecommendation(name='title', recommended_value=None, level=OptionRecommendation.LOW, help=_('Set the title.')), @@ -237,6 +320,7 @@ OptionRecommendation(name='language', rec = self.get_option_by_name(name) if rec is not None and rec.level <= level: rec.recommended_value = val + rec.level = level def merge_ui_recommendations(self, recommendations): ''' @@ -248,6 +332,7 @@ OptionRecommendation(name='language', rec = self.get_option_by_name(name) if rec is not None and rec.level <= level and rec.level < rec.HIGH: rec.recommended_value = val + rec.level = level def read_user_metadata(self): ''' @@ -332,6 +417,9 @@ OptionRecommendation(name='language', self.opts.source = self.opts.input_profile self.opts.dest = self.opts.output_profile + from calibre.ebooks.oeb.transforms.structure import DetectStructure + DetectStructure()(self.oeb, self.opts) + from calibre.ebooks.oeb.transforms.flatcss import CSSFlattener fbase = self.opts.base_font_size if fbase == 0: @@ -364,6 +452,8 @@ OptionRecommendation(name='language', trimmer = ManifestTrimmer() trimmer(self.oeb, self.opts) + self.oeb.toc.rationalize_play_orders() + self.log.info('Creating %s...'%self.output_plugin.name) self.output_plugin.convert(self.oeb, self.output, self.input_plugin, self.opts, self.log) @@ -384,4 +474,3 @@ def create_oebbook(log, path_or_stream, opts, reader=None): reader()(oeb, path_or_stream) return oeb - diff --git a/src/calibre/ebooks/epub/from_any.py b/src/calibre/ebooks/epub/from_any.py index b3e5281525..196ed59646 100644 --- a/src/calibre/ebooks/epub/from_any.py +++ b/src/calibre/ebooks/epub/from_any.py @@ -15,88 +15,15 @@ from calibre.ebooks import DRMError from calibre.ebooks.epub import config as common_config 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, OPF from calibre.utils.zipfile import ZipFile from calibre.customize.ui import run_plugins_on_preprocess -def lit2opf(path, tdir, opts): - from calibre.ebooks.lit.reader import LitReader - print 'Exploding LIT file:', path - reader = LitReader(path) - reader.extract_content(tdir, False) - 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 - print 'Exploding MOBI file:', path.encode('utf-8') if isinstance(path, unicode) else path - reader = MobiReader(path) - reader.extract_content(tdir) - files = list(walk(tdir)) - opts.encoding = 'utf-8' - for f in files: - if f.lower().endswith('.opf'): - return f - html_pat = re.compile(r'\.(x){0,1}htm(l){0,1}', re.IGNORECASE) - hf = [f for f in files if html_pat.match(os.path.splitext(f)[1]) is not None] - mi = MetaInformation(os.path.splitext(os.path.basename(path))[0], [_('Unknown')]) - opf = OPFCreator(tdir, mi) - opf.create_manifest([(hf[0], None)]) - opf.create_spine([hf[0]]) - ans = os.path.join(tdir, 'metadata.opf') - opf.render(open(ans, 'wb')) - return ans - -def fb22opf(path, tdir, opts): - from calibre.ebooks.lrf.fb2.convert_from import to_html - print 'Converting FB2 to HTML...' - return to_html(path, tdir) - def rtf2opf(path, tdir, opts): from calibre.ebooks.lrf.rtf.convert_from import generate_html generate_html(path, tdir) return os.path.join(tdir, 'metadata.opf') -def txt2opf(path, tdir, opts): - from calibre.ebooks.lrf.txt.convert_from import generate_html - generate_html(path, opts.encoding, tdir) - return os.path.join(tdir, 'metadata.opf') - -def pdf2opf(path, tdir, opts): - from calibre.ebooks.lrf.pdf.convert_from import generate_html - generate_html(path, tdir) - opts.dont_split_on_page_breaks = True - return os.path.join(tdir, 'metadata.opf') - def epub2opf(path, tdir, opts): zf = ZipFile(path) zf.extractall(tdir) @@ -110,35 +37,23 @@ def epub2opf(path, tdir, opts): if opf and os.path.exists(encfile): if not process_encryption(encfile, opf): raise DRMError(os.path.basename(path)) - + if opf is None: raise ValueError('%s is not a valid EPUB file'%path) return opf - + def odt2epub(path, tdir, opts): from calibre.ebooks.odt.to_oeb import Extract opts.encoding = 'utf-8' return Extract()(path, tdir) -MAP = { - 'lit' : lit2opf, - 'mobi' : mobi2opf, - 'prc' : mobi2opf, - 'azw' : mobi2opf, - 'fb2' : fb22opf, - 'rtf' : rtf2opf, - 'txt' : txt2opf, - 'pdf' : pdf2opf, - 'epub' : epub2opf, - 'odt' : odt2epub, - } -SOURCE_FORMATS = ['lit', 'mobi', 'prc', 'azw', 'fb2', 'odt', 'rtf', +SOURCE_FORMATS = ['lit', 'mobi', 'prc', 'azw', 'fb2', 'odt', 'rtf', 'txt', 'pdf', 'rar', 'zip', 'oebzip', 'htm', 'html', 'epub'] def unarchive(path, tdir): extract(path, tdir) files = list(walk(tdir)) - + for ext in ['opf'] + list(MAP.keys()): for f in files: if f.lower().endswith('.'+ext): @@ -147,32 +62,32 @@ def unarchive(path, tdir): return f, ext return find_html_index(files) -def any2epub(opts, path, notification=None, create_epub=True, +def any2epub(opts, path, notification=None, create_epub=True, oeb_cover=False, extract_to=None): path = run_plugins_on_preprocess(path) ext = os.path.splitext(path)[1] if not ext: raise ValueError('Unknown file type: '+path) ext = ext.lower()[1:] - + if opts.output is None: opts.output = os.path.splitext(os.path.basename(path))[0]+'.epub' - + with nested(TemporaryDirectory('_any2epub1'), TemporaryDirectory('_any2epub2')) as (tdir1, tdir2): if ext in ['rar', 'zip', 'oebzip']: path, ext = unarchive(path, tdir1) print 'Found %s file in archive'%(ext.upper()) - + if ext in MAP.keys(): path = MAP[ext](path, tdir2, opts) ext = 'opf' - - + + if re.match(r'((x){0,1}htm(l){0,1})|opf', ext) is None: raise ValueError('Conversion from %s is not supported'%ext.upper()) - + print 'Creating EPUB file...' - html2epub(path, opts, notification=notification, + html2epub(path, opts, notification=notification, create_epub=create_epub, oeb_cover=oeb_cover, extract_to=extract_to) diff --git a/src/calibre/ebooks/lrf/fb2/__init__.py b/src/calibre/ebooks/fb2/__init__.py similarity index 100% rename from src/calibre/ebooks/lrf/fb2/__init__.py rename to src/calibre/ebooks/fb2/__init__.py diff --git a/src/calibre/ebooks/lrf/fb2/fb2.xsl b/src/calibre/ebooks/fb2/fb2.xsl similarity index 100% rename from src/calibre/ebooks/lrf/fb2/fb2.xsl rename to src/calibre/ebooks/fb2/fb2.xsl diff --git a/src/calibre/ebooks/fb2/input.py b/src/calibre/ebooks/fb2/input.py new file mode 100644 index 0000000000..d96758a4bd --- /dev/null +++ b/src/calibre/ebooks/fb2/input.py @@ -0,0 +1,74 @@ +from __future__ import with_statement +__license__ = 'GPL v3' +__copyright__ = '2008, Anatoly Shipitsin ' +""" +Convert .fb2 files to .lrf +""" +import os +from base64 import b64decode +from lxml import etree + +from calibre.customize.conversion import InputFormatPlugin, OptionRecommendation +from calibre import guess_type + +FB2NS = 'http://www.gribuser.ru/xml/fictionbook/2.0' + +class FB2Input(InputFormatPlugin): + + name = 'FB2 Input' + author = 'Anatoly Shipitsin' + description = 'Convert FB2 files to HTML' + file_types = set(['fb2']) + + recommendations = set([ + ('level1_toc', '//h:h1', OptionRecommendation.MED), + ('level2_toc', '//h:h2', OptionRecommendation.MED), + ('level3_toc', '//h:h3', OptionRecommendation.MED), + ]) + + def convert(self, stream, options, file_ext, log, + accelerators): + from calibre.resources import fb2_xsl + from calibre.ebooks.metadata.opf2 import OPFCreator + from calibre.ebooks.metadata.meta import get_metadata + from calibre.ebooks.oeb.base import XLINK_NS + NAMESPACES = {'f':FB2NS, 'l':XLINK_NS} + + log.debug('Parsing XML...') + parser = etree.XMLParser(recover=True, no_network=True) + doc = etree.parse(stream, parser) + self.extract_embedded_content(doc) + log.debug('Converting XML to HTML...') + styledoc = etree.fromstring(fb2_xsl) + + transform = etree.XSLT(styledoc) + result = transform(doc) + open('index.xhtml', 'wb').write(transform.tostring(result)) + stream.seek(0) + mi = get_metadata(stream, 'fb2') + if not mi.title: + mi.title = _('Unknown') + if not mi.authors: + mi.authors = [_('Unknown')] + opf = OPFCreator(os.getcwdu(), mi) + entries = [(f, guess_type(f)[0]) for f in os.listdir('.')] + opf.create_manifest(entries) + opf.create_spine(['index.xhtml']) + + for img in doc.xpath('//f:coverpage/f:image', namespaces=NAMESPACES): + href = img.get('{%s}href'%XLINK_NS, img.get('href', None)) + if href is not None: + if href.startswith('#'): + href = href[1:] + opf.guide.set_cover(os.path.abspath(href)) + + opf.render(open('metadata.opf', 'wb')) + return os.path.join(os.getcwd(), 'metadata.opf') + + def extract_embedded_content(self, doc): + for elem in doc.xpath('./*'): + if 'binary' in elem.tag and elem.attrib.has_key('id'): + fname = elem.attrib['id'] + data = b64decode(elem.text.strip()) + open(fname, 'wb').write(data) + diff --git a/src/calibre/ebooks/lrf/fb2/convert_from.py b/src/calibre/ebooks/lrf/fb2/convert_from.py deleted file mode 100644 index 24562e708c..0000000000 --- a/src/calibre/ebooks/lrf/fb2/convert_from.py +++ /dev/null @@ -1,125 +0,0 @@ -from __future__ import with_statement -__license__ = 'GPL v3' -__copyright__ = '2008, Anatoly Shipitsin ' -""" -Convert .fb2 files to .lrf -""" -import os, sys, shutil, logging -from base64 import b64decode -from lxml import etree - -from calibre.ebooks.lrf import option_parser as lrf_option_parser -from calibre.ebooks.metadata.meta import get_metadata -from calibre.ebooks.lrf.html.convert_from import process_file as html_process_file -from calibre import setup_cli_handlers -from calibre.resources import fb2_xsl -from calibre.ptempfile import PersistentTemporaryDirectory -from calibre.ebooks.metadata.opf import OPFCreator -from calibre.ebooks.metadata import MetaInformation - - -def option_parser(): - parser = lrf_option_parser( -_('''%prog [options] mybook.fb2 - - -%prog converts mybook.fb2 to mybook.lrf''')) - parser.add_option('--debug-html-generation', action='store_true', default=False, - dest='debug_html_generation', help=_('Print generated HTML to stdout and quit.')) - parser.add_option('--keep-intermediate-files', action='store_true', default=False, - help=_('Keep generated HTML files after completing conversion to LRF.')) - return parser - -def extract_embedded_content(doc): - for elem in doc.xpath('./*'): - if 'binary' in elem.tag and elem.attrib.has_key('id'): - fname = elem.attrib['id'] - data = b64decode(elem.text.strip()) - open(fname, 'wb').write(data) - -def to_html(fb2file, tdir): - fb2file = os.path.abspath(fb2file) - cwd = os.getcwd() - try: - os.chdir(tdir) - print 'Parsing XML...' - parser = etree.XMLParser(recover=True, no_network=True) - doc = etree.parse(fb2file, parser) - extract_embedded_content(doc) - print 'Converting XML to HTML...' - styledoc = etree.fromstring(fb2_xsl) - - transform = etree.XSLT(styledoc) - result = transform(doc) - open('index.html', 'wb').write(transform.tostring(result)) - try: - mi = get_metadata(open(fb2file, 'rb'), 'fb2') - except: - mi = MetaInformation(None, None) - if not mi.title: - mi.title = os.path.splitext(os.path.basename(fb2file))[0] - if not mi.authors: - mi.authors = [_('Unknown')] - opf = OPFCreator(tdir, mi) - opf.create_manifest([('index.html', None)]) - opf.create_spine(['index.html']) - opf.render(open('metadata.opf', 'wb')) - return os.path.join(tdir, 'metadata.opf') - finally: - os.chdir(cwd) - - -def generate_html(fb2file, encoding, logger): - tdir = PersistentTemporaryDirectory('_fb22lrf') - to_html(fb2file, tdir) - return os.path.join(tdir, 'index.html') - -def process_file(path, options, logger=None): - if logger is None: - level = logging.DEBUG if options.verbose else logging.INFO - logger = logging.getLogger('fb22lrf') - setup_cli_handlers(logger, level) - fb2 = os.path.abspath(os.path.expanduser(path)) - f = open(fb2, 'rb') - mi = get_metadata(f, 'fb2') - f.close() - htmlfile = generate_html(fb2, options.encoding, logger) - tdir = os.path.dirname(htmlfile) - cwd = os.getcwdu() - try: - if not options.output: - ext = '.lrs' if options.lrs else '.lrf' - options.output = os.path.abspath(os.path.basename(os.path.splitext(path)[0]) + ext) - options.output = os.path.abspath(os.path.expanduser(options.output)) - if not mi.title: - mi.title = os.path.splitext(os.path.basename(fb2))[0] - if (not options.title or options.title == _('Unknown')): - options.title = mi.title - if (not options.author or options.author == _('Unknown')) and mi.authors: - options.author = mi.authors.pop() - if (not options.category or options.category == _('Unknown')) and mi.category: - options.category = mi.category - if (not options.freetext or options.freetext == _('Unknown')) and mi.comments: - options.freetext = mi.comments - os.chdir(tdir) - html_process_file(htmlfile, options, logger) - finally: - os.chdir(cwd) - if getattr(options, 'keep_intermediate_files', False): - logger.debug('Intermediate files in '+ tdir) - else: - shutil.rmtree(tdir) - -def main(args=sys.argv, logger=None): - parser = option_parser() - options, args = parser.parse_args(args) - if len(args) != 2: - parser.print_help() - print - print 'No fb2 file specified' - return 1 - process_file(args[1], options, logger) - return 0 - -if __name__ == '__main__': - sys.exit(main()) diff --git a/src/calibre/ebooks/oeb/base.py b/src/calibre/ebooks/oeb/base.py index dda36a7500..85510e2127 100644 --- a/src/calibre/ebooks/oeb/base.py +++ b/src/calibre/ebooks/oeb/base.py @@ -41,10 +41,12 @@ NCX_NS = 'http://www.daisy.org/z3986/2005/ncx/' SVG_NS = 'http://www.w3.org/2000/svg' XLINK_NS = 'http://www.w3.org/1999/xlink' CALIBRE_NS = 'http://calibre.kovidgoyal.net/2009/metadata' +RE_NS = 'http://exslt.org/regular-expressions' + XPNSMAP = {'h' : XHTML_NS, 'o1' : OPF1_NS, 'o2' : OPF2_NS, 'd09': DC09_NS, 'd10': DC10_NS, 'd11': DC11_NS, 'xsi': XSI_NS, 'dt' : DCTERMS_NS, 'ncx': NCX_NS, - 'svg': SVG_NS, 'xl' : XLINK_NS} + 'svg': SVG_NS, 'xl' : XLINK_NS, 're': RE_NS} OPF1_NSMAP = {'dc': DC11_NS, 'oebpackage': OPF1_NS} OPF2_NSMAP = {'opf': OPF2_NS, 'dc': DC11_NS, 'dcterms': DCTERMS_NS, 'xsi': XSI_NS, 'calibre': CALIBRE_NS} @@ -1256,16 +1258,21 @@ class TOC(object): :attr:`klass`: Optional semantic class referenced by this node. :attr:`id`: Option unique identifier for this node. """ - def __init__(self, title=None, href=None, klass=None, id=None): + def __init__(self, title=None, href=None, klass=None, id=None, + play_order=None): self.title = title self.href = urlnormalize(href) if href else href self.klass = klass self.id = id self.nodes = [] + self.play_order = 0 + if play_order is None: + play_order = self.next_play_order() + self.play_order = play_order - def add(self, title, href, klass=None, id=None): + def add(self, title, href, klass=None, id=None, play_order=0): """Create and return a new sub-node of this node.""" - node = TOC(title, href, klass, id) + node = TOC(title, href, klass, id, play_order) self.nodes.append(node) return node @@ -1276,6 +1283,18 @@ class TOC(object): for node in child.iter(): yield node + def count(self): + return len(list(self.iter())) - 1 + + def next_play_order(self): + return max([x.play_order for x in self.iter()])+1 + + def has_href(self, href): + for x in self.iter(): + if x.href == href: + return True + return False + def iterdescendants(self): """Iterate over all descendant nodes in depth-first order.""" for child in self.nodes: @@ -1309,6 +1328,10 @@ class TOC(object): except ValueError: return 1 + def __str__(self): + return 'TOC: %s --> %s'%(self.title, self.href) + + def to_opf1(self, tour): for node in self.nodes: element(tour, 'site', attrib={ @@ -1319,7 +1342,7 @@ class TOC(object): def to_ncx(self, parent): for node in self.nodes: id = node.id or unicode(uuid.uuid4()) - attrib = {'id': id, 'playOrder': '0'} + attrib = {'id': id, 'playOrder': str(node.play_order)} if node.klass: attrib['class'] = node.klass point = element(parent, NCX('navPoint'), attrib=attrib) @@ -1329,6 +1352,34 @@ class TOC(object): node.to_ncx(point) return parent + def rationalize_play_orders(self): + ''' + Ensure that all nodes with the same play_order have the same href and + with different play_orders have different hrefs. + ''' + def po_node(n): + for x in self.iter(): + if x is n: + return + if x.play_order == n.play_order: + return x + + def href_node(n): + for x in self.iter(): + if x is n: + return + if x.href == n.href: + return x + + for x in self.iter(): + y = po_node(x) + if y is not None: + if x.href != y.href: + x.play_order = getattr(href_node(x), 'play_order', + self.next_play_order()) + y = href_node(x) + if y is not None: + x.play_order = y.play_order class PageList(object): """Collection of named "pages" to mapped positions within an OEB data model diff --git a/src/calibre/ebooks/oeb/iterator.py b/src/calibre/ebooks/oeb/iterator.py index 81e1f89029..ab3e90083d 100644 --- a/src/calibre/ebooks/oeb/iterator.py +++ b/src/calibre/ebooks/oeb/iterator.py @@ -118,6 +118,7 @@ class EbookIterator(object): print 'Loaded embedded font:', repr(family) def __enter__(self): + self.delete_on_exit = [] self._tdir = TemporaryDirectory('_ebook_iter') self.base = self._tdir.__enter__() from calibre.ebooks.conversion.plumber import Plumber @@ -137,9 +138,11 @@ class EbookIterator(object): cover = self.opf.cover if self.ebook_ext in ('lit', 'mobi', 'prc', 'opf') and cover: - cfile = os.path.join(os.path.dirname(self.spine[0]), 'calibre_ei_cover.html') + cfile = os.path.join(os.path.dirname(self.spine[0]), + 'calibre_iterator_cover.html') open(cfile, 'wb').write(TITLEPAGE%cover) self.spine[0:0] = [SpineItem(cfile)] + self.delete_on_exit.append(cfile) if self.opf.path_to_html_toc is not None and \ self.opf.path_to_html_toc not in self.spine: @@ -221,3 +224,6 @@ class EbookIterator(object): def __exit__(self, *args): self._tdir.__exit__(*args) + for x in self.delete_on_exit: + if os.path.exists(x): + os.remove(x) diff --git a/src/calibre/ebooks/oeb/reader.py b/src/calibre/ebooks/oeb/reader.py index 6f0ff44bc9..02b3b92b01 100644 --- a/src/calibre/ebooks/oeb/reader.py +++ b/src/calibre/ebooks/oeb/reader.py @@ -343,7 +343,8 @@ class OEBReader(object): continue id = child.get('id') klass = child.get('class') - node = toc.add(title, href, id=id, klass=klass) + po = int(child.get('playOrder', self.oeb.toc.next_play_order())) + node = toc.add(title, href, id=id, klass=klass, play_order=po) self._toc_from_navpoint(item, node, child) def _toc_from_ncx(self, item): diff --git a/src/calibre/ebooks/oeb/transforms/split.py b/src/calibre/ebooks/oeb/transforms/split.py index 33ab14b73d..bc7e4e195d 100644 --- a/src/calibre/ebooks/oeb/transforms/split.py +++ b/src/calibre/ebooks/oeb/transforms/split.py @@ -15,12 +15,10 @@ from lxml.etree import XPath as _XPath from lxml import etree from lxml.cssselect import CSSSelector -from calibre.ebooks.oeb.base import OEB_STYLES, XPNSMAP, urldefrag, \ - rewrite_links +from calibre.ebooks.oeb.base import OEB_STYLES, XPNSMAP as NAMESPACES, \ + urldefrag, rewrite_links from calibre.ebooks.epub import tostring, rules -NAMESPACES = dict(XPNSMAP) -NAMESPACES['re'] = 'http://exslt.org/regular-expressions' XPath = functools.partial(_XPath, namespaces=NAMESPACES) diff --git a/src/calibre/ebooks/oeb/transforms/structure.py b/src/calibre/ebooks/oeb/transforms/structure.py new file mode 100644 index 0000000000..0f1502ef03 --- /dev/null +++ b/src/calibre/ebooks/oeb/transforms/structure.py @@ -0,0 +1,151 @@ +#!/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' + +from lxml import etree +from urlparse import urlparse + +from calibre.ebooks.oeb.base import XPNSMAP, TOC +XPath = lambda x: etree.XPath(x, namespaces=XPNSMAP) + +class DetectStructure(object): + + def __call__(self, oeb, opts): + self.log = oeb.log + self.oeb = oeb + self.opts = opts + self.log('Detecting structure...') + + self.detect_chapters() + if self.oeb.auto_generated_toc or opts.use_auto_toc: + orig_toc = self.oeb.toc + self.oeb.toc = TOC() + self.create_level_based_toc() + if self.oeb.toc.count() < 1: + if not opts.no_chapters_in_toc and self.detected_chapters: + self.create_toc_from_chapters() + if self.oeb.toc.count() < opts.toc_threshold: + self.create_toc_from_links() + if self.oeb.toc.count() < 2 and orig_toc.count() > 2: + self.oeb.toc = orig_toc + else: + self.oeb.auto_generated_toc = True + self.log('Auto generated TOC with %d entries.' % + self.oeb.toc.count()) + + + def detect_chapters(self): + self.detected_chapters = [] + if self.opts.chapter: + chapter_xpath = XPath(self.opts.chapter) + for item in self.oeb.spine: + for x in chapter_xpath(item.data): + self.detected_chapters.append((item, x)) + + chapter_mark = self.opts.chapter_mark + page_break_before = 'display: block; page-break-before: always' + page_break_after = 'display: block; page-break-after: always' + for item, elem in self.detected_chapters: + text = u' '.join([t.strip() for t in elem.xpath('descendant::text()')]) + self.log('\tDetected chapter:', text[:50]) + if chapter_mark == 'none': + continue + elif chapter_mark == 'rule': + mark = etree.Element('hr') + elif chapter_mark == 'pagebreak': + mark = etree.Element('div', style=page_break_after) + else: # chapter_mark == 'both': + mark = etree.Element('hr', style=page_break_before) + elem.addprevious(mark) + + def create_level_based_toc(self): + if self.opts.level1_toc is None: + return + for item in self.oeb.spine: + self.add_leveled_toc_items(item) + + def create_toc_from_chapters(self): + counter = self.oeb.toc.next_play_order() + for item, elem in self.detected_chapters: + text, href = self.elem_to_link(item, elem, counter) + self.oeb.toc.add(text, href, play_order=counter) + counter += 1 + + def create_toc_from_links(self): + for item in self.oeb.spine: + for a in item.data.xpath('//h:a[@href]'): + href = a.get('href') + purl = urlparse(href) + if not purl[0] or purl[0] == 'file': + href, frag = purl.path, purl.fragment + href = item.abshref(href) + if frag: + href = '#'.join((href, frag)) + if not self.oeb.toc.has_href(href): + text = u' '.join([t.strip() for t in \ + a.xpath('descendant::text()')]) + text = text[:100].strip() + if not self.oeb.toc.has_text(text): + self.oeb.toc.add(text, href, + play_order=self.oeb.toc.next_play_order()) + + + def elem_to_link(self, item, elem, counter): + text = u' '.join([t.strip() for t in elem.xpath('descendant::text()')]) + text = text[:100].strip() + id = elem.get('id', 'calibre_toc_%d'%counter) + elem.set('id', id) + href = '#'.join((item.href, id)) + return text, href + + + def add_leveled_toc_items(self, item): + level1 = XPath(self.opts.level1_toc)(item.data) + level1_order = [] + + counter = 1 + if level1: + added = {} + for elem in level1: + text, _href = self.elem_to_link(item, elem, counter) + counter += 1 + if text: + node = self.oeb.toc.add(text, _href, + play_order=self.oeb.toc.next_play_order()) + level1_order.append(node) + added[elem] = node + #node.add(_('Top'), _href) + if self.opts.level2_toc is not None: + added2 = {} + level2 = list(XPath(self.opts.level2_toc)(item.data)) + for elem in level2: + level1 = None + for item in item.data.iterdescendants(): + if item in added.keys(): + level1 = added[item] + elif item == elem and level1 is not None: + text, _href = self.elem_to_link(item, elem, counter) + counter += 1 + if text: + added2[elem] = level1.add(text, _href, + play_order=self.oeb.toc.next_play_order()) + if self.opts.level3_toc is not None: + level3 = list(XPath(self.opts.level3_toc)(item.data)) + for elem in level3: + level2 = None + for item in item.data.iterdescendants(): + if item in added2.keys(): + level2 = added2[item] + elif item == elem and level2 is not None: + text, _href = \ + self.elem_to_link(item, elem, counter) + counter += 1 + if text: + level2.add(text, _href, + play_order=self.oeb.toc.next_play_order()) + + diff --git a/src/calibre/linux.py b/src/calibre/linux.py index ee51370b61..2d13ea2730 100644 --- a/src/calibre/linux.py +++ b/src/calibre/linux.py @@ -27,10 +27,6 @@ entry_points = { 'lrs2lrf = calibre.ebooks.lrf.lrs.convert_from:main', 'isbndb = calibre.ebooks.metadata.isbndb:main', 'librarything = calibre.ebooks.metadata.library_thing:main', - 'comic2lrf = calibre.ebooks.lrf.comic.convert_from:main', - 'comic2epub = calibre.ebooks.epub.from_comic:main', - 'comic2mobi = calibre.ebooks.mobi.from_comic:main', - 'comic2pdf = calibre.ebooks.pdf.from_comic:main', 'calibre-debug = calibre.debug:main', 'calibredb = calibre.library.cli:main', 'calibre-fontconfig = calibre.utils.fontconfig:main', @@ -151,8 +147,6 @@ def setup_completion(fatal_errors): from calibre.ebooks.lrf.pdf.reflow import option_parser as pdfhtmlop from calibre.web.feeds.main import option_parser as feeds2disk from calibre.web.feeds.recipes import titles as feed_titles - from calibre.ebooks.lrf.comic.convert_from import option_parser as comicop - from calibre.ebooks.epub.from_comic import option_parser as comic2epub from calibre.ebooks.metadata.fetch import option_parser as fem_op from calibre.gui2.main import option_parser as guiop from calibre.utils.smtp import option_parser as smtp_op @@ -181,10 +175,6 @@ def setup_completion(fatal_errors): f.write(opts_and_exts('ebook-meta', metaop, list(meta_filetypes()))) f.write(opts_and_exts('lrfviewer', lrfviewerop, ['lrf'])) f.write(opts_and_exts('pdfrelow', pdfhtmlop, ['pdf'])) - f.write(opts_and_exts('comic2lrf', comicop, ['cbz', 'cbr'])) - f.write(opts_and_exts('comic2epub', comic2epub, ['cbz', 'cbr'])) - f.write(opts_and_exts('comic2mobi', comic2epub, ['cbz', 'cbr'])) - f.write(opts_and_exts('comic2pdf', comic2epub, ['cbz', 'cbr'])) f.write(opts_and_words('feeds2disk', feeds2disk, feed_titles)) f.write(opts_and_words('fetch-ebook-metadata', fem_op, [])) f.write(opts_and_words('calibre-smtp', smtp_op, [])) diff --git a/upload.py b/upload.py index 6bc90aada2..a29e5b097c 100644 --- a/upload.py +++ b/upload.py @@ -139,7 +139,7 @@ class resources(OptionlessCommand): RESOURCES = dict( opf_template = 'ebooks/metadata/opf.xml', ncx_template = 'ebooks/metadata/ncx.xml', - fb2_xsl = 'ebooks/lrf/fb2/fb2.xsl', + fb2_xsl = 'ebooks/fb2/fb2.xsl', metadata_sqlite = 'library/metadata_sqlite.sql', jquery = 'gui2/viewer/jquery.js', jquery_scrollTo = 'gui2/viewer/jquery_scrollTo.js', From a0d9e40869bc5c1cd4572c850c5b1000e5a3d125 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 19 Apr 2009 15:06:19 -0700 Subject: [PATCH 02/16] Add --filter-toc option --- src/calibre/ebooks/conversion/plumber.py | 9 +++++++++ src/calibre/ebooks/oeb/base.py | 10 ++++++++++ src/calibre/ebooks/oeb/transforms/structure.py | 8 ++++++++ 3 files changed, 27 insertions(+) diff --git a/src/calibre/ebooks/conversion/plumber.py b/src/calibre/ebooks/conversion/plumber.py index 453591e433..3a2d39c314 100644 --- a/src/calibre/ebooks/conversion/plumber.py +++ b/src/calibre/ebooks/conversion/plumber.py @@ -176,6 +176,15 @@ OptionRecommendation(name='max_toc_links', ) ), +OptionRecommendation(name='toc_filter', + recommended_value=None, level=OptionRecommendation.LOW, + help=_('Remove entries from the Table of Contents whose titles ' + 'match the specified regular expression. Matching entries and all ' + 'their children are removed.' + ) + ), + + OptionRecommendation(name='chapter', recommended_value="//*[((name()='h1' or name()='h2') and " "re:test(., 'chapter|book|section|part', 'i')) or @class " diff --git a/src/calibre/ebooks/oeb/base.py b/src/calibre/ebooks/oeb/base.py index 85510e2127..70303470d7 100644 --- a/src/calibre/ebooks/oeb/base.py +++ b/src/calibre/ebooks/oeb/base.py @@ -1276,6 +1276,16 @@ class TOC(object): self.nodes.append(node) return node + def remove(self, node): + for child in self.nodes: + if child is node: + self.nodes.remove(child) + return True + else: + if child.remove(node): + return True + return False + def iter(self): """Iterate over this node and all descendants in depth-first order.""" yield self diff --git a/src/calibre/ebooks/oeb/transforms/structure.py b/src/calibre/ebooks/oeb/transforms/structure.py index 0f1502ef03..6499a5e9c4 100644 --- a/src/calibre/ebooks/oeb/transforms/structure.py +++ b/src/calibre/ebooks/oeb/transforms/structure.py @@ -6,6 +6,8 @@ __license__ = 'GPL v3' __copyright__ = '2009, Kovid Goyal ' __docformat__ = 'restructuredtext en' +import re + from lxml import etree from urlparse import urlparse @@ -37,6 +39,12 @@ class DetectStructure(object): self.log('Auto generated TOC with %d entries.' % self.oeb.toc.count()) + if opts.toc_filter is not None: + regexp = re.compile(opts.toc_filter) + for node in self.oeb.toc.iter(): + if not node.title or regexp.search(node.title) is not None: + self.oeb.toc.remove(node) + def detect_chapters(self): self.detected_chapters = [] From 40dd093352d63eac2be7a205f4ca80492f010d52 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 19 Apr 2009 15:35:13 -0700 Subject: [PATCH 03/16] Switch to using OpenLibrary as the primary source of book covers. Should speed up cover fetching. Also Fix #2316 (Calibre now fetches smaller images as covers) --- src/calibre/ebooks/metadata/library_thing.py | 35 +++++++++++--------- src/calibre/gui2/dialogs/metadata_single.py | 7 ++-- src/calibre/gui2/dialogs/metadata_single.ui | 2 +- src/calibre/web/feeds/news.py | 2 +- 4 files changed, 24 insertions(+), 22 deletions(-) diff --git a/src/calibre/ebooks/metadata/library_thing.py b/src/calibre/ebooks/metadata/library_thing.py index ef41d5e937..0f46c72c75 100644 --- a/src/calibre/ebooks/metadata/library_thing.py +++ b/src/calibre/ebooks/metadata/library_thing.py @@ -4,13 +4,15 @@ __copyright__ = '2008, Kovid Goyal ' Fetch cover from LibraryThing.com based on ISBN number. ''' -import sys, socket, os, re, mechanize +import sys, socket, os, re from calibre import browser as _browser from calibre.utils.config import OptionParser -from calibre.ebooks.BeautifulSoup import BeautifulSoup +from calibre.ebooks.BeautifulSoup import BeautifulSoup browser = None +OPENLIBRARY = 'http://covers.openlibrary.org/b/isbn/%s-L.jpg?default=false' + class LibraryThingError(Exception): pass @@ -30,15 +32,21 @@ def login(username, password, force=True): browser['formusername'] = username browser['formpassword'] = password browser.submit() - -def cover_from_isbn(isbn, timeout=5.): + +def cover_from_isbn(isbn, timeout=5., username=None, password=None): global browser if browser is None: browser = _browser() _timeout = socket.getdefaulttimeout() socket.setdefaulttimeout(timeout) - src = None + src = None + try: + return browser.open(OPENLIBRARY%isbn).read(), 'jpg' + except: + pass # Cover not found + if username and password: + login(username, password, force=False) try: src = browser.open('http://www.librarything.com/isbn/'+isbn).read().decode('utf-8', 'replace') except Exception, err: @@ -55,7 +63,7 @@ def cover_from_isbn(isbn, timeout=5.): url = url.find('img') if url is None: raise LibraryThingError(_('LibraryThing.com server error. Try again later.')) - url = re.sub(r'_SX\d+', '', url['src']) + url = re.sub(r'_S[XY]\d+', '', url['src']) cover_data = browser.open(url).read() return cover_data, url.rpartition('.')[-1] finally: @@ -68,9 +76,9 @@ _(''' Fetch a cover image for the book identified by ISBN from LibraryThing.com ''')) - parser.add_option('-u', '--username', default=None, + parser.add_option('-u', '--username', default=None, help='Username for LibraryThing.com') - parser.add_option('-p', '--password', default=None, + parser.add_option('-p', '--password', default=None, help='Password for LibraryThing.com') return parser @@ -81,13 +89,8 @@ def main(args=sys.argv): parser.print_help() return 1 isbn = args[1] - if opts.username and opts.password: - try: - login(opts.username, opts.password) - except mechanize.FormNotFoundError: - raise LibraryThingError(_('LibraryThing.com server error. Try again later.')) - - cover_data, ext = cover_from_isbn(isbn) + cover_data, ext = cover_from_isbn(isbn, username=opts.username, + password=opts.password) if not ext: ext = 'jpg' oname = os.path.abspath(isbn+'.'+ext) @@ -96,4 +99,4 @@ def main(args=sys.argv): return 0 if __name__ == '__main__': - sys.exit(main()) \ No newline at end of file + sys.exit(main()) diff --git a/src/calibre/gui2/dialogs/metadata_single.py b/src/calibre/gui2/dialogs/metadata_single.py index 4a74c87097..1a2f2d3129 100644 --- a/src/calibre/gui2/dialogs/metadata_single.py +++ b/src/calibre/gui2/dialogs/metadata_single.py @@ -20,7 +20,7 @@ from calibre.gui2.dialogs.password import PasswordDialog from calibre.gui2.widgets import ProgressIndicator from calibre.ebooks import BOOK_EXTENSIONS from calibre.ebooks.metadata import authors_to_sort_string, string_to_authors, authors_to_string -from calibre.ebooks.metadata.library_thing import login, cover_from_isbn +from calibre.ebooks.metadata.library_thing import cover_from_isbn from calibre import islinux from calibre.ebooks.metadata.meta import get_metadata from calibre.utils.config import prefs @@ -60,9 +60,8 @@ class CoverFetcher(QThread): return self.isbn = results[0] - if self.username and self.password: - login(self.username, self.password, force=False) - self.cover_data = cover_from_isbn(self.isbn, timeout=self.timeout)[0] + self.cover_data = cover_from_isbn(self.isbn, timeout=self.timeout, + username=self.username, password=self.password)[0] except Exception, e: self.exception = e self.traceback = traceback.format_exc() diff --git a/src/calibre/gui2/dialogs/metadata_single.ui b/src/calibre/gui2/dialogs/metadata_single.ui index 6e96c8d741..5772609de6 100644 --- a/src/calibre/gui2/dialogs/metadata_single.ui +++ b/src/calibre/gui2/dialogs/metadata_single.ui @@ -589,7 +589,7 @@ - Fetch &cover image from server + Download &cover image diff --git a/src/calibre/web/feeds/news.py b/src/calibre/web/feeds/news.py index 2494951ccd..bb3610343e 100644 --- a/src/calibre/web/feeds/news.py +++ b/src/calibre/web/feeds/news.py @@ -265,7 +265,7 @@ class BasicNewsRecipe(object, LoggingInterface): def get_feeds(self): ''' - Return a list of :term:RSS feeds to fetch for this profile. Each element of the list + Return a list of :term:`RSS` feeds to fetch for this profile. Each element of the list must be a 2-element tuple of the form (title, url). If title is None or an empty string, the title from the feed is used. This method is useful if your recipe needs to do some processing to figure out the list of feeds to download. If From fc599043699940c4e2549654f54785f0d3ed6a90 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 19 Apr 2009 15:46:25 -0700 Subject: [PATCH 04/16] IGN:Now that we use OpenLibrary for covers. Remove password dialog for LibraryThing --- src/calibre/gui2/dialogs/metadata_single.py | 14 ++++++-------- src/calibre/gui2/dialogs/metadata_single.ui | 13 +------------ 2 files changed, 7 insertions(+), 20 deletions(-) diff --git a/src/calibre/gui2/dialogs/metadata_single.py b/src/calibre/gui2/dialogs/metadata_single.py index 1a2f2d3129..05756407b3 100644 --- a/src/calibre/gui2/dialogs/metadata_single.py +++ b/src/calibre/gui2/dialogs/metadata_single.py @@ -25,7 +25,6 @@ from calibre import islinux from calibre.ebooks.metadata.meta import get_metadata from calibre.utils.config import prefs from calibre.customize.ui import run_plugins_on_import -from calibre.gui2 import config as gui_conf class CoverFetcher(QThread): @@ -289,7 +288,6 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog): self.series_index.setValue(self.db.series_index(row)) QObject.connect(self.series, SIGNAL('currentIndexChanged(int)'), self.enable_series_index) QObject.connect(self.series, SIGNAL('editTextChanged(QString)'), self.enable_series_index) - QObject.connect(self.password_button, SIGNAL('clicked()'), self.change_password) self.show() height_of_rest = self.frameGeometry().height() - self.cover.height() @@ -377,15 +375,15 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog): def fetch_cover(self): isbn = unicode(self.isbn.text()).strip() - d = self.lt_password_dialog() - if not gui_conf['asked_library_thing_password'] and \ - (not d.username() or not d.password()): - d.exec_() - gui_conf['asked_library_thing_password'] = True + #d = self.lt_password_dialog() + #if not gui_conf['asked_library_thing_password'] and \ + # (not d.username() or not d.password()): + # d.exec_() + # gui_conf['asked_library_thing_password'] = True self.fetch_cover_button.setEnabled(False) self.setCursor(Qt.WaitCursor) title, author = map(unicode, (self.title.text(), self.authors.text())) - self.cover_fetcher = CoverFetcher(d.username(), d.password(), isbn, + self.cover_fetcher = CoverFetcher(None, None, isbn, self.timeout, title, author) self.cover_fetcher.start() self._hangcheck = QTimer(self) diff --git a/src/calibre/gui2/dialogs/metadata_single.ui b/src/calibre/gui2/dialogs/metadata_single.ui index 5772609de6..2c4ab859a3 100644 --- a/src/calibre/gui2/dialogs/metadata_single.ui +++ b/src/calibre/gui2/dialogs/metadata_single.ui @@ -589,17 +589,7 @@ - Download &cover image - - - - - - - Change the username and/or password for your account at LibraryThing.com - - - Change &password + Download &cover @@ -655,7 +645,6 @@ comments fetch_metadata_button fetch_cover_button - password_button formats add_format_button remove_format_button From db67fe6cd582f67b80ea93ebefb97f580eaa668c Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 19 Apr 2009 15:47:02 -0700 Subject: [PATCH 05/16] version 0.5.8 --- src/calibre/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/constants.py b/src/calibre/constants.py index 138a631b7c..186eb37e34 100644 --- a/src/calibre/constants.py +++ b/src/calibre/constants.py @@ -2,7 +2,7 @@ __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net' __docformat__ = 'restructuredtext en' __appname__ = 'calibre' -__version__ = '0.5.7' +__version__ = '0.5.8' __author__ = "Kovid Goyal " ''' Various run time constants. From cb7d91de134e48a05d9e2756895a99242bd50685 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 19 Apr 2009 15:48:56 -0700 Subject: [PATCH 06/16] IGN:Tag release From dc06ef85cf824a3265b94e6152b1fc2e98330ba3 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 19 Apr 2009 16:06:58 -0700 Subject: [PATCH 07/16] IGN:Updated translations --- src/calibre/translations/ar.po | 1054 +++-- src/calibre/translations/bg.po | 1039 +++-- src/calibre/translations/ca.po | 1042 +++-- src/calibre/translations/cs.po | 1081 +++--- src/calibre/translations/da.po | 1074 +++--- src/calibre/translations/de.po | 1249 +++--- src/calibre/translations/el.po | 1039 +++-- src/calibre/translations/es.po | 1256 +++--- src/calibre/translations/fr.po | 1257 +++--- src/calibre/translations/gl.po | 1039 +++-- src/calibre/translations/he.po | 1039 +++-- src/calibre/translations/hr.po | 1180 +++--- src/calibre/translations/hu.po | 1078 +++--- src/calibre/translations/it.po | 1119 +++--- src/calibre/translations/ja.po | 1393 +++---- src/calibre/translations/nb.po | 1051 +++-- src/calibre/translations/nds.po | 1134 +++--- src/calibre/translations/nl.po | 1063 +++-- src/calibre/translations/pl.po | 1099 +++--- src/calibre/translations/pt.po | 1119 +++--- src/calibre/translations/ro.po | 1039 +++-- src/calibre/translations/ru.po | 1137 +++--- src/calibre/translations/sk.po | 1117 +++--- src/calibre/translations/sl.po | 1063 +++-- src/calibre/translations/sv.po | 1039 +++-- src/calibre/translations/te.po | 1039 +++-- src/calibre/translations/uk.po | 6384 +++++++++++++++++++++++++++++++ 27 files changed, 19546 insertions(+), 15677 deletions(-) create mode 100644 src/calibre/translations/uk.po diff --git a/src/calibre/translations/ar.po b/src/calibre/translations/ar.po index cc771f54f0..50d56ea137 100644 --- a/src/calibre/translations/ar.po +++ b/src/calibre/translations/ar.po @@ -7,14 +7,14 @@ msgid "" msgstr "" "Project-Id-Version: calibre\n" "Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2009-03-29 04:54+0000\n" +"POT-Creation-Date: 2009-04-16 20:18+0000\n" "PO-Revision-Date: 2009-04-04 16:00+0000\n" "Last-Translator: صقر بن عبدالله \n" "Language-Team: Arabic \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2009-04-08 23:20+0000\n" +"X-Launchpad-Export-Date: 2009-04-19 22:56+0000\n" "X-Generator: Launchpad (build Unknown)\n" #: /home/kovid/work/calibre/src/calibre/customize/__init__.py:41 @@ -43,11 +43,11 @@ msgstr "لا يفعل شيءً" #: /home/kovid/work/calibre/src/calibre/ebooks/lrf/pdf/convert_from.py:82 #: /home/kovid/work/calibre/src/calibre/ebooks/lrf/rtf/convert_from.py:179 #: /home/kovid/work/calibre/src/calibre/ebooks/lrf/txt/convert_from.py:70 -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:200 -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:230 -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:233 -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:272 -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:296 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:199 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:229 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:232 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:271 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:301 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/meta.py:53 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/meta.py:55 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/meta.py:95 @@ -55,27 +55,27 @@ msgstr "لا يفعل شيءً" #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/mobi.py:148 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/opf.py:334 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/opf.py:449 -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/opf2.py:862 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/opf2.py:860 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/pdf.py:12 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/topaz.py:29 #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:37 -#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:60 -#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:69 -#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:135 -#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:564 +#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:61 +#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:70 +#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:138 +#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:622 #: /home/kovid/work/calibre/src/calibre/ebooks/odt/to_oeb.py:46 -#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:573 -#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:578 -#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1154 +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:576 +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:581 #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1157 +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1160 #: /home/kovid/work/calibre/src/calibre/ebooks/pdf/pdftrim.py:53 #: /home/kovid/work/calibre/src/calibre/ebooks/pdf/pdftrim.py:54 -#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:182 -#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:189 -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:445 -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:454 -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:606 -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:609 +#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:186 +#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:193 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:453 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:462 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:614 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:617 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf.py:48 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub.py:171 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub.py:173 @@ -85,25 +85,25 @@ msgstr "لا يفعل شيءً" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:33 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:38 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:39 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:121 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:122 #: /home/kovid/work/calibre/src/calibre/gui2/library.py:364 #: /home/kovid/work/calibre/src/calibre/gui2/library.py:377 #: /home/kovid/work/calibre/src/calibre/gui2/library.py:905 #: /home/kovid/work/calibre/src/calibre/gui2/tools.py:61 #: /home/kovid/work/calibre/src/calibre/gui2/tools.py:123 -#: /home/kovid/work/calibre/src/calibre/library/cli.py:263 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:264 #: /home/kovid/work/calibre/src/calibre/library/database.py:916 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:482 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:494 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:876 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:911 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:1218 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:1220 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:1400 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:1423 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:1474 -#: /home/kovid/work/calibre/src/calibre/library/server.py:327 -#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:51 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:497 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:509 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:894 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:929 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:1236 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:1238 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:1418 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:1441 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:1492 +#: /home/kovid/work/calibre/src/calibre/library/server.py:340 +#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:50 msgid "Unknown" msgstr "مجهول" @@ -165,6 +165,7 @@ msgstr "إقرأ ميتاداتا لكتب في أرشيفات RAR" #: /home/kovid/work/calibre/src/calibre/customize/builtins.py:228 #: /home/kovid/work/calibre/src/calibre/customize/builtins.py:238 #: /home/kovid/work/calibre/src/calibre/customize/builtins.py:248 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:259 msgid "Set metadata in %s files" msgstr "ضبط الميتاداتا في الملفات %s" @@ -184,74 +185,70 @@ msgstr "تخصيص الملحقات المحلية" msgid "Disabled plugins" msgstr "ملحقات معطلة" -#: /home/kovid/work/calibre/src/calibre/customize/ui.py:66 +#: /home/kovid/work/calibre/src/calibre/customize/ui.py:73 msgid "No valid plugin found in " msgstr "لا يجد ملحق صالح " -#: /home/kovid/work/calibre/src/calibre/customize/ui.py:185 +#: /home/kovid/work/calibre/src/calibre/customize/ui.py:192 msgid "Initialization of plugin %s failed with traceback:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/ui.py:262 +#: /home/kovid/work/calibre/src/calibre/customize/ui.py:269 msgid "" " %prog options\n" -" \n" +"\n" " Customize calibre by loading external plugins.\n" " " msgstr "" -" %prog options\n" -" \n" -"تخصيص كاليبر بتحميل ملحقات خارجية.\n" -" " -#: /home/kovid/work/calibre/src/calibre/customize/ui.py:268 +#: /home/kovid/work/calibre/src/calibre/customize/ui.py:275 msgid "Add a plugin by specifying the path to the zip file containing it." msgstr "إضافة ملحق يتخصيص مسار إلى ملف zip الذي يحتويه." -#: /home/kovid/work/calibre/src/calibre/customize/ui.py:270 +#: /home/kovid/work/calibre/src/calibre/customize/ui.py:277 msgid "Remove a custom plugin by name. Has no effect on builtin plugins" msgstr "حذف الملحق المخصص عن طريق اسمه. لا يؤثر على الملحقات المضمنة" -#: /home/kovid/work/calibre/src/calibre/customize/ui.py:272 +#: /home/kovid/work/calibre/src/calibre/customize/ui.py:279 msgid "" "Customize plugin. Specify name of plugin and customization string separated " "by a comma." msgstr "تخصيص الملحق حدد اسم الملحق وسلسلة التخصيص وفرقهما بفاصلة." -#: /home/kovid/work/calibre/src/calibre/customize/ui.py:274 +#: /home/kovid/work/calibre/src/calibre/customize/ui.py:281 msgid "List all installed plugins" msgstr "قائمة كل الملحقات المثبتة" -#: /home/kovid/work/calibre/src/calibre/customize/ui.py:276 +#: /home/kovid/work/calibre/src/calibre/customize/ui.py:283 msgid "Enable the named plugin" msgstr "تمكين الملحق المسمى" -#: /home/kovid/work/calibre/src/calibre/customize/ui.py:278 +#: /home/kovid/work/calibre/src/calibre/customize/ui.py:285 msgid "Disable the named plugin" msgstr "تعطيل الملحق المسمى" -#: /home/kovid/work/calibre/src/calibre/devices/cybookg3/driver.py:42 +#: /home/kovid/work/calibre/src/calibre/devices/cybookg3/driver.py:41 #: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:390 -#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:72 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:77 msgid "The reader has no storage card connected." msgstr "ليس للقارئ بطاقة تخزين." -#: /home/kovid/work/calibre/src/calibre/devices/cybookg3/driver.py:61 -#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:91 +#: /home/kovid/work/calibre/src/calibre/devices/cybookg3/driver.py:60 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:96 msgid "There is insufficient free space on the storage card" msgstr "لا توجد مساحة كافية في بطاقة التخزين" -#: /home/kovid/work/calibre/src/calibre/devices/cybookg3/driver.py:63 -#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:93 +#: /home/kovid/work/calibre/src/calibre/devices/cybookg3/driver.py:62 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:98 msgid "There is insufficient free space in main memory" msgstr "لا توجد مساحة كافية في الذاكرة الرئيسية" #: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:140 #: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:168 #: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:196 -#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:195 -#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:231 -#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:258 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:205 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:242 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:269 msgid "Unable to detect the %s disk drive. Try rebooting." msgstr "لم يتمكن من كشف القرص %s. حاول إعادة التشغيل." @@ -499,7 +496,7 @@ msgstr "" msgid "Could not find an ebook inside the archive" msgstr "لم يتمكّن من الحصول على كتاب داخل الأرشيف" -#: /home/kovid/work/calibre/src/calibre/ebooks/epub/from_html.py:233 +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/from_html.py:262 msgid "" "%prog [options] file.html|opf\n" "\n" @@ -507,16 +504,16 @@ msgid "" "file.\n" "If you specify an OPF file instead of an HTML file, the list of links is " "takes from\n" -"the element of the OPF file. \n" +"the element of the OPF file.\n" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/epub/from_html.py:486 +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/from_html.py:519 #: /home/kovid/work/calibre/src/calibre/ebooks/lit/writer.py:758 #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/writer.py:621 msgid "Output written to " msgstr "تم كتابة الخرج في " -#: /home/kovid/work/calibre/src/calibre/ebooks/epub/from_html.py:508 +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/from_html.py:541 #: /home/kovid/work/calibre/src/calibre/ebooks/html.py:1155 msgid "You must specify an input HTML file" msgstr "يجب أن تخصص ملف HTML لتدخيله" @@ -640,7 +637,7 @@ msgid "%prog [options] LITFILE" msgstr "" #: /home/kovid/work/calibre/src/calibre/ebooks/lit/reader.py:895 -#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:588 +#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:657 msgid "Output directory. Defaults to current directory." msgstr "دليل الخرج. الإفتراضي هو الدليل الحالي." @@ -655,7 +652,7 @@ msgid "Useful for debugging." msgstr "" #: /home/kovid/work/calibre/src/calibre/ebooks/lit/reader.py:912 -#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:612 +#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:681 msgid "OEB ebook created in" msgstr "تم إنشاء كتاب OEB في" @@ -698,7 +695,7 @@ msgid "Sort key for the author" msgstr "مفتاح الترتيب للمؤلف" #: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:89 -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:297 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:302 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata.py:58 #: /home/kovid/work/calibre/src/calibre/gui2/library.py:113 msgid "Publisher" @@ -1329,7 +1326,7 @@ msgstr "" "\n" #: /home/kovid/work/calibre/src/calibre/ebooks/lrf/meta.py:587 -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:43 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:42 msgid "Set the book title" msgstr "تحديد عنوان الكتاب" @@ -1346,7 +1343,7 @@ msgid "Set sort key for the author" msgstr "ضبط مفتاح الترتيب للمؤلف" #: /home/kovid/work/calibre/src/calibre/ebooks/lrf/meta.py:595 -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:47 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:46 msgid "The category this book belongs to. E.g.: History" msgstr "تصنيف الكتاب. مثلاً: تاريخ" @@ -1454,15 +1451,15 @@ msgid "" "%prog converts mybook.txt to mybook.lrf" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:45 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:44 msgid "Set the authors" msgstr "تحديد المؤلفين" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:49 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:48 msgid "Set the comment" msgstr "تحديد التعليق" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:295 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:300 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:69 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:70 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata.py:55 @@ -1472,7 +1469,7 @@ msgstr "تحديد التعليق" msgid "Title" msgstr "العنوان" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:296 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:301 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata.py:56 #: /home/kovid/work/calibre/src/calibre/gui2/library.py:109 #: /home/kovid/work/calibre/src/calibre/gui2/library.py:366 @@ -1480,11 +1477,11 @@ msgstr "العنوان" msgid "Author(s)" msgstr "المؤلف أو المؤلفون" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:298 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:303 msgid "Producer" msgstr "المنتج" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:299 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:304 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:71 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info_ui.py:64 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:489 @@ -1495,7 +1492,7 @@ msgstr "المنتج" msgid "Comments" msgstr "التعليقات" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:301 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:312 #: /home/kovid/work/calibre/src/calibre/gui2/library.py:114 #: /home/kovid/work/calibre/src/calibre/gui2/library.py:311 #: /home/kovid/work/calibre/src/calibre/gui2/library.py:915 @@ -1505,7 +1502,7 @@ msgstr "التعليقات" msgid "Tags" msgstr "الوسوم" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:303 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:314 #: /home/kovid/work/calibre/src/calibre/gui2/library.py:115 #: /home/kovid/work/calibre/src/calibre/gui2/library.py:327 #: /home/kovid/work/calibre/src/calibre/gui2/status.py:59 @@ -1513,11 +1510,11 @@ msgstr "الوسوم" msgid "Series" msgstr "السلسلة" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:304 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:315 msgid "Language" msgstr "اللغة" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:306 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:317 #: /home/kovid/work/calibre/src/calibre/gui2/library.py:914 msgid "Timestamp" msgstr "ختم التوقيت" @@ -1551,7 +1548,7 @@ msgid "Usage: imp-meta file.imp" msgstr "الاستخدام: imp-meta file.imp" #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/imp.py:54 -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/pdf.py:59 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/pdf.py:68 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/rb.py:60 msgid "No filename specified." msgstr "لم يتم تحديد اسم الملف." @@ -1634,11 +1631,11 @@ msgstr "تحديد اللغة" msgid "Set the ISBN" msgstr "تحديد الـISBN" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/opf2.py:1025 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/opf2.py:1023 msgid "Set the dc:language field" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/pdf.py:58 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/pdf.py:67 msgid "Usage: pdf-meta file.pdf" msgstr "الاستخدام: pdf-meta file.pdf" @@ -1650,11 +1647,11 @@ msgstr "الاستخدام: rb-meta file.rb" msgid "Creating Mobipocket file from EPUB..." msgstr "إنشاء ملف Mobipocket من EPUB..." -#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:586 +#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:655 msgid "%prog [options] myebook.mobi" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:610 +#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:679 msgid "Raw MOBI HTML saved in" msgstr "" @@ -1717,74 +1714,74 @@ msgstr "" msgid "The output directory. Defaults to the current directory." msgstr "دليل الخرج. الإفتراضي هو الدليل الحالي." -#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:826 +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:829 msgid "Cover" msgstr "الغلاف" -#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:827 +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:830 msgid "Title Page" msgstr "صقحة العنوان" -#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:828 +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:831 #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/transforms/htmltoc.py:18 #: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:47 #: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:160 msgid "Table of Contents" msgstr "المحتويات" -#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:829 +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:832 msgid "Index" msgstr "الفهرس" -#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:830 +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:833 msgid "Glossary" msgstr "المسرد" -#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:831 +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:834 msgid "Acknowledgements" msgstr "شكر وتقدير" -#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:832 +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:835 msgid "Bibliography" msgstr "ببليوغرافيا" -#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:833 +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:836 msgid "Colophon" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:834 +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:837 msgid "Copyright" msgstr "حقوق المؤلف" -#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:835 +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:838 msgid "Dedication" msgstr "الإهداء" -#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:836 +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:839 msgid "Epigraph" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:837 +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:840 msgid "Foreword" msgstr "افتتاحية" -#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:838 +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:841 msgid "List of Illustrations" msgstr "قائمة الرسوم" -#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:839 +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:842 msgid "List of Tables" msgstr "قائمة الجداول" -#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:840 +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:843 msgid "Notes" msgstr "الملاحظات" -#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:841 +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:844 msgid "Preface" msgstr "افتتاحية" -#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:842 +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:845 msgid "Main Text" msgstr "النصّ الرئيسي" @@ -1954,7 +1951,7 @@ msgid "Adding books to database..." msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/add.py:183 -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:751 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:753 msgid "Reading metadata..." msgstr "قراءة الميتاداتا..." @@ -1962,174 +1959,175 @@ msgstr "قراءة الميتاداتا..." msgid "Searching in" msgstr "يتم البحث في" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:85 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:93 msgid "Device no longer connected." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:136 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:144 msgid "Get device information" msgstr "احصل على معلومات الجهاز" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:146 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:154 msgid "Get list of books on device" msgstr "احصل على قائمة الكتب على الجهاز" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:155 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:163 msgid "Send metadata to device" msgstr "ارسل الميتاداتا إلى الجهاز" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:164 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:172 msgid "Upload %d books to device" msgstr "رفع %d كتاب إلى الجهاز" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:179 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:187 msgid "Delete books from device" msgstr "حذف كتب من الجهاز" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:194 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:202 msgid "Download books from device" msgstr "تنزيل الكتب من الجهاز" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:204 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:212 msgid "View book on device" msgstr "عرض كتاب على الجهاز" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:211 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:219 msgid "and delete from library" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:232 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:240 msgid "Set default send to device action" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:237 -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:244 -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:246 -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:248 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:245 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:252 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:254 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:256 msgid "Email to" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:262 -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:267 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:270 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:275 msgid "Send to main memory" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:264 -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:269 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:272 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:277 msgid "Send to storage card" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:272 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:280 msgid "Send specific format to main memory" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:274 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:282 msgid "Send specific format to storage card" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:402 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:410 msgid "No books" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:403 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:411 msgid "selected to send" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:408 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:416 msgid "Choose format to send to device" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:415 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:423 msgid "No device" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:416 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:424 msgid "Cannot send: No device is connected" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:420 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:428 msgid "No card" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:421 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:429 msgid "Cannot send: Device has no storage card" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:452 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:460 msgid "E-book:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:455 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:463 msgid "Attached, you will find the e-book" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:456 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:464 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:109 msgid "by" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:457 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:465 msgid "in the %s format." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:470 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:478 msgid "Sending email to" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:474 -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:620 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:482 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:628 msgid "No suitable formats" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:475 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:483 msgid "" "Could not email the following books as no suitable formats were " "found:
    %s
" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:494 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:502 msgid "Failed to email books" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:495 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:503 msgid "Failed to email the following books:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:499 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:507 msgid "Sent by email:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:526 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:534 msgid "News:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:527 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:535 msgid "Attached is the" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:538 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:546 msgid "Sent news to" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:574 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:582 msgid "Sending news to device." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:617 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:625 msgid "Sending books to device." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:621 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:629 msgid "" "Could not upload the following books to the device, as no suitable formats " -"were found:
    %s
" +"were found. Try changing the output format in the upper right corner next to " +"the red heart and re-converting.
    %s
" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:667 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:677 msgid "No space on device" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/device.py:668 +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:678 msgid "" "

Cannot upload books to device there is no more free space available " msgstr "" @@ -2165,7 +2163,7 @@ msgstr "حوار" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info_ui.py:63 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/choose_format_ui.py:41 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/confirm_delete_ui.py:49 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/password_ui.py:56 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/password_ui.py:57 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/progress_ui.py:49 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/progress_ui.py:50 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/warning_ui.py:53 @@ -2314,114 +2312,114 @@ msgstr "" msgid "new email address" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:442 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:451 msgid "Finish gmail setup" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:443 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:452 msgid "Dont forget to enter your gmail username and password" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:450 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:457 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:459 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:466 msgid "Bad configuration" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:451 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:460 msgid "You must set the From email address" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:458 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:467 msgid "You must set the username and password for the mail server." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:505 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:514 msgid "No valid plugin path" msgstr "مسار الملحق غير صالح" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:506 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:515 msgid "%s is not a valid plugin path" msgstr "%s ليس مسار لملحق صالح" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:509 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:518 msgid "Choose plugin" msgstr "إختيار الملحق" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:520 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:529 msgid "Plugin cannot be disabled" msgstr "لا يمكن تعطيل الملحق" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:521 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:530 msgid "The plugin: %s cannot be disabled" msgstr "الملحق: %s لا يمكن تعطيله" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:531 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:540 msgid "Plugin not customizable" msgstr "لا يمكن تخصيص الملحق" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:532 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:541 msgid "Plugin: %s does not need customization" msgstr "الملحق: %s لا يحتاج التخصيص" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:535 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:544 msgid "Customize %s" msgstr "تخصيص %s" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:545 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:554 msgid "Cannot remove builtin plugin" msgstr "لم يمكن حذف الملحق المضمن" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:546 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:555 msgid " cannot be removed. It is a builtin plugin. Try disabling it instead." msgstr " لا يمكن حذفه. هذا ملحق مضمن في البرنامج. حاول تعطيله بدلاً من حذفه." -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:568 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:577 msgid "Error log:" msgstr "سجل الأخطاء:" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:575 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:584 msgid "Access log:" msgstr "سجل النفاذ:" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:600 -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:461 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:609 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:463 msgid "Failed to start content server" msgstr "فشل في تشغيل خادم المحتوى" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:624 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:633 msgid "Select database location" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:641 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:650 msgid "Invalid size" msgstr "حجم غير صالح" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:642 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:651 msgid "The size %s is invalid. must be of the form widthxheight" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:682 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:687 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:691 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:696 msgid "Invalid database location" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:683 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:692 msgid "Invalid database location " msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:684 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:693 msgid "
Must be a directory." msgstr "
يجب أن يكون دليل." -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:688 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:697 msgid "Invalid database location.
Cannot write to " msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:702 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:711 msgid "Compacting..." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:703 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:712 msgid "Compacting database. This may take a while." msgstr "" @@ -2551,7 +2549,7 @@ msgid "Automatically send downloaded &news to ebook reader" msgstr "إرسال الأخبار& التي تم تنزيلها آلياً إلى قارئ الكتب الإلكترونية" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:564 -msgid "&Delete news from library when it is sent to reader" +msgid "&Delete news from library when it is automatically sent to reader" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:565 @@ -2653,7 +2651,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:590 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:607 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/password_ui.py:57 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/password_ui.py:58 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:173 msgid "&Username:" msgstr "&اسم المستخدم:" @@ -2664,7 +2662,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:592 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:608 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/password_ui.py:58 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/password_ui.py:59 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:174 msgid "&Password:" msgstr "&كلمة السرّ" @@ -2737,7 +2735,7 @@ msgid "" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:610 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/password_ui.py:59 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/password_ui.py:60 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:175 msgid "&Show password" msgstr "إظهار& كلمة السرّ" @@ -2889,26 +2887,26 @@ msgstr "إختار الغلاف لـ " #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub.py:113 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:174 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:81 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:107 msgid "Cannot read" msgstr "لا يمكن القراءة" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub.py:114 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:175 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:82 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:108 msgid "You do not have permission to read the file: " msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub.py:122 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub.py:129 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:183 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:90 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:116 msgid "Error reading file" msgstr "خطأ في قراءة الملف" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub.py:123 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:184 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:91 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:117 msgid "

There was an error reading from file:
" msgstr "" @@ -2918,7 +2916,7 @@ msgid " is not a valid picture" msgstr " ليست صورة صالحة" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub.py:242 -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:972 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:974 msgid "Cannot convert" msgstr "لا يمكن تحويله" @@ -3336,7 +3334,7 @@ msgid "Convert %s to LRF" msgstr "تحول %s إلى LRF" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:109 -#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:360 +#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:366 msgid "Set conversion defaults" msgstr "" @@ -3616,75 +3614,76 @@ msgstr "حذف الت&هيئة:" msgid "A&utomatically set author sort" msgstr "ضبط& ترتيب المؤلف آلياً" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:139 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:140 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:165 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:166 msgid "No format selected" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:150 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:176 msgid "Could not read metadata" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:151 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:177 msgid "Could not read metadata from %s format" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:159 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:165 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:185 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:191 msgid "Could not read cover" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:160 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:186 msgid "Could not read cover from %s format" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:166 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:192 msgid "The cover in the %s format is invalid" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:348 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:374 msgid "" -"

Enter your username and password for LibraryThing.com.
If you " -"do not have one, you can register " -"for free!.

" +"

Enter your username and password for LibraryThing.com. This is " +"optional. It will make fetching of covers faster and more " +"reliable.
If you do not have an account, you can register for free.

" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:370 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:401 msgid "Downloading cover..." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:373 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:384 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:390 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:413 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:418 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:424 msgid "Cannot fetch cover" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:374 -msgid "You must specify the ISBN identifier for this book." -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:385 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:391 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:414 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:425 msgid "Could not fetch cover.
" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:386 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:415 msgid "The download timed out." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:397 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:419 +msgid "Could not find cover for this book. Try specifying the ISBN first." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:431 msgid "Bad cover" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:398 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:432 msgid "The cover is not a valid picture" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:433 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:467 msgid "Cannot fetch metadata" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:434 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:468 msgid "You must specify at least one of ISBN, Title, Authors or Publisher" msgstr "" @@ -3746,7 +3745,7 @@ msgstr "" msgid "Change &password" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/password_ui.py:55 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/password_ui.py:56 msgid "Password needed" msgstr "" @@ -3758,96 +3757,96 @@ msgstr "" msgid "You" msgstr "أنت" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:123 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:138 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:124 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:139 msgid "Custom" msgstr "مخصّص" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:125 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:136 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:222 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:126 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:137 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:223 msgid "Scheduled" msgstr "تم جدولته" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:234 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:235 #: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:216 msgid "Search" msgstr "بحث" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:310 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:311 msgid "%d recipes" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:311 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:312 msgid "Monday" msgstr "الأثنين" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:311 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:312 msgid "Tuesday" msgstr "الثلاثاء" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:311 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:312 msgid "Wednesday" msgstr "الأربعاء" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:311 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:312 msgid "day" msgstr "اليوم" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:312 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:313 msgid "Friday" msgstr "الجمعة" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:312 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:313 msgid "Saturday" msgstr "السبت" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:312 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:313 msgid "Sunday" msgstr "الأحد" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:312 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:313 msgid "Thursday" msgstr "الخميس" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:345 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:346 msgid "Must set account information" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:346 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:347 msgid "This recipe requires a username and password" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:377 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:378 msgid "Created by: " msgstr "أنشأه: " -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:415 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:416 msgid "%d days, %d hours and %d minutes ago" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:417 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:418 msgid "Last downloaded" msgstr "آخر تنزيل" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:419 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:420 msgid "Last downloaded: never" msgstr "آخر تنزيل: لم ينزّل من قبل" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:445 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:446 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:162 msgid "Schedule news download" msgstr "جدولة تنزيل الأخبار" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:448 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:449 msgid "Add a custom news source" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:455 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:456 #: /home/kovid/work/calibre/src/calibre/gui2/tags.py:50 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:820 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:824 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:1139 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:838 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:842 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:1157 msgid "News" msgstr "الأخبار" @@ -4474,117 +4473,117 @@ msgstr "" msgid "&Donate to support calibre" msgstr "تبرع& لدعم كاليبر" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:111 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:113 msgid "&Restart" msgstr "إعادة تشغيل&" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:145 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:147 msgid "" "

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

" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:168 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:170 msgid "Edit metadata individually" msgstr "تحرير الميتاداتا فردياً" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:170 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:172 msgid "Edit metadata in bulk" msgstr "تحرير الميتاداتا جملةً" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:173 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:175 msgid "Add books from a single directory" msgstr "إضافة كتب من دليل واحد" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:174 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:176 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:177 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:179 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:198 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:200 #: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:360 msgid "Save to disk" msgstr "حفظ إلى القرص" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:199 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:201 msgid "Save to disk in a single directory" msgstr "حفظ إلى القرص في دليل واحد" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:200 -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1201 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:202 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1205 msgid "Save only %s format to disk" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:204 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:206 #: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:366 msgid "View" msgstr "عرض" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:205 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:207 msgid "View specific format" msgstr "عرض تهيئة معينة" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:231 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:233 msgid "Convert individually" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:232 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:234 msgid "Bulk convert" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:234 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:236 msgid "Set defaults for conversion" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:235 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:237 msgid "Set defaults for conversion of comics" msgstr "ضبط الإفتراضي في تحويل الرسومات" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:274 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:276 msgid "Similar books..." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:328 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:330 msgid "Bad database location" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:331 -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1364 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:333 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1368 msgid "Choose a location for your ebook library." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:504 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:506 msgid "Browse by covers" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:599 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:601 msgid "Device: " msgstr "الجهاز: " -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:601 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:603 msgid " detected." msgstr " تم كشفه." -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:624 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:626 msgid "Connected " msgstr "متصل " -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:636 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:638 msgid "Device database corrupted" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:637 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:639 msgid "" "\n" "

The database of books on the reader is corrupted. Try the " @@ -4600,257 +4599,255 @@ msgid "" " " msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:712 -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:765 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:714 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:767 msgid "Uploading books to device." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:720 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:722 msgid "Books" msgstr "كتب" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:721 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:723 msgid "EPUB Books" msgstr "كتب EPUB" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:722 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:724 msgid "LRF Books" msgstr "كتب LRF" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:723 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:725 msgid "HTML Books" msgstr "كتب HTML" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:724 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:726 msgid "LIT Books" msgstr "كتب LIT" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:725 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:727 msgid "MOBI Books" msgstr "كتب MOBI" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:726 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:728 msgid "Text books" msgstr "كتب نصّية" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:727 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:729 msgid "PDF Books" msgstr "كتب PDF" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:728 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:730 msgid "Comics" msgstr "الرسومات" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:729 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:731 msgid "Archives" msgstr "أرشيفات" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:750 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:752 msgid "Adding books..." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:786 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:788 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:799 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:801 msgid "Deleting books from device." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:832 -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:859 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:834 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:861 msgid "Cannot edit metadata" msgstr "لا يمكن تحرير الميتاداتا" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:833 -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:860 -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:883 -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:973 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:835 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:862 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:885 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:975 msgid "No books selected" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:882 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:884 msgid "Cannot save to disk" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:887 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:889 msgid "Saving to disk..." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:892 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:894 msgid "Saved" msgstr "تم الحفظ" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:899 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:901 msgid "Choose destination directory" msgstr "إختيار دليل الوجهة" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:914 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:916 msgid "" "

Could not save the following books to disk, because the %s format is not " "available for them: