mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-07 10:14:46 -04:00
Sync to trunk.
This commit is contained in:
commit
381fa159f4
39
recipes/automatiseringgids.recipe
Normal file
39
recipes/automatiseringgids.recipe
Normal file
@ -0,0 +1,39 @@
|
||||
import re
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class autogids(BasicNewsRecipe):
|
||||
title = u'Automatiseringgids IT'
|
||||
oldest_article = 7
|
||||
__author__ = 'DrMerry'
|
||||
description = 'IT-nieuws van Automatiseringgids'
|
||||
language = 'nl'
|
||||
publisher = 'AutomatiseringGids'
|
||||
category = 'Nieuws, IT, Nederlandstalig'
|
||||
simultaneous_downloads = 5
|
||||
#delay = 1
|
||||
timefmt = ' [%A, %d %B, %Y]'
|
||||
#timefmt = ''
|
||||
no_stylesheets = True
|
||||
remove_javascript = True
|
||||
remove_empty_feeds = True
|
||||
publication_type = 'newspaper'
|
||||
encoding = 'utf-8'
|
||||
cover_url = 'http://www.automatiseringgids.nl/siteimg/header_logo.gif'
|
||||
keep_only_tags = [dict(id=['content'])]
|
||||
extra_css = '.artikelheader {font-size:0.8em; color: #666;} .artikelintro {font-weight:bold} div.imgArticle {float: right; margin: 0 0em 1em 1em; display: block; position: relative; } \
|
||||
h2 { margin: 0 0 0.5em; min-height: 30px; font-size: 1.5em; letter-spacing: -0.2px; margin: 0 0 0.5em; color: black; font-weight: bold; line-height: 1.2em; padding: 4px 3px 0; }'
|
||||
|
||||
|
||||
|
||||
remove_tags = [dict(name='div', attrs={'id':['loginbox','reactiecollapsible','reactiebox']}),
|
||||
dict(name='div', attrs={'class':['column_a','column_c','bannerfullsize','reactieheader','reactiecollapsible','formulier','artikel_headeroptions']}),
|
||||
dict(name='ul', attrs={'class':['highlightlist']}),
|
||||
dict(name='input', attrs={'type':['button']}),
|
||||
dict(name='div', attrs={'style':['display:block; width:428px; height:30px; float:left;']}),
|
||||
]
|
||||
preprocess_regexps = [
|
||||
(re.compile(r'(<h3>Reacties</h3>|<h2>Zie ook:</h2>|<div style=".*</div>|<a[^>]*>|</a>)', re.DOTALL|re.IGNORECASE),
|
||||
lambda match: ''),
|
||||
]
|
||||
|
||||
feeds = [(u'Actueel', u'http://www.automatiseringgids.nl/rss.aspx')]
|
@ -1,9 +1,9 @@
|
||||
#!/usr/bin/env python
|
||||
__license__ = 'GPL v3'
|
||||
__author__ = 'DrMerry Based on v1.01 by Lorenzo Vigentini'
|
||||
__copyright__ = 'For version 1.02: DrMerry'
|
||||
__version__ = 'v1.02'
|
||||
__date__ = '08, July 2011'
|
||||
__copyright__ = 'For version 1.02, 1.03: DrMerry'
|
||||
__version__ = 'v1.03'
|
||||
__date__ = '11, July 2011'
|
||||
__description__ = 'Computeractive publishes new downloads, reviews, news stories, step-by-step guides and answers to PC problems every day. Original version (c): 2009, Lorenzo Vigentini <l.vigentini at gmail.com>'
|
||||
|
||||
'''
|
||||
@ -11,9 +11,10 @@ http://www.computeractive.co.uk/
|
||||
'''
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
import re
|
||||
|
||||
class computeractive(BasicNewsRecipe):
|
||||
__author__ = 'Lorenzo Vigentini'
|
||||
__author__ = 'DrMerry'
|
||||
description = 'Computeractive publishes new downloads, reviews, news stories, step-by-step guides and answers to PC problems every day.'
|
||||
cover_url = 'http://images.pcworld.com/images/common/header/header-logo.gif'
|
||||
|
||||
@ -31,6 +32,8 @@ class computeractive(BasicNewsRecipe):
|
||||
|
||||
remove_javascript = True
|
||||
no_stylesheets = True
|
||||
remove_empty_feeds = True
|
||||
remove_tags_after = dict(name='div', attrs={'class':'article_tags_block'})
|
||||
|
||||
keep_only_tags = [
|
||||
dict(name='div', attrs={'id':'container_left'})
|
||||
@ -42,13 +45,14 @@ class computeractive(BasicNewsRecipe):
|
||||
dict(name='a', attrs={'class':'largerImage'})
|
||||
]
|
||||
|
||||
preprocess_regexps = [
|
||||
(re.compile(r'(<a [^>]*>|</a>)', re.DOTALL|re.IGNORECASE),
|
||||
lambda match: ''),
|
||||
]
|
||||
|
||||
feeds = [
|
||||
(u'General content', u'http://feeds.computeractive.co.uk/rss/latest/computeractive/all'),
|
||||
(u'News', u'http://feeds.computeractive.co.uk/rss/latest/computeractive/news'),
|
||||
(u'Downloads', u'http://feeds.computeractive.co.uk/rss/latest/computeractive/downloads'),
|
||||
(u'Hardware', u'http://feeds.computeractive.co.uk/rss/latest/computeractive/hardware'),
|
||||
(u'Software', u'http://feeds.computeractive.co.uk/rss/latest/computeractive/software'),
|
||||
(u'Competitions', u'http://www.v3.co.uk/feeds/rss20/personal-technology/competitions')
|
||||
]
|
||||
|
||||
|
||||
|
@ -9,7 +9,7 @@ engadget.com
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class Engadget(BasicNewsRecipe):
|
||||
title = u'Engadget_Full'
|
||||
title = u'Engadget'
|
||||
__author__ = 'Starson17'
|
||||
__version__ = 'v1.00'
|
||||
__date__ = '02, July 2011'
|
||||
|
35
recipes/geek_poke.recipe
Normal file
35
recipes/geek_poke.recipe
Normal file
@ -0,0 +1,35 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
import re
|
||||
|
||||
class AdvancedUserRecipe1307556816(BasicNewsRecipe):
|
||||
title = u'Geek and Poke'
|
||||
__author__ = u'DrMerry'
|
||||
description = u'Geek and Poke Cartoons'
|
||||
oldest_article = 31
|
||||
max_articles_per_feed = 100
|
||||
language = u'en'
|
||||
simultaneous_downloads = 5
|
||||
#delay = 1
|
||||
timefmt = ' [%A, %d %B, %Y]'
|
||||
summary_length = -1
|
||||
no_stylesheets = True
|
||||
cover_url = 'http://geekandpoke.typepad.com/aboutcoders.jpeg'
|
||||
remove_javascript = True
|
||||
remove_empty_feeds = True
|
||||
publication_type = 'blog'
|
||||
|
||||
preprocess_regexps = [ (re.compile(r'(<p> </p>|<iframe.*</iframe>|<a[^>]*>Tweet</a>|<a[^>]*>|</a>)', re.DOTALL|re.IGNORECASE),lambda match: ''),
|
||||
(re.compile(r'( | )', re.DOTALL|re.IGNORECASE),lambda match: ' '),
|
||||
(re.compile(r'<br( /)?>(<br( /)?>)+', re.DOTALL|re.IGNORECASE),lambda match: '<br>')
|
||||
]
|
||||
|
||||
extra_css = 'body, h3, p, h2, h1, div, span{margin:0px} h2.date-header {font-size: 0.7em; color:#eee;} h3.entry-header{font-size: 1.0em} div.entry-body{font-size: 0.9em}'
|
||||
|
||||
|
||||
remove_tags_before = dict(name='h2', attrs={'class':'date-header'})
|
||||
remove_tags_after = dict(name='div', attrs={'class':'entry-body'})
|
||||
|
||||
|
||||
feeds = [(u'Geek and Poke', u'http://feeds.feedburner.com/GeekAndPoke?format=xml')]
|
BIN
recipes/icons/pecat.png
Normal file
BIN
recipes/icons/pecat.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 383 B |
@ -16,16 +16,14 @@ class i09(BasicNewsRecipe):
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets = True
|
||||
encoding = 'utf-8'
|
||||
use_embedded_content = False
|
||||
use_embedded_content = True
|
||||
language = 'en'
|
||||
masthead_url = 'http://cache.gawkerassets.com/assets/io9.com/img/logo.png'
|
||||
extra_css = '''
|
||||
body{font-family: "Lucida Grande",Helvetica,Arial,sans-serif}
|
||||
img{margin-bottom: 1em}
|
||||
h1{font-family :Arial,Helvetica,sans-serif; font-size:large}
|
||||
h2{font-family :Arial,Helvetica,sans-serif; font-size:x-small}
|
||||
'''
|
||||
|
||||
conversion_options = {
|
||||
'comment' : description
|
||||
, 'tags' : category
|
||||
@ -33,13 +31,11 @@ class i09(BasicNewsRecipe):
|
||||
, 'language' : language
|
||||
}
|
||||
|
||||
remove_attributes = ['width','height']
|
||||
keep_only_tags = [dict(attrs={'class':'content permalink'})]
|
||||
remove_tags_before = dict(name='h1')
|
||||
remove_tags = [dict(attrs={'class':'contactinfo'})]
|
||||
remove_tags_after = dict(attrs={'class':'contactinfo'})
|
||||
feeds = [(u'Articles', u'http://feeds.gawker.com/io9/vip?format=xml')]
|
||||
|
||||
feeds = [(u'Articles', u'http://feeds.gawker.com/io9/full')]
|
||||
remove_tags = [
|
||||
{'class': 'feedflare'},
|
||||
]
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
return self.adeify_images(soup)
|
||||
|
72
recipes/pecat.recipe
Normal file
72
recipes/pecat.recipe
Normal file
@ -0,0 +1,72 @@
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2011, Darko Miletic <darko.miletic at gmail.com>'
|
||||
'''
|
||||
www.pecat.co.rs
|
||||
'''
|
||||
|
||||
import re
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class Pecat_rs(BasicNewsRecipe):
|
||||
title = 'Pecat'
|
||||
__author__ = 'Darko Miletic'
|
||||
description = 'Internet portal slobodne Srbije'
|
||||
oldest_article = 15
|
||||
max_articles_per_feed = 100
|
||||
language = 'sr'
|
||||
encoding = 'utf-8'
|
||||
no_stylesheets = True
|
||||
use_embedded_content = True
|
||||
masthead_url = 'http://www.pecat.co.rs/wp-content/themes/zenko-v1/images/logo.jpg'
|
||||
publication_type = 'magazine'
|
||||
extra_css = """
|
||||
@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)}
|
||||
@font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)}
|
||||
body{font-family: Arial,Helvetica,sans1,sans-serif}
|
||||
img{display: block; margin-bottom: 1em; margin-top: 1em}
|
||||
p{display: block; margin-bottom: 1em; margin-top: 1em}
|
||||
"""
|
||||
|
||||
conversion_options = {
|
||||
'comment' : description
|
||||
, 'tags' : 'politika, Srbija'
|
||||
, 'publisher': 'Pecat'
|
||||
, 'language' : language
|
||||
}
|
||||
|
||||
preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')]
|
||||
|
||||
feeds = [(u'Clanci', u'http://www.pecat.co.rs/feed/')]
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
for item in soup.findAll(style=True):
|
||||
del item['style']
|
||||
for item in soup.findAll('a'):
|
||||
limg = item.find('img')
|
||||
if item.string is not None:
|
||||
str = item.string
|
||||
item.replaceWith(str)
|
||||
else:
|
||||
if limg:
|
||||
limg.extract()
|
||||
item.replaceWith(limg)
|
||||
else:
|
||||
str = self.tag_to_string(item)
|
||||
item.replaceWith(str)
|
||||
for item in soup.findAll('img'):
|
||||
dad = item.findParent('p')
|
||||
if dad:
|
||||
mydad = dad.parent
|
||||
myIndex = mydad.contents.index(dad)
|
||||
item.extract()
|
||||
mydad.insert(myIndex,item)
|
||||
for item in soup.findAll('strong'):
|
||||
dad = item.findParent('p')
|
||||
if dad:
|
||||
mydad = dad.parent
|
||||
myIndex = mydad.contents.index(dad)
|
||||
item.extract()
|
||||
item.name='h4'
|
||||
mydad.insert(myIndex,item)
|
||||
return soup
|
@ -1,5 +1,5 @@
|
||||
" Project wide builtins
|
||||
let g:pyflakes_builtins += ["dynamic_property", "__", "P", "I", "lopen", "icu_lower", "icu_upper", "icu_title"]
|
||||
let g:pyflakes_builtins += ["dynamic_property", "__", "P", "I", "lopen", "icu_lower", "icu_upper", "icu_title", "ngettext"]
|
||||
|
||||
python << EOFPY
|
||||
import os
|
||||
|
@ -64,7 +64,7 @@ class Check(Command):
|
||||
description = 'Check for errors in the calibre source code'
|
||||
|
||||
BUILTINS = ['_', '__', 'dynamic_property', 'I', 'P', 'lopen', 'icu_lower',
|
||||
'icu_upper', 'icu_title']
|
||||
'icu_upper', 'icu_title', 'ngettext']
|
||||
CACHE = '.check-cache.pickle'
|
||||
|
||||
def get_files(self, cache):
|
||||
|
@ -1,646 +0,0 @@
|
||||
#! /usr/bin/env python
|
||||
# Originally written by Barry Warsaw <barry@zope.com>
|
||||
#
|
||||
# Minimally patched to make it even more xgettext compatible
|
||||
# by Peter Funk <pf@artcom-gmbh.de>
|
||||
#
|
||||
# 2002-11-22 Jrgen Hermann <jh@web.de>
|
||||
# Added checks that _() only contains string literals, and
|
||||
# command line args are resolved to module lists, i.e. you
|
||||
# can now pass a filename, a module or package name, or a
|
||||
# directory (including globbing chars, important for Win32).
|
||||
# Made docstring fit in 80 chars wide displays using pydoc.
|
||||
#
|
||||
|
||||
__doc__ = """pygettext -- Python equivalent of xgettext(1)
|
||||
|
||||
Many systems (Solaris, Linux, Gnu) provide extensive tools that ease the
|
||||
internationalization of C programs. Most of these tools are independent of
|
||||
the programming language and can be used from within Python programs.
|
||||
Martin von Loewis' work[1] helps considerably in this regard.
|
||||
|
||||
There's one problem though; xgettext is the program that scans source code
|
||||
looking for message strings, but it groks only C (or C++). Python
|
||||
introduces a few wrinkles, such as dual quoting characters, triple quoted
|
||||
strings, and raw strings. xgettext understands none of this.
|
||||
|
||||
Enter pygettext, which uses Python's standard tokenize module to scan
|
||||
Python source code, generating .pot files identical to what GNU xgettext[2]
|
||||
generates for C and C++ code. From there, the standard GNU tools can be
|
||||
used.
|
||||
|
||||
A word about marking Python strings as candidates for translation. GNU
|
||||
xgettext recognizes the following keywords: gettext, dgettext, dcgettext,
|
||||
and gettext_noop. But those can be a lot of text to include all over your
|
||||
code. C and C++ have a trick: they use the C preprocessor. Most
|
||||
internationalized C source includes a #define for gettext() to _() so that
|
||||
what has to be written in the source is much less. Thus these are both
|
||||
translatable strings:
|
||||
|
||||
gettext("Translatable String")
|
||||
_("Translatable String")
|
||||
|
||||
Python of course has no preprocessor so this doesn't work so well. Thus,
|
||||
pygettext searches only for _() by default, but see the -k/--keyword flag
|
||||
below for how to augment this.
|
||||
|
||||
[1] http://www.python.org/workshops/1997-10/proceedings/loewis.html
|
||||
[2] http://www.gnu.org/software/gettext/gettext.html
|
||||
|
||||
NOTE: pygettext attempts to be option and feature compatible with GNU
|
||||
xgettext where ever possible. However some options are still missing or are
|
||||
not fully implemented. Also, xgettext's use of command line switches with
|
||||
option arguments is broken, and in these cases, pygettext just defines
|
||||
additional switches.
|
||||
|
||||
Usage: pygettext [options] inputfile ...
|
||||
|
||||
Options:
|
||||
|
||||
-a
|
||||
--extract-all
|
||||
Extract all strings.
|
||||
|
||||
-d name
|
||||
--default-domain=name
|
||||
Rename the default output file from messages.pot to name.pot.
|
||||
|
||||
-E
|
||||
--escape
|
||||
Replace non-ASCII characters with octal escape sequences.
|
||||
|
||||
-D
|
||||
--docstrings
|
||||
Extract module, class, method, and function docstrings. These do
|
||||
not need to be wrapped in _() markers, and in fact cannot be for
|
||||
Python to consider them docstrings. (See also the -X option).
|
||||
|
||||
-h
|
||||
--help
|
||||
Print this help message and exit.
|
||||
|
||||
-k word
|
||||
--keyword=word
|
||||
Keywords to look for in addition to the default set, which are:
|
||||
%(DEFAULTKEYWORDS)s
|
||||
|
||||
You can have multiple -k flags on the command line.
|
||||
|
||||
-K
|
||||
--no-default-keywords
|
||||
Disable the default set of keywords (see above). Any keywords
|
||||
explicitly added with the -k/--keyword option are still recognized.
|
||||
|
||||
--no-location
|
||||
Do not write filename/lineno location comments.
|
||||
|
||||
-n
|
||||
--add-location
|
||||
Write filename/lineno location comments indicating where each
|
||||
extracted string is found in the source. These lines appear before
|
||||
each msgid. The style of comments is controlled by the -S/--style
|
||||
option. This is the default.
|
||||
|
||||
-o filename
|
||||
--output=filename
|
||||
Rename the default output file from messages.pot to filename. If
|
||||
filename is `-' then the output is sent to standard out.
|
||||
|
||||
-p dir
|
||||
--output-dir=dir
|
||||
Output files will be placed in directory dir.
|
||||
|
||||
-S stylename
|
||||
--style stylename
|
||||
Specify which style to use for location comments. Two styles are
|
||||
supported:
|
||||
|
||||
Solaris # File: filename, line: line-number
|
||||
GNU #: filename:line
|
||||
|
||||
The style name is case insensitive. GNU style is the default.
|
||||
|
||||
-v
|
||||
--verbose
|
||||
Print the names of the files being processed.
|
||||
|
||||
-V
|
||||
--version
|
||||
Print the version of pygettext and exit.
|
||||
|
||||
-w columns
|
||||
--width=columns
|
||||
Set width of output to columns.
|
||||
|
||||
-x filename
|
||||
--exclude-file=filename
|
||||
Specify a file that contains a list of strings that are not be
|
||||
extracted from the input files. Each string to be excluded must
|
||||
appear on a line by itself in the file.
|
||||
|
||||
-X filename
|
||||
--no-docstrings=filename
|
||||
Specify a file that contains a list of files (one per line) that
|
||||
should not have their docstrings extracted. This is only useful in
|
||||
conjunction with the -D option above.
|
||||
|
||||
If `inputfile' is -, standard input is read.
|
||||
"""
|
||||
|
||||
import os
|
||||
import imp
|
||||
import sys
|
||||
import glob
|
||||
import time
|
||||
import getopt
|
||||
import token
|
||||
import tokenize
|
||||
import operator
|
||||
|
||||
__version__ = '1.5'
|
||||
|
||||
default_keywords = ['_']
|
||||
DEFAULTKEYWORDS = ', '.join(default_keywords)
|
||||
|
||||
EMPTYSTRING = ''
|
||||
|
||||
from setup import __appname__, __version__ as version
|
||||
|
||||
# The normal pot-file header. msgmerge and Emacs's po-mode work better if it's
|
||||
# there.
|
||||
pot_header = '''\
|
||||
# Translation template file..
|
||||
# Copyright (C) %(year)s Kovid Goyal
|
||||
# Kovid Goyal <kovid@kovidgoyal.net>, %(year)s.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: %(appname)s %(version)s\\n"
|
||||
"POT-Creation-Date: %%(time)s\\n"
|
||||
"PO-Revision-Date: %%(time)s\\n"
|
||||
"Last-Translator: Automatically generated\\n"
|
||||
"Language-Team: LANGUAGE\\n"
|
||||
"MIME-Version: 1.0\\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\\n"
|
||||
"Content-Transfer-Encoding: 8bit\\n"
|
||||
"Generated-By: pygettext.py %%(version)s\\n"
|
||||
|
||||
'''%dict(appname=__appname__, version=version, year=time.strftime('%Y'))
|
||||
|
||||
def usage(code, msg=''):
|
||||
print >> sys.stderr, __doc__ % globals()
|
||||
if msg:
|
||||
print >> sys.stderr, msg
|
||||
sys.exit(code)
|
||||
|
||||
|
||||
|
||||
escapes = []
|
||||
|
||||
def make_escapes(pass_iso8859):
|
||||
global escapes
|
||||
if pass_iso8859:
|
||||
# Allow iso-8859 characters to pass through so that e.g. 'msgid
|
||||
# would result not result in 'msgid "H\366he"'. Otherwise we
|
||||
# escape any character outside the 32..126 range.
|
||||
mod = 128
|
||||
else:
|
||||
mod = 256
|
||||
for i in range(256):
|
||||
if 32 <= (i % mod) <= 126:
|
||||
escapes.append(chr(i))
|
||||
else:
|
||||
escapes.append("\\%03o" % i)
|
||||
escapes[ord('\\')] = '\\\\'
|
||||
escapes[ord('\t')] = '\\t'
|
||||
escapes[ord('\r')] = '\\r'
|
||||
escapes[ord('\n')] = '\\n'
|
||||
escapes[ord('\"')] = '\\"'
|
||||
|
||||
|
||||
def escape(s):
|
||||
global escapes
|
||||
s = list(s)
|
||||
for i in range(len(s)):
|
||||
s[i] = escapes[ord(s[i])]
|
||||
return EMPTYSTRING.join(s)
|
||||
|
||||
|
||||
def safe_eval(s):
|
||||
# unwrap quotes, safely
|
||||
return eval(s, {'__builtins__':{}}, {})
|
||||
|
||||
|
||||
def normalize(s):
|
||||
# This converts the various Python string types into a format that is
|
||||
# appropriate for .po files, namely much closer to C style.
|
||||
lines = s.split('\n')
|
||||
if len(lines) == 1:
|
||||
s = '"' + escape(s) + '"'
|
||||
else:
|
||||
if not lines[-1]:
|
||||
del lines[-1]
|
||||
lines[-1] = lines[-1] + '\n'
|
||||
for i in range(len(lines)):
|
||||
lines[i] = escape(lines[i])
|
||||
lineterm = '\\n"\n"'
|
||||
s = '""\n"' + lineterm.join(lines) + '"'
|
||||
return s
|
||||
|
||||
|
||||
def containsAny(str, set):
|
||||
"""Check whether 'str' contains ANY of the chars in 'set'"""
|
||||
return 1 in [c in str for c in set]
|
||||
|
||||
|
||||
def _visit_pyfiles(list, dirname, names):
|
||||
"""Helper for getFilesForName()."""
|
||||
# get extension for python source files
|
||||
if not globals().has_key('_py_ext'):
|
||||
global _py_ext
|
||||
_py_ext = [triple[0] for triple in imp.get_suffixes()
|
||||
if triple[2] == imp.PY_SOURCE][0]
|
||||
|
||||
# don't recurse into CVS directories
|
||||
if 'CVS' in names:
|
||||
names.remove('CVS')
|
||||
|
||||
# add all *.py files to list
|
||||
list.extend(
|
||||
[os.path.join(dirname, file) for file in names
|
||||
if os.path.splitext(file)[1] == _py_ext]
|
||||
)
|
||||
|
||||
|
||||
def _get_modpkg_path(dotted_name, pathlist=None):
|
||||
"""Get the filesystem path for a module or a package.
|
||||
|
||||
Return the file system path to a file for a module, and to a directory for
|
||||
a package. Return None if the name is not found, or is a builtin or
|
||||
extension module.
|
||||
"""
|
||||
# split off top-most name
|
||||
parts = dotted_name.split('.', 1)
|
||||
|
||||
if len(parts) > 1:
|
||||
# we have a dotted path, import top-level package
|
||||
try:
|
||||
file, pathname, description = imp.find_module(parts[0], pathlist)
|
||||
if file: file.close()
|
||||
except ImportError:
|
||||
return None
|
||||
|
||||
# check if it's indeed a package
|
||||
if description[2] == imp.PKG_DIRECTORY:
|
||||
# recursively handle the remaining name parts
|
||||
pathname = _get_modpkg_path(parts[1], [pathname])
|
||||
else:
|
||||
pathname = None
|
||||
else:
|
||||
# plain name
|
||||
try:
|
||||
file, pathname, description = imp.find_module(
|
||||
dotted_name, pathlist)
|
||||
if file:
|
||||
file.close()
|
||||
if description[2] not in [imp.PY_SOURCE, imp.PKG_DIRECTORY]:
|
||||
pathname = None
|
||||
except ImportError:
|
||||
pathname = None
|
||||
|
||||
return pathname
|
||||
|
||||
|
||||
def getFilesForName(name):
|
||||
"""Get a list of module files for a filename, a module or package name,
|
||||
or a directory.
|
||||
"""
|
||||
if not os.path.exists(name):
|
||||
# check for glob chars
|
||||
if containsAny(name, "*?[]"):
|
||||
files = glob.glob(name)
|
||||
list = []
|
||||
for file in files:
|
||||
list.extend(getFilesForName(file))
|
||||
return list
|
||||
|
||||
# try to find module or package
|
||||
name = _get_modpkg_path(name)
|
||||
if not name:
|
||||
return []
|
||||
|
||||
if os.path.isdir(name):
|
||||
# find all python files in directory
|
||||
list = []
|
||||
os.path.walk(name, _visit_pyfiles, list)
|
||||
return list
|
||||
elif os.path.exists(name):
|
||||
# a single file
|
||||
return [name]
|
||||
|
||||
return []
|
||||
|
||||
|
||||
class TokenEater:
|
||||
def __init__(self, options):
|
||||
self.__options = options
|
||||
self.__messages = {}
|
||||
self.__state = self.__waiting
|
||||
self.__data = []
|
||||
self.__lineno = -1
|
||||
self.__freshmodule = 1
|
||||
self.__curfile = None
|
||||
|
||||
def __call__(self, ttype, tstring, stup, etup, line):
|
||||
# dispatch
|
||||
## import token
|
||||
## print >> sys.stderr, 'ttype:', token.tok_name[ttype], \
|
||||
## 'tstring:', tstring
|
||||
self.__state(ttype, tstring, stup[0])
|
||||
|
||||
def __waiting(self, ttype, tstring, lineno):
|
||||
opts = self.__options
|
||||
# Do docstring extractions, if enabled
|
||||
if opts.docstrings and not opts.nodocstrings.get(self.__curfile):
|
||||
# module docstring?
|
||||
if self.__freshmodule:
|
||||
if ttype == tokenize.STRING:
|
||||
self.__addentry(safe_eval(tstring), lineno, isdocstring=1)
|
||||
self.__freshmodule = 0
|
||||
elif ttype not in (tokenize.COMMENT, tokenize.NL):
|
||||
self.__freshmodule = 0
|
||||
return
|
||||
# class docstring?
|
||||
if ttype == tokenize.NAME and tstring in ('class', 'def'):
|
||||
self.__state = self.__suiteseen
|
||||
return
|
||||
if ttype == tokenize.NAME and tstring in opts.keywords:
|
||||
self.__state = self.__keywordseen
|
||||
|
||||
def __suiteseen(self, ttype, tstring, lineno):
|
||||
# ignore anything until we see the colon
|
||||
if ttype == tokenize.OP and tstring == ':':
|
||||
self.__state = self.__suitedocstring
|
||||
|
||||
def __suitedocstring(self, ttype, tstring, lineno):
|
||||
# ignore any intervening noise
|
||||
if ttype == tokenize.STRING:
|
||||
self.__addentry(safe_eval(tstring), lineno, isdocstring=1)
|
||||
self.__state = self.__waiting
|
||||
elif ttype not in (tokenize.NEWLINE, tokenize.INDENT,
|
||||
tokenize.COMMENT):
|
||||
# there was no class docstring
|
||||
self.__state = self.__waiting
|
||||
|
||||
def __keywordseen(self, ttype, tstring, lineno):
|
||||
if ttype == tokenize.OP and tstring == '(':
|
||||
self.__data = []
|
||||
self.__lineno = lineno
|
||||
self.__state = self.__openseen
|
||||
else:
|
||||
self.__state = self.__waiting
|
||||
|
||||
def __openseen(self, ttype, tstring, lineno):
|
||||
if ttype == tokenize.OP and tstring == ')':
|
||||
# We've seen the last of the translatable strings. Record the
|
||||
# line number of the first line of the strings and update the list
|
||||
# of messages seen. Reset state for the next batch. If there
|
||||
# were no strings inside _(), then just ignore this entry.
|
||||
if self.__data:
|
||||
self.__addentry(EMPTYSTRING.join(self.__data))
|
||||
self.__state = self.__waiting
|
||||
elif ttype == tokenize.STRING:
|
||||
self.__data.append(safe_eval(tstring))
|
||||
elif ttype not in [tokenize.COMMENT, token.INDENT, token.DEDENT,
|
||||
token.NEWLINE, tokenize.NL]:
|
||||
# warn if we see anything else than STRING or whitespace
|
||||
print >> sys.stderr, \
|
||||
'*** %(file)s:%(lineno)s: Seen unexpected token "%(token)s"'\
|
||||
% {
|
||||
'token': tstring,
|
||||
'file': self.__curfile,
|
||||
'lineno': self.__lineno
|
||||
}
|
||||
self.__state = self.__waiting
|
||||
|
||||
def __addentry(self, msg, lineno=None, isdocstring=0):
|
||||
if lineno is None:
|
||||
lineno = self.__lineno
|
||||
if not msg in self.__options.toexclude:
|
||||
entry = (self.__curfile, lineno)
|
||||
self.__messages.setdefault(msg, {})[entry] = isdocstring
|
||||
|
||||
def set_filename(self, filename):
|
||||
self.__curfile = filename
|
||||
self.__freshmodule = 1
|
||||
|
||||
def write(self, fp):
|
||||
options = self.__options
|
||||
timestamp = time.strftime('%Y-%m-%d %H:%M+%Z')
|
||||
# The time stamp in the header doesn't have the same format as that
|
||||
# generated by xgettext...
|
||||
print >> fp, pot_header % {'time': timestamp, 'version': __version__}
|
||||
# Sort the entries. First sort each particular entry's keys, then
|
||||
# sort all the entries by their first item.
|
||||
reverse = {}
|
||||
for k, v in self.__messages.items():
|
||||
keys = v.keys()
|
||||
keys.sort()
|
||||
reverse.setdefault(tuple(keys), []).append((k, v))
|
||||
rkeys = reverse.keys()
|
||||
rkeys.sort()
|
||||
for rkey in rkeys:
|
||||
rentries = reverse[rkey]
|
||||
rentries.sort()
|
||||
for k, v in rentries:
|
||||
isdocstring = 0
|
||||
# If the entry was gleaned out of a docstring, then add a
|
||||
# comment stating so. This is to aid translators who may wish
|
||||
# to skip translating some unimportant docstrings.
|
||||
if reduce(operator.__add__, v.values()):
|
||||
isdocstring = 1
|
||||
# k is the message string, v is a dictionary-set of (filename,
|
||||
# lineno) tuples. We want to sort the entries in v first by
|
||||
# file name and then by line number.
|
||||
v = v.keys()
|
||||
v.sort()
|
||||
if not options.writelocations:
|
||||
pass
|
||||
# location comments are different b/w Solaris and GNU:
|
||||
elif options.locationstyle == options.SOLARIS:
|
||||
for filename, lineno in v:
|
||||
d = {'filename': filename, 'lineno': lineno}
|
||||
print >>fp, \
|
||||
'# File: %(filename)s, line: %(lineno)d' % d
|
||||
elif options.locationstyle == options.GNU:
|
||||
# fit as many locations on one line, as long as the
|
||||
# resulting line length doesn't exceeds 'options.width'
|
||||
locline = '#:'
|
||||
for filename, lineno in v:
|
||||
d = {'filename': filename, 'lineno': lineno}
|
||||
s = ' %(filename)s:%(lineno)d' % d
|
||||
if len(locline) + len(s) <= options.width:
|
||||
locline = locline + s
|
||||
else:
|
||||
print >> fp, locline
|
||||
locline = "#:" + s
|
||||
if len(locline) > 2:
|
||||
print >> fp, locline
|
||||
if isdocstring:
|
||||
print >> fp, '#, docstring'
|
||||
print >> fp, 'msgid', normalize(k)
|
||||
print >> fp, 'msgstr ""\n'
|
||||
|
||||
|
||||
|
||||
def main(outfile, args=sys.argv[1:]):
|
||||
global default_keywords
|
||||
try:
|
||||
opts, args = getopt.getopt(
|
||||
args,
|
||||
'ad:DEhk:Kno:p:S:Vvw:x:X:',
|
||||
['extract-all', 'default-domain=', 'escape', 'help',
|
||||
'keyword=', 'no-default-keywords',
|
||||
'add-location', 'no-location', 'output=', 'output-dir=',
|
||||
'style=', 'verbose', 'version', 'width=', 'exclude-file=',
|
||||
'docstrings', 'no-docstrings',
|
||||
])
|
||||
except getopt.error, msg:
|
||||
usage(1, msg)
|
||||
|
||||
# for holding option values
|
||||
class Options:
|
||||
# constants
|
||||
GNU = 1
|
||||
SOLARIS = 2
|
||||
# defaults
|
||||
extractall = 0 # FIXME: currently this option has no effect at all.
|
||||
escape = 0
|
||||
keywords = []
|
||||
outpath = ''
|
||||
outfile = 'messages.pot'
|
||||
writelocations = 1
|
||||
locationstyle = GNU
|
||||
verbose = 0
|
||||
width = 78
|
||||
excludefilename = ''
|
||||
docstrings = 0
|
||||
nodocstrings = {}
|
||||
|
||||
options = Options()
|
||||
locations = {'gnu' : options.GNU,
|
||||
'solaris' : options.SOLARIS,
|
||||
}
|
||||
|
||||
# parse options
|
||||
for opt, arg in opts:
|
||||
if opt in ('-h', '--help'):
|
||||
usage(0)
|
||||
elif opt in ('-a', '--extract-all'):
|
||||
options.extractall = 1
|
||||
elif opt in ('-d', '--default-domain'):
|
||||
options.outfile = arg + '.pot'
|
||||
elif opt in ('-E', '--escape'):
|
||||
options.escape = 1
|
||||
elif opt in ('-D', '--docstrings'):
|
||||
options.docstrings = 1
|
||||
elif opt in ('-k', '--keyword'):
|
||||
options.keywords.append(arg)
|
||||
elif opt in ('-K', '--no-default-keywords'):
|
||||
default_keywords = []
|
||||
elif opt in ('-n', '--add-location'):
|
||||
options.writelocations = 1
|
||||
elif opt in ('--no-location',):
|
||||
options.writelocations = 0
|
||||
elif opt in ('-S', '--style'):
|
||||
options.locationstyle = locations.get(arg.lower())
|
||||
if options.locationstyle is None:
|
||||
usage(1, ('Invalid value for --style: %s') % arg)
|
||||
elif opt in ('-o', '--output'):
|
||||
options.outfile = arg
|
||||
elif opt in ('-p', '--output-dir'):
|
||||
options.outpath = arg
|
||||
elif opt in ('-v', '--verbose'):
|
||||
options.verbose = 1
|
||||
elif opt in ('-V', '--version'):
|
||||
print ('pygettext.py (xgettext for Python) %s') % __version__
|
||||
sys.exit(0)
|
||||
elif opt in ('-w', '--width'):
|
||||
try:
|
||||
options.width = int(arg)
|
||||
except ValueError:
|
||||
usage(1, ('--width argument must be an integer: %s') % arg)
|
||||
elif opt in ('-x', '--exclude-file'):
|
||||
options.excludefilename = arg
|
||||
elif opt in ('-X', '--no-docstrings'):
|
||||
fp = open(arg)
|
||||
try:
|
||||
while 1:
|
||||
line = fp.readline()
|
||||
if not line:
|
||||
break
|
||||
options.nodocstrings[line[:-1]] = 1
|
||||
finally:
|
||||
fp.close()
|
||||
|
||||
# calculate escapes
|
||||
make_escapes(options.escape)
|
||||
|
||||
# calculate all keywords
|
||||
options.keywords.extend(default_keywords)
|
||||
|
||||
# initialize list of strings to exclude
|
||||
if options.excludefilename:
|
||||
try:
|
||||
fp = open(options.excludefilename)
|
||||
options.toexclude = fp.readlines()
|
||||
fp.close()
|
||||
except IOError:
|
||||
print >> sys.stderr, (
|
||||
"Can't read --exclude-file: %s") % options.excludefilename
|
||||
sys.exit(1)
|
||||
else:
|
||||
options.toexclude = []
|
||||
|
||||
# resolve args to module lists
|
||||
expanded = []
|
||||
for arg in args:
|
||||
if arg == '-':
|
||||
expanded.append(arg)
|
||||
else:
|
||||
expanded.extend(getFilesForName(arg))
|
||||
args = expanded
|
||||
|
||||
# slurp through all the files
|
||||
eater = TokenEater(options)
|
||||
for filename in args:
|
||||
if filename == '-':
|
||||
if options.verbose:
|
||||
print ('Reading standard input')
|
||||
fp = sys.stdin
|
||||
closep = 0
|
||||
else:
|
||||
if options.verbose:
|
||||
print ('Working on %s') % filename
|
||||
fp = open(filename)
|
||||
closep = 1
|
||||
try:
|
||||
eater.set_filename(filename)
|
||||
try:
|
||||
tokenize.tokenize(fp.readline, eater)
|
||||
except tokenize.TokenError, e:
|
||||
print >> sys.stderr, '%s: %s, line %d, column %d' % (
|
||||
e[0], filename, e[1][0], e[1][1])
|
||||
except IndentationError, e:
|
||||
print >> sys.stderr, '%s: %s, line %s, column %s' % (
|
||||
e[0], filename, e.lineno, e[1][1])
|
||||
|
||||
finally:
|
||||
if closep:
|
||||
fp.close()
|
||||
|
||||
# write the output
|
||||
eater.write(outfile)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.stdout)
|
@ -6,11 +6,10 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import os, cStringIO, tempfile, shutil, atexit, subprocess, glob, re
|
||||
import os, tempfile, shutil, subprocess, glob, re, time, textwrap
|
||||
from distutils import sysconfig
|
||||
|
||||
from setup import Command, __appname__
|
||||
from setup.pygettext import main as pygettext
|
||||
from setup import Command, __appname__, __version__
|
||||
from setup.build_environment import pyqt
|
||||
|
||||
class POT(Command):
|
||||
@ -60,13 +59,44 @@ class POT(Command):
|
||||
|
||||
|
||||
def run(self, opts):
|
||||
pot_header = textwrap.dedent('''\
|
||||
# Translation template file..
|
||||
# Copyright (C) %(year)s Kovid Goyal
|
||||
# Kovid Goyal <kovid@kovidgoyal.net>, %(year)s.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: %(appname)s %(version)s\\n"
|
||||
"POT-Creation-Date: %(time)s\\n"
|
||||
"PO-Revision-Date: %(time)s\\n"
|
||||
"Last-Translator: Automatically generated\\n"
|
||||
"Language-Team: LANGUAGE\\n"
|
||||
"MIME-Version: 1.0\\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/calibre\\n"
|
||||
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\\n"
|
||||
"Content-Transfer-Encoding: 8bit\\n"
|
||||
|
||||
''')%dict(appname=__appname__, version=__version__,
|
||||
year=time.strftime('%Y'),
|
||||
time=time.strftime('%Y-%m-%d %H:%M+%Z'))
|
||||
|
||||
files = self.source_files()
|
||||
buf = cStringIO.StringIO()
|
||||
with tempfile.NamedTemporaryFile() as fl:
|
||||
fl.write('\n'.join(files))
|
||||
fl.flush()
|
||||
out = tempfile.NamedTemporaryFile(suffix='.pot', delete=False)
|
||||
out.close()
|
||||
self.info('Creating translations template...')
|
||||
tempdir = tempfile.mkdtemp()
|
||||
atexit.register(shutil.rmtree, tempdir)
|
||||
pygettext(buf, ['-k', '__', '-p', tempdir]+files)
|
||||
src = buf.getvalue()
|
||||
subprocess.check_call(['xgettext', '-f', fl.name,
|
||||
'--default-domain=calibre', '-o', out.name, '-L', 'Python',
|
||||
'--from-code=UTF-8', '--sort-by-file', '--omit-header',
|
||||
'--no-wrap', '-k__',
|
||||
])
|
||||
with open(out.name, 'rb') as f:
|
||||
src = f.read()
|
||||
os.remove(out.name)
|
||||
src = pot_header + '\n' + src
|
||||
src += '\n\n' + self.get_tweaks_docs()
|
||||
pot = os.path.join(self.PATH, __appname__+'.pot')
|
||||
with open(pot, 'wb') as f:
|
||||
|
@ -211,6 +211,28 @@ if __name__ == '__main__':
|
||||
class TestSHLock(unittest.TestCase):
|
||||
"""Testcases for SHLock class."""
|
||||
|
||||
def test_multithread_deadlock(self):
|
||||
lock = SHLock()
|
||||
def two_shared():
|
||||
r = RWLockWrapper(lock)
|
||||
with r:
|
||||
time.sleep(0.2)
|
||||
with r:
|
||||
pass
|
||||
def one_exclusive():
|
||||
time.sleep(0.1)
|
||||
w = RWLockWrapper(lock, is_shared=False)
|
||||
with w:
|
||||
pass
|
||||
threads = [Thread(target=two_shared), Thread(target=one_exclusive)]
|
||||
for t in threads:
|
||||
t.daemon = True
|
||||
t.start()
|
||||
for t in threads:
|
||||
t.join(5)
|
||||
live = [t for t in threads if t.is_alive()]
|
||||
self.assertListEqual(live, [], 'ShLock hung')
|
||||
|
||||
def test_upgrade(self):
|
||||
lock = SHLock()
|
||||
lock.acquire(shared=True)
|
||||
|
@ -124,11 +124,11 @@ class ANDROID(USBMS):
|
||||
'IDEOS_TABLET', 'MYTOUCH_4G', 'UMS_COMPOSITE', 'SCH-I800_CARD',
|
||||
'7', 'A956', 'A955', 'A43', 'ANDROID_PLATFORM', 'TEGRA_2',
|
||||
'MB860', 'MULTI-CARD', 'MID7015A', 'INCREDIBLE', 'A7EB', 'STREAK',
|
||||
'MB525', 'ANDROID2.3']
|
||||
'MB525', 'ANDROID2.3', 'SGH-I997']
|
||||
WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD', 'SGH-I897',
|
||||
'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_CARD',
|
||||
'A70S', 'A101IT', '7', 'INCREDIBLE', 'A7EB', 'SGH-T849_CARD',
|
||||
'__UMS_COMPOSITE']
|
||||
'__UMS_COMPOSITE', 'SGH-I997_CARD']
|
||||
|
||||
OSX_MAIN_MEM = 'Android Device Main Memory'
|
||||
|
||||
|
@ -446,7 +446,8 @@ class ITUNES(DriverBase):
|
||||
}
|
||||
|
||||
if self.report_progress is not None:
|
||||
self.report_progress((i+1)/book_count, _('%d of %d') % (i+1, book_count))
|
||||
self.report_progress((i+1)/book_count,
|
||||
_('%(num)d of %(tot)d') % dict(num=i+1, tot=book_count))
|
||||
self._purge_orphans(library_books, cached_books)
|
||||
|
||||
elif iswindows:
|
||||
@ -485,7 +486,8 @@ class ITUNES(DriverBase):
|
||||
|
||||
if self.report_progress is not None:
|
||||
self.report_progress((i+1)/book_count,
|
||||
_('%d of %d') % (i+1, book_count))
|
||||
_('%(num)d of %(tot)d') % dict(num=i+1,
|
||||
tot=book_count))
|
||||
self._purge_orphans(library_books, cached_books)
|
||||
|
||||
finally:
|
||||
@ -1075,7 +1077,8 @@ class ITUNES(DriverBase):
|
||||
|
||||
# Report progress
|
||||
if self.report_progress is not None:
|
||||
self.report_progress((i+1)/file_count, _('%d of %d') % (i+1, file_count))
|
||||
self.report_progress((i+1)/file_count,
|
||||
_('%(num)d of %(tot)d') % dict(num=i+1, tot=file_count))
|
||||
|
||||
elif iswindows:
|
||||
try:
|
||||
@ -1118,7 +1121,8 @@ class ITUNES(DriverBase):
|
||||
|
||||
# Report progress
|
||||
if self.report_progress is not None:
|
||||
self.report_progress((i+1)/file_count, _('%d of %d') % (i+1, file_count))
|
||||
self.report_progress((i+1)/file_count,
|
||||
_('%(num)d of %(tot)d') % dict(num=i+1, tot=file_count))
|
||||
finally:
|
||||
pythoncom.CoUninitialize()
|
||||
|
||||
@ -3107,7 +3111,8 @@ class ITUNES_ASYNC(ITUNES):
|
||||
}
|
||||
|
||||
if self.report_progress is not None:
|
||||
self.report_progress((i+1)/book_count, _('%d of %d') % (i+1, book_count))
|
||||
self.report_progress((i+1)/book_count,
|
||||
_('%(num)d of %(tot)d') % dict(num=i+1, tot=book_count))
|
||||
|
||||
elif iswindows:
|
||||
try:
|
||||
@ -3147,7 +3152,8 @@ class ITUNES_ASYNC(ITUNES):
|
||||
|
||||
if self.report_progress is not None:
|
||||
self.report_progress((i+1)/book_count,
|
||||
_('%d of %d') % (i+1, book_count))
|
||||
_('%(num)d of %(tot)d') % dict(num=i+1,
|
||||
tot=book_count))
|
||||
|
||||
finally:
|
||||
pythoncom.CoUninitialize()
|
||||
|
@ -57,6 +57,7 @@ class KOBO(USBMS):
|
||||
def initialize(self):
|
||||
USBMS.initialize(self)
|
||||
self.book_class = Book
|
||||
self.dbversion = 7
|
||||
|
||||
def books(self, oncard=None, end_session=True):
|
||||
from calibre.ebooks.metadata.meta import path_to_ext
|
||||
@ -214,7 +215,7 @@ class KOBO(USBMS):
|
||||
'BookID is Null and ( ___ExpirationStatus <> "3" or ___ExpirationStatus is Null)'
|
||||
elif self.dbversion < 16 and self.dbversion >= 14:
|
||||
query= 'select Title, Attribution, DateCreated, ContentID, MimeType, ContentType, ' \
|
||||
'ImageID, ReadStatus, ___ExpirationStatus, "-1" as FavouritesIndex, "-1" as Accessibility from content where ' \
|
||||
'ImageID, ReadStatus, ___ExpirationStatus, FavouritesIndex, "-1" as Accessibility from content where ' \
|
||||
'BookID is Null and ( ___ExpirationStatus <> "3" or ___ExpirationStatus is Null)'
|
||||
elif self.dbversion < 14 and self.dbversion >= 8:
|
||||
query= 'select Title, Attribution, DateCreated, ContentID, MimeType, ContentType, ' \
|
||||
@ -319,8 +320,15 @@ class KOBO(USBMS):
|
||||
# Kobo does not delete the Book row (ie the row where the BookID is Null)
|
||||
# The next server sync should remove the row
|
||||
cursor.execute('delete from content where BookID = ?', t)
|
||||
try:
|
||||
cursor.execute('update content set ReadStatus=0, FirstTimeReading = \'true\', ___PercentRead=0, ___ExpirationStatus=3 ' \
|
||||
'where BookID is Null and ContentID =?',t)
|
||||
except Exception as e:
|
||||
if 'no such column' not in str(e):
|
||||
raise
|
||||
cursor.execute('update content set ReadStatus=0, FirstTimeReading = \'true\', ___PercentRead=0 ' \
|
||||
'where BookID is Null and ContentID =?',t)
|
||||
|
||||
|
||||
connection.commit()
|
||||
|
||||
@ -609,8 +617,9 @@ class KOBO(USBMS):
|
||||
cursor = connection.cursor()
|
||||
try:
|
||||
cursor.execute (query)
|
||||
except:
|
||||
except Exception as e:
|
||||
debug_print(' Database Exception: Unable to reset Shortlist list')
|
||||
if 'no such column' not in str(e):
|
||||
raise
|
||||
else:
|
||||
connection.commit()
|
||||
@ -623,8 +632,9 @@ class KOBO(USBMS):
|
||||
|
||||
try:
|
||||
cursor.execute('update content set FavouritesIndex=1 where BookID is Null and ContentID = ?', t)
|
||||
except:
|
||||
except Exception as e:
|
||||
debug_print(' Database Exception: Unable set book as Shortlist')
|
||||
if 'no such column' not in str(e):
|
||||
raise
|
||||
else:
|
||||
connection.commit()
|
||||
@ -664,6 +674,7 @@ class KOBO(USBMS):
|
||||
# Need to reset the collections outside the particular loops
|
||||
# otherwise the last item will not be removed
|
||||
self.reset_readstatus(connection, oncard)
|
||||
if self.dbversion >= 14:
|
||||
self.reset_favouritesindex(connection, oncard)
|
||||
|
||||
# Process any collections that exist
|
||||
@ -682,7 +693,7 @@ class KOBO(USBMS):
|
||||
if category in readstatuslist.keys():
|
||||
# Manage ReadStatus
|
||||
self.set_readstatus(connection, ContentID, readstatuslist.get(category))
|
||||
if category == 'Shortlist':
|
||||
if category == 'Shortlist' and self.dbversion >= 14:
|
||||
# Manage FavouritesIndex/Shortlist
|
||||
self.set_favouritesindex(connection, ContentID)
|
||||
if category in accessibilitylist.keys():
|
||||
@ -692,6 +703,7 @@ class KOBO(USBMS):
|
||||
# Since no collections exist the ReadStatus needs to be reset to 0 (Unread)
|
||||
debug_print("No Collections - reseting ReadStatus")
|
||||
self.reset_readstatus(connection, oncard)
|
||||
if self.dbversion >= 14:
|
||||
debug_print("No Collections - reseting FavouritesIndex")
|
||||
self.reset_favouritesindex(connection, oncard)
|
||||
|
||||
|
@ -67,10 +67,10 @@ class PRS505(USBMS):
|
||||
_('Comma separated list of metadata fields '
|
||||
'to turn into collections on the device. Possibilities include: ')+\
|
||||
'series, tags, authors' +\
|
||||
_('. Two special collections are available: %s:%s and %s:%s. Add '
|
||||
_('. Two special collections are available: %(abt)s:%(abtv)s and %(aba)s:%(abav)s. Add '
|
||||
'these values to the list to enable them. The collections will be '
|
||||
'given the name provided after the ":" character.')%(
|
||||
'abt', ALL_BY_TITLE, 'aba', ALL_BY_AUTHOR),
|
||||
'given the name provided after the ":" character.')%dict(
|
||||
abt='abt', abtv=ALL_BY_TITLE, aba='aba', abav=ALL_BY_AUTHOR),
|
||||
_('Upload separate cover thumbnails for books (newer readers)') +
|
||||
':::'+_('Normally, the SONY readers get the cover image from the'
|
||||
' ebook file itself. With this option, calibre will send a '
|
||||
|
@ -144,9 +144,9 @@ def add_pipeline_options(parser, plumber):
|
||||
|
||||
'HEURISTIC PROCESSING' : (
|
||||
_('Modify the document text and structure using common'
|
||||
' patterns. Disabled by default. Use %s to enable. '
|
||||
' Individual actions can be disabled with the %s options.')
|
||||
% ('--enable-heuristics', '--disable-*'),
|
||||
' patterns. Disabled by default. Use %(en)s to enable. '
|
||||
' Individual actions can be disabled with the %(dis)s options.')
|
||||
% dict(en='--enable-heuristics', dis='--disable-*'),
|
||||
['enable_heuristics'] + HEURISTIC_OPTIONS
|
||||
),
|
||||
|
||||
|
@ -17,7 +17,8 @@ class ParseError(ValueError):
|
||||
self.name = name
|
||||
self.desc = desc
|
||||
ValueError.__init__(self,
|
||||
_('Failed to parse: %s with error: %s')%(name, desc))
|
||||
_('Failed to parse: %(name)s with error: %(err)s')%dict(
|
||||
name=name, err=desc))
|
||||
|
||||
class ePubFixer(Plugin):
|
||||
|
||||
|
@ -561,7 +561,9 @@ class HTMLConverter(object):
|
||||
para = children[i]
|
||||
break
|
||||
if para is None:
|
||||
raise ConversionError(_('Failed to parse link %s %s')%(tag, children))
|
||||
raise ConversionError(
|
||||
_('Failed to parse link %(tag)s %(children)s')%dict(
|
||||
tag=tag, children=children))
|
||||
text = self.get_text(tag, 1000)
|
||||
if not text:
|
||||
text = 'Link'
|
||||
@ -954,7 +956,9 @@ class HTMLConverter(object):
|
||||
self.scaled_images[path] = pt
|
||||
return pt.name
|
||||
except (IOError, SystemError) as err: # PIL chokes on interlaced PNG images as well a some GIF images
|
||||
self.log.warning(_('Unable to process image %s. Error: %s')%(path, err))
|
||||
self.log.warning(
|
||||
_('Unable to process image %(path)s. Error: %(err)s')%dict(
|
||||
path=path, err=err))
|
||||
|
||||
if width == None or height == None:
|
||||
width, height = im.size
|
||||
@ -1014,7 +1018,7 @@ class HTMLConverter(object):
|
||||
try:
|
||||
self.images[path] = ImageStream(path, encoding=encoding)
|
||||
except LrsError as err:
|
||||
self.log.warning(_('Could not process image: %s\n%s')%(
|
||||
self.log.warning(('Could not process image: %s\n%s')%(
|
||||
original_path, err))
|
||||
return
|
||||
|
||||
|
@ -4,8 +4,9 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
import sys, array, os, re, codecs, logging
|
||||
|
||||
from calibre import setup_cli_handlers, sanitize_file_name
|
||||
from calibre import setup_cli_handlers
|
||||
from calibre.utils.config import OptionParser
|
||||
from calibre.utils.filenames import ascii_filename
|
||||
from calibre.ebooks.lrf.meta import LRFMetaFile
|
||||
from calibre.ebooks.lrf.objects import get_object, PageTree, StyleObject, \
|
||||
Font, Text, TOCObject, BookAttr, ruby_tags
|
||||
@ -89,7 +90,7 @@ class LRFDocument(LRFMetaFile):
|
||||
bookinfo += u'<FreeText reading="">%s</FreeText>\n</BookInfo>\n<DocInfo>\n'%(self.metadata.free_text,)
|
||||
th = self.doc_info.thumbnail
|
||||
if th:
|
||||
prefix = sanitize_file_name(self.metadata.title, as_unicode=True)
|
||||
prefix = ascii_filename(self.metadata.title)
|
||||
bookinfo += u'<CThumbnail file="%s" />\n'%(prefix+'_thumbnail.'+self.doc_info.thumbnail_extension,)
|
||||
if write_files:
|
||||
open(prefix+'_thumbnail.'+self.doc_info.thumbnail_extension, 'wb').write(th)
|
||||
|
@ -529,8 +529,8 @@ class Metadata(object):
|
||||
for t in st.intersection(ot):
|
||||
sidx = lstags.index(t)
|
||||
oidx = lotags.index(t)
|
||||
self_tags[sidx] = other.tags[oidx]
|
||||
self_tags += [t for t in other.tags if t.lower() in ot-st]
|
||||
self_tags[sidx] = other_tags[oidx]
|
||||
self_tags += [t for t in other_tags if t.lower() in ot-st]
|
||||
setattr(self, x, self_tags)
|
||||
|
||||
my_comments = getattr(self, 'comments', '')
|
||||
@ -742,7 +742,7 @@ class Metadata(object):
|
||||
ans += [('ISBN', unicode(self.isbn))]
|
||||
ans += [(_('Tags'), u', '.join([unicode(t) for t in self.tags]))]
|
||||
if self.series:
|
||||
ans += [(_('Series'), unicode(self.series)+ ' #%s'%self.format_series_index())]
|
||||
ans += [_('Series'), unicode(self.series) + ' #%s'%self.format_series_index()]
|
||||
ans += [(_('Language'), unicode(self.language))]
|
||||
if self.timestamp is not None:
|
||||
ans += [(_('Timestamp'), unicode(self.timestamp.isoformat(' ')))]
|
||||
|
@ -21,9 +21,9 @@ USAGE='%%prog ebook_file [' + _('options') + ']\n' + \
|
||||
_('''
|
||||
Read/Write metadata from/to ebook files.
|
||||
|
||||
Supported formats for reading metadata: %s
|
||||
Supported formats for reading metadata: %(read)s
|
||||
|
||||
Supported formats for writing metadata: %s
|
||||
Supported formats for writing metadata: %(write)s
|
||||
|
||||
Different file types support different kinds of metadata. If you try to set
|
||||
some metadata on a file type that does not support it, the metadata will be
|
||||
@ -99,7 +99,7 @@ def option_parser():
|
||||
for w in metadata_writers():
|
||||
writers = writers.union(set(w.file_types))
|
||||
ft, w = ', '.join(sorted(filetypes())), ', '.join(sorted(writers))
|
||||
return config().option_parser(USAGE%(ft, w))
|
||||
return config().option_parser(USAGE%dict(read=ft, write=w))
|
||||
|
||||
def do_set_metadata(opts, mi, stream, stream_type):
|
||||
mi = MetaInformation(mi)
|
||||
|
@ -95,9 +95,9 @@ class CoverManager(object):
|
||||
authors = [unicode(x) for x in m.creator if x.role == 'aut']
|
||||
series_string = None
|
||||
if m.series and m.series_index:
|
||||
series_string = _('Book %s of %s')%(
|
||||
fmt_sidx(m.series_index[0], use_roman=True),
|
||||
unicode(m.series[0]))
|
||||
series_string = _('Book %(sidx)s of %(series)s')%dict(
|
||||
sidx=fmt_sidx(m.series_index[0], use_roman=True),
|
||||
series=unicode(m.series[0]))
|
||||
|
||||
try:
|
||||
from calibre.ebooks import calibre_cover
|
||||
|
@ -32,8 +32,8 @@ class SplitError(ValueError):
|
||||
size = len(tostring(root))/1024.
|
||||
ValueError.__init__(self,
|
||||
_('Could not find reasonable point at which to split: '
|
||||
'%s Sub-tree size: %d KB')%
|
||||
(path, size))
|
||||
'%(path)s Sub-tree size: %(size)d KB')%dict(
|
||||
path=path, size=size))
|
||||
|
||||
class Split(object):
|
||||
|
||||
|
@ -7,6 +7,7 @@ __docformat__ = 'restructuredtext en'
|
||||
import sys, struct, zlib, bz2, os
|
||||
|
||||
from calibre import guess_type
|
||||
from calibre.utils.filenames import ascii_filename
|
||||
|
||||
class FileStream:
|
||||
def IsBinary(self):
|
||||
@ -156,6 +157,8 @@ class SNBFile:
|
||||
f.fileSize = os.path.getsize(os.path.join(tdir,fileName))
|
||||
f.fileBody = open(os.path.join(tdir,fileName), 'rb').read()
|
||||
f.fileName = fileName.replace(os.sep, '/')
|
||||
if isinstance(f.fileName, unicode):
|
||||
f.fileName = ascii_filename(f.fileName).encode('ascii')
|
||||
self.files.append(f)
|
||||
|
||||
def AppendBinary(self, fileName, tdir):
|
||||
@ -164,6 +167,8 @@ class SNBFile:
|
||||
f.fileSize = os.path.getsize(os.path.join(tdir,fileName))
|
||||
f.fileBody = open(os.path.join(tdir,fileName), 'rb').read()
|
||||
f.fileName = fileName.replace(os.sep, '/')
|
||||
if isinstance(f.fileName, unicode):
|
||||
f.fileName = ascii_filename(f.fileName).encode('ascii')
|
||||
self.files.append(f)
|
||||
|
||||
def GetFileStream(self, fileName):
|
||||
|
@ -1,4 +1,4 @@
|
||||
# coding:utf8
|
||||
# coding:utf-8
|
||||
__license__ = 'GPL 3'
|
||||
__copyright__ = '2010, Hiroshi Miura <miurahr@linux.com>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
@ -74,6 +74,13 @@ gprefs.defaults['action-layout-context-menu-device'] = (
|
||||
'Add To Library', 'Edit Collections',
|
||||
)
|
||||
|
||||
gprefs.defaults['action-layout-context-menu-cover-browser'] = (
|
||||
'Edit Metadata', 'Send To Device', 'Save To Disk',
|
||||
'Connect Share', 'Copy To Library', None,
|
||||
'Convert Books', 'View', 'Open Folder', 'Show Book Details',
|
||||
'Similar Books', 'Tweak ePub', None, 'Remove Books',
|
||||
)
|
||||
|
||||
gprefs.defaults['show_splash_screen'] = True
|
||||
gprefs.defaults['toolbar_icon_size'] = 'medium'
|
||||
gprefs.defaults['automerge'] = 'ignore'
|
||||
|
@ -120,16 +120,16 @@ class FetchAnnotationsAction(InterfaceAction):
|
||||
spanTag['style'] = 'font-weight:bold'
|
||||
if bookmark.book_format == 'pdf':
|
||||
spanTag.insert(0,NavigableString(
|
||||
_("%s<br />Last Page Read: %d (%d%%)") % \
|
||||
(strftime(u'%x', timestamp.timetuple()),
|
||||
last_read_location,
|
||||
percent_read)))
|
||||
_("%(time)s<br />Last Page Read: %(loc)d (%(pr)d%%)") % \
|
||||
dict(time=strftime(u'%x', timestamp.timetuple()),
|
||||
loc=last_read_location,
|
||||
pr=percent_read)))
|
||||
else:
|
||||
spanTag.insert(0,NavigableString(
|
||||
_("%s<br />Last Page Read: Location %d (%d%%)") % \
|
||||
(strftime(u'%x', timestamp.timetuple()),
|
||||
last_read_location,
|
||||
percent_read)))
|
||||
_("%(time)s<br />Last Page Read: Location %(loc)d (%(pr)d%%)") % \
|
||||
dict(time=strftime(u'%x', timestamp.timetuple()),
|
||||
loc=last_read_location,
|
||||
pr=percent_read)))
|
||||
|
||||
divTag.insert(dtc, spanTag)
|
||||
dtc += 1
|
||||
@ -145,23 +145,23 @@ class FetchAnnotationsAction(InterfaceAction):
|
||||
for location in sorted(user_notes):
|
||||
if user_notes[location]['text']:
|
||||
annotations.append(
|
||||
_('<b>Location %d • %s</b><br />%s<br />') % \
|
||||
(user_notes[location]['displayed_location'],
|
||||
user_notes[location]['type'],
|
||||
user_notes[location]['text'] if \
|
||||
_('<b>Location %(dl)d • %(typ)s</b><br />%(text)s<br />') % \
|
||||
dict(dl=user_notes[location]['displayed_location'],
|
||||
typ=user_notes[location]['type'],
|
||||
text=(user_notes[location]['text'] if \
|
||||
user_notes[location]['type'] == 'Note' else \
|
||||
'<i>%s</i>' % user_notes[location]['text']))
|
||||
'<i>%s</i>' % user_notes[location]['text'])))
|
||||
else:
|
||||
if bookmark.book_format == 'pdf':
|
||||
annotations.append(
|
||||
_('<b>Page %d • %s</b><br />') % \
|
||||
(user_notes[location]['displayed_location'],
|
||||
user_notes[location]['type']))
|
||||
_('<b>Page %(dl)d • %(typ)s</b><br />') % \
|
||||
dict(dl=user_notes[location]['displayed_location'],
|
||||
typ=user_notes[location]['type']))
|
||||
else:
|
||||
annotations.append(
|
||||
_('<b>Location %d • %s</b><br />') % \
|
||||
(user_notes[location]['displayed_location'],
|
||||
user_notes[location]['type']))
|
||||
_('<b>Location %(dl)d • %(typ)s</b><br />') % \
|
||||
dict(dl=user_notes[location]['displayed_location'],
|
||||
typ=user_notes[location]['type']))
|
||||
|
||||
for annotation in annotations:
|
||||
divTag.insert(dtc, annotation)
|
||||
|
@ -82,7 +82,8 @@ class GenerateCatalogAction(InterfaceAction):
|
||||
self.gui.sync_catalogs()
|
||||
if job.fmt not in ['EPUB','MOBI']:
|
||||
export_dir = choose_dir(self.gui, _('Export Catalog Directory'),
|
||||
_('Select destination for %s.%s') % (job.catalog_title, job.fmt.lower()))
|
||||
_('Select destination for %(title)s.%(fmt)s') % dict(
|
||||
title=job.catalog_title, fmt=job.fmt.lower()))
|
||||
if export_dir:
|
||||
destination = os.path.join(export_dir, '%s.%s' % (job.catalog_title, job.fmt.lower()))
|
||||
shutil.copyfile(job.catalog_file_path, destination)
|
||||
|
@ -160,8 +160,9 @@ class CopyToLibraryAction(InterfaceAction):
|
||||
error_dialog(self.gui, _('Failed'), _('Could not copy books: ') + e,
|
||||
det_msg=tb, show=True)
|
||||
else:
|
||||
self.gui.status_bar.show_message(_('Copied %d books to %s') %
|
||||
(len(ids), loc), 2000)
|
||||
self.gui.status_bar.show_message(
|
||||
_('Copied %(num)d books to %(loc)s') %
|
||||
dict(num=len(ids), loc=loc), 2000)
|
||||
if delete_after and self.worker.processed:
|
||||
v = self.gui.library_view
|
||||
ci = v.currentIndex()
|
||||
|
@ -159,9 +159,9 @@ def render_data(mi, use_roman_numbers=True, all_fields=False):
|
||||
sidx = mi.get(field+'_index')
|
||||
if sidx is None:
|
||||
sidx = 1.0
|
||||
val = _('Book %s of <span class="series_name">%s</span>')%(fmt_sidx(sidx,
|
||||
use_roman=use_roman_numbers),
|
||||
prepare_string_for_xml(getattr(mi, field)))
|
||||
val = _('Book %(sidx)s of <span class="series_name">%(series)s</span>')%dict(
|
||||
sidx=fmt_sidx(sidx, use_roman=use_roman_numbers),
|
||||
series=prepare_string_for_xml(getattr(mi, field)))
|
||||
|
||||
ans.append((field, u'<td class="title">%s</td><td>%s</td>'%(name, val)))
|
||||
|
||||
@ -541,7 +541,8 @@ class BookDetails(QWidget): # {{{
|
||||
self.setToolTip(
|
||||
'<p>'+_('Double-click to open Book Details window') +
|
||||
'<br><br>' + _('Path') + ': ' + self.current_path +
|
||||
'<br><br>' + _('Cover size: %dx%d')%(sz.width(), sz.height())
|
||||
'<br><br>' + _('Cover size: %(width)d x %(height)d')%dict(
|
||||
width=sz.width(), height=sz.height())
|
||||
)
|
||||
|
||||
def reset_info(self):
|
||||
|
@ -9,8 +9,8 @@ Module to implement the Cover Flow feature
|
||||
|
||||
import sys, os, time
|
||||
|
||||
from PyQt4.Qt import QImage, QSizePolicy, QTimer, QDialog, Qt, QSize, \
|
||||
QStackedLayout, QLabel, QByteArray, pyqtSignal
|
||||
from PyQt4.Qt import (QImage, QSizePolicy, QTimer, QDialog, Qt, QSize,
|
||||
QStackedLayout, QLabel, QByteArray, pyqtSignal)
|
||||
|
||||
from calibre import plugins
|
||||
from calibre.gui2 import config, available_height, available_width, gprefs
|
||||
@ -84,6 +84,7 @@ if pictureflow is not None:
|
||||
class CoverFlow(pictureflow.PictureFlow):
|
||||
|
||||
dc_signal = pyqtSignal()
|
||||
context_menu_requested = pyqtSignal()
|
||||
|
||||
def __init__(self, parent=None):
|
||||
pictureflow.PictureFlow.__init__(self, parent,
|
||||
@ -94,6 +95,17 @@ if pictureflow is not None:
|
||||
QSizePolicy.Expanding))
|
||||
self.dc_signal.connect(self._data_changed,
|
||||
type=Qt.QueuedConnection)
|
||||
self.context_menu = None
|
||||
self.setContextMenuPolicy(Qt.DefaultContextMenu)
|
||||
|
||||
def set_context_menu(self, cm):
|
||||
self.context_menu = cm
|
||||
|
||||
def contextMenuEvent(self, event):
|
||||
if self.context_menu is not None:
|
||||
self.context_menu_requested.emit()
|
||||
self.context_menu.popup(event.globalPos())
|
||||
event.accept()
|
||||
|
||||
def sizeHint(self):
|
||||
return self.minimumSize()
|
||||
@ -149,6 +161,7 @@ class CoverFlowMixin(object):
|
||||
self.cover_flow_sync_flag = True
|
||||
self.cover_flow = CoverFlow(parent=self)
|
||||
self.cover_flow.currentChanged.connect(self.sync_listview_to_cf)
|
||||
self.cover_flow.context_menu_requested.connect(self.cf_context_menu_requested)
|
||||
self.library_view.selectionModel().currentRowChanged.connect(
|
||||
self.sync_cf_to_listview)
|
||||
self.db_images = DatabaseImages(self.library_view.model())
|
||||
@ -234,6 +247,14 @@ class CoverFlowMixin(object):
|
||||
self.cover_flow.setCurrentSlide(current.row())
|
||||
self.cover_flow_sync_flag = True
|
||||
|
||||
def cf_context_menu_requested(self):
|
||||
row = self.cover_flow.currentSlide()
|
||||
m = self.library_view.model()
|
||||
index = m.index(row, 0)
|
||||
sm = self.library_view.selectionModel()
|
||||
sm.select(index, sm.ClearAndSelect|sm.Rows)
|
||||
self.library_view.setCurrentIndex(index)
|
||||
|
||||
def cover_flow_do_sync(self):
|
||||
self.cover_flow_sync_flag = True
|
||||
try:
|
||||
|
@ -912,8 +912,9 @@ class DeviceMixin(object): # {{{
|
||||
format_count[f] = 1
|
||||
for f in self.device_manager.device.settings().format_map:
|
||||
if f in format_count.keys():
|
||||
formats.append((f, _('%i of %i Books') % (format_count[f],
|
||||
len(rows)), True if f in aval_out_formats else False))
|
||||
formats.append((f, _('%(num)i of %(total)i Books') % dict(
|
||||
num=format_count[f], total=len(rows)),
|
||||
True if f in aval_out_formats else False))
|
||||
elif f in aval_out_formats:
|
||||
formats.append((f, _('0 of %i Books') % len(rows), True))
|
||||
d = ChooseFormatDeviceDialog(self, _('Choose format to send to device'), formats)
|
||||
|
@ -106,7 +106,8 @@ class BookInfo(QDialog, Ui_BookInfo):
|
||||
Qt.KeepAspectRatio, Qt.SmoothTransformation)
|
||||
self.cover.set_pixmap(pixmap)
|
||||
sz = pixmap.size()
|
||||
self.cover.setToolTip(_('Cover size: %dx%d')%(sz.width(), sz.height()))
|
||||
self.cover.setToolTip(_('Cover size: %(width)d x %(height)d')%dict(
|
||||
width=sz.width(), height=sz.height()))
|
||||
|
||||
def refresh(self, row):
|
||||
if isinstance(row, QModelIndex):
|
||||
|
@ -173,10 +173,10 @@ class MyBlockingBusy(QDialog): # {{{
|
||||
mi = self.db.get_metadata(id, index_is_id=True)
|
||||
series_string = None
|
||||
if mi.series:
|
||||
series_string = _('Book %s of %s')%(
|
||||
fmt_sidx(mi.series_index,
|
||||
series_string = _('Book %(sidx)s of %(series)s')%dict(
|
||||
sidx=fmt_sidx(mi.series_index,
|
||||
use_roman=config['use_roman_numerals_for_series_number']),
|
||||
mi.series)
|
||||
series=mi.series)
|
||||
|
||||
cdata = calibre_cover(mi.title, mi.format_field('authors')[-1],
|
||||
series_string=series_string)
|
||||
@ -749,15 +749,9 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog):
|
||||
val = self.s_r_do_regexp(mi)
|
||||
val = self.s_r_do_destination(mi, val)
|
||||
if dfm['is_multiple']:
|
||||
if dest == 'authors' and len(val) == 0:
|
||||
error_dialog(self, _('Search/replace invalid'),
|
||||
_('Authors cannot be set to the empty string. '
|
||||
'Book title %s not processed')%mi.title,
|
||||
show=True)
|
||||
return
|
||||
# convert the colon-separated pair strings back into a dict, which
|
||||
# is what set_identifiers wants
|
||||
if dfm['is_csp']:
|
||||
# convert the colon-separated pair strings back into a dict,
|
||||
# which is what set_identifiers wants
|
||||
dst_id_type = unicode(self.s_r_dst_ident.text())
|
||||
if dst_id_type:
|
||||
v = ''.join(val)
|
||||
@ -769,11 +763,7 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog):
|
||||
else:
|
||||
val = self.s_r_replace_mode_separator().join(val)
|
||||
if dest == 'title' and len(val) == 0:
|
||||
error_dialog(self, _('Search/replace invalid'),
|
||||
_('Title cannot be set to the empty string. '
|
||||
'Book title %s not processed')%mi.title,
|
||||
show=True)
|
||||
return
|
||||
val = _('Unknown')
|
||||
|
||||
if dfm['is_custom']:
|
||||
extra = self.db.get_custom_extra(id, label=dfm['label'], index_is_id=True)
|
||||
|
@ -701,7 +701,9 @@ class PluginUpdaterDialog(SizePersistedDialog):
|
||||
|
||||
if DEBUG:
|
||||
prints('Locating zip file for %s: %s'% (display_plugin.name, display_plugin.forum_link))
|
||||
self.gui.status_bar.showMessage(_('Locating zip file for %s: %s') % (display_plugin.name, display_plugin.forum_link))
|
||||
self.gui.status_bar.showMessage(
|
||||
_('Locating zip file for %(name)s: %(link)s') % dict(
|
||||
name=display_plugin.name, link=display_plugin.forum_link))
|
||||
plugin_zip_url = self._read_zip_attachment_url(display_plugin.forum_link)
|
||||
if not plugin_zip_url:
|
||||
return error_dialog(self.gui, _('Install Plugin Failed'),
|
||||
|
@ -381,7 +381,9 @@ class SchedulerDialog(QDialog, Ui_Dialog):
|
||||
d = utcnow() - last_downloaded
|
||||
def hm(x): return (x-x%3600)//3600, (x%3600 - (x%3600)%60)//60
|
||||
hours, minutes = hm(d.seconds)
|
||||
tm = _('%d days, %d hours and %d minutes ago')%(d.days, hours, minutes)
|
||||
tm = _('%(days)d days, %(hours)d hours'
|
||||
' and %(mins)d minutes ago')%dict(
|
||||
days=d.days, hours=hours, mins=minutes)
|
||||
if d < timedelta(days=366):
|
||||
ld_text = tm
|
||||
else:
|
||||
|
@ -57,7 +57,7 @@ class TagCategories(QDialog, Ui_TagCategories):
|
||||
lambda: [n for (id, n) in self.db.all_publishers()],
|
||||
lambda: self.db.all_tags()
|
||||
]
|
||||
category_names = ['', _('Authors'), _('Series'), _('Publishers'), _('Tags')]
|
||||
category_names = ['', _('Authors'), ngettext('Series', 'Series', 2), _('Publishers'), _('Tags')]
|
||||
|
||||
cvals = {}
|
||||
for key,cc in self.db.custom_field_metadata().iteritems():
|
||||
@ -260,6 +260,7 @@ class TagCategories(QDialog, Ui_TagCategories):
|
||||
self.applied_items = [cat[2] for cat in self.categories.get(self.current_cat_name, [])]
|
||||
else:
|
||||
self.applied_items = []
|
||||
self.applied_items.sort(key=lambda x:sort_key(self.all_items[x].name))
|
||||
self.display_filtered_categories(None)
|
||||
|
||||
def accept(self):
|
||||
|
@ -18,7 +18,8 @@ class ListWidgetItem(QListWidgetItem):
|
||||
def data(self, role):
|
||||
if role == Qt.DisplayRole:
|
||||
if self.initial_value != self.current_value:
|
||||
return _('%s (was %s)')%(self.current_value, self.initial_value)
|
||||
return _('%(curr)s (was %(initial)s)')%dict(
|
||||
curr=self.current_value, initial=self.initial_value)
|
||||
else:
|
||||
return self.current_value
|
||||
elif role == Qt.EditRole:
|
||||
|
@ -143,7 +143,9 @@ class UserProfiles(ResizableDialog, Ui_Dialog):
|
||||
pt = PersistentTemporaryFile(suffix='.recipe')
|
||||
pt.write(src.encode('utf-8'))
|
||||
pt.close()
|
||||
body = _('The attached file: %s is a recipe to download %s.')%(os.path.basename(pt.name), title)
|
||||
body = _('The attached file: %(fname)s is a '
|
||||
'recipe to download %(title)s.')%dict(
|
||||
fname=os.path.basename(pt.name), title=title)
|
||||
subject = _('Recipe for ')+title
|
||||
url = QUrl('mailto:')
|
||||
url.addQueryItem('subject', subject)
|
||||
|
@ -51,8 +51,8 @@ class DownloadDialog(QDialog): # {{{
|
||||
self.setWindowTitle(_('Download %s')%fname)
|
||||
self.l = QVBoxLayout(self)
|
||||
self.purl = urlparse(url)
|
||||
self.msg = QLabel(_('Downloading <b>%s</b> from %s')%(fname,
|
||||
self.purl.netloc))
|
||||
self.msg = QLabel(_('Downloading <b>%(fname)s</b> from %(url)s')%dict(
|
||||
fname=fname, url=self.purl.netloc))
|
||||
self.msg.setWordWrap(True)
|
||||
self.l.addWidget(self.msg)
|
||||
self.pb = QProgressBar(self)
|
||||
@ -82,8 +82,8 @@ class DownloadDialog(QDialog): # {{{
|
||||
self.exec_()
|
||||
if self.worker.err is not None:
|
||||
error_dialog(self.parent(), _('Download failed'),
|
||||
_('Failed to download from %r with error: %s')%(
|
||||
self.worker.url, self.worker.err),
|
||||
_('Failed to download from %(url)r with error: %(err)s')%dict(
|
||||
url=self.worker.url, err=self.worker.err),
|
||||
det_msg=self.worker.tb, show=True)
|
||||
|
||||
def update(self):
|
||||
|
@ -120,7 +120,7 @@ def send_mails(jobnames, callback, attachments, to_s, subjects,
|
||||
texts, attachment_names, job_manager):
|
||||
for name, attachment, to, subject, text, aname in zip(jobnames,
|
||||
attachments, to_s, subjects, texts, attachment_names):
|
||||
description = _('Email %s to %s') % (name, to)
|
||||
description = _('Email %(name)s to %(to)s') % dict(name=name, to=to)
|
||||
job = ThreadedJob('email', description, gui_sendmail, (attachment, aname, to,
|
||||
subject, text), {}, callback)
|
||||
job_manager.run_threaded_job(job)
|
||||
|
@ -62,7 +62,6 @@ class LibraryViewMixin(object): # {{{
|
||||
view = getattr(self, view+'_view')
|
||||
view.verticalHeader().sectionDoubleClicked.connect(self.iactions['View'].view_specific_book)
|
||||
|
||||
self.build_context_menus()
|
||||
self.library_view.model().set_highlight_only(config['highlight_search_matches'])
|
||||
|
||||
def build_context_menus(self):
|
||||
@ -81,6 +80,11 @@ class LibraryViewMixin(object): # {{{
|
||||
for v in (self.memory_view, self.card_a_view, self.card_b_view):
|
||||
v.set_context_menu(dm, ec)
|
||||
|
||||
if self.cover_flow is not None:
|
||||
cm = QMenu(self.cover_flow)
|
||||
populate_menu(cm,
|
||||
gprefs['action-layout-context-menu-cover-browser'])
|
||||
self.cover_flow.set_context_menu(cm)
|
||||
|
||||
def search_done(self, view, ok):
|
||||
if view is self.current_view():
|
||||
|
@ -878,9 +878,10 @@ class Cover(ImageView): # {{{
|
||||
series = self.dialog.series.current_val
|
||||
series_string = None
|
||||
if series:
|
||||
series_string = _('Book %s of %s')%(
|
||||
fmt_sidx(self.dialog.series_index.current_val,
|
||||
use_roman=config['use_roman_numerals_for_series_number']), series)
|
||||
series_string = _('Book %(sidx)s of %(series)s')%dict(
|
||||
sidx=fmt_sidx(self.dialog.series_index.current_val,
|
||||
use_roman=config['use_roman_numerals_for_series_number']),
|
||||
series=series)
|
||||
self.current_val = calibre_cover(title, author,
|
||||
series_string=series_string)
|
||||
|
||||
@ -921,8 +922,8 @@ class Cover(ImageView): # {{{
|
||||
self.setPixmap(pm)
|
||||
tt = _('This book has no cover')
|
||||
if self._cdata:
|
||||
tt = _('Cover size: %dx%d pixels') % \
|
||||
(pm.width(), pm.height())
|
||||
tt = _('Cover size: %(width)d x %(height)d pixels') % \
|
||||
dict(width=pm.width(), height=pm.height())
|
||||
self.setToolTip(tt)
|
||||
|
||||
return property(fget=fget, fset=fset)
|
||||
|
@ -196,7 +196,7 @@ def download(ids, db, do_identify, covers,
|
||||
ans[i] = mi
|
||||
count += 1
|
||||
notifications.put((count/len(ids),
|
||||
_('Downloaded %d of %d')%(count, len(ids))))
|
||||
_('Downloaded %(num)d of %(tot)d')%dict(num=count, tot=len(ids))))
|
||||
log('Download complete, with %d failures'%len(failed_ids))
|
||||
return (ans, failed_ids, failed_covers, title_map, all_failed)
|
||||
|
||||
|
@ -726,8 +726,8 @@ class CoversWidget(QWidget): # {{{
|
||||
if num < 2:
|
||||
txt = _('Could not find any covers for <b>%s</b>')%self.book.title
|
||||
else:
|
||||
txt = _('Found <b>%d</b> covers of %s. Pick the one you like'
|
||||
' best.')%(num-1, self.title)
|
||||
txt = _('Found <b>%(num)d</b> covers of %(title)s. Pick the one you like'
|
||||
' best.')%dict(num=num-1, title=self.title)
|
||||
self.msg.setText(txt)
|
||||
|
||||
self.finished.emit()
|
||||
|
@ -1332,6 +1332,7 @@ void PictureFlow::mousePressEvent(QMouseEvent* event)
|
||||
|
||||
void PictureFlow::mouseReleaseEvent(QMouseEvent* event)
|
||||
{
|
||||
bool accepted = false;
|
||||
int sideWidth = (d->buffer.width() - slideSize().width()) /2;
|
||||
|
||||
if (d->singlePress)
|
||||
@ -1339,14 +1340,21 @@ void PictureFlow::mouseReleaseEvent(QMouseEvent* event)
|
||||
if (event->x() < sideWidth )
|
||||
{
|
||||
showPrevious();
|
||||
accepted = true;
|
||||
} else if ( event->x() > sideWidth + slideSize().width() ) {
|
||||
showNext();
|
||||
accepted = true;
|
||||
} else {
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
emit itemActivated(d->getTarget());
|
||||
accepted = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (accepted) {
|
||||
event->accept();
|
||||
}
|
||||
}
|
||||
|
||||
emit inputReceived();
|
||||
}
|
||||
|
@ -445,15 +445,15 @@ class RulesModel(QAbstractListModel): # {{{
|
||||
def rule_to_html(self, col, rule):
|
||||
if not isinstance(rule, Rule):
|
||||
return _('''
|
||||
<p>Advanced Rule for column <b>%s</b>:
|
||||
<pre>%s</pre>
|
||||
''')%(col, prepare_string_for_xml(rule))
|
||||
<p>Advanced Rule for column <b>%(col)s</b>:
|
||||
<pre>%(rule)s</pre>
|
||||
''')%dict(col=col, rule=prepare_string_for_xml(rule))
|
||||
conditions = [self.condition_to_html(c) for c in rule.conditions]
|
||||
return _('''\
|
||||
<p>Set the color of <b>%s</b> to <b>%s</b> if the following
|
||||
<p>Set the color of <b>%(col)s</b> to <b>%(color)s</b> if the following
|
||||
conditions are met:</p>
|
||||
<ul>%s</ul>
|
||||
''') % (col, rule.color, ''.join(conditions))
|
||||
<ul>%(rule)s</ul>
|
||||
''') % dict(col=col, color=rule.color, rule=''.join(conditions))
|
||||
|
||||
def condition_to_html(self, condition):
|
||||
c, a, v = condition
|
||||
@ -464,8 +464,8 @@ class RulesModel(QAbstractListModel): # {{{
|
||||
action_name = trans
|
||||
|
||||
return (
|
||||
_('<li>If the <b>%s</b> column <b>%s</b> value: <b>%s</b>') %
|
||||
(c, action_name, prepare_string_for_xml(v)))
|
||||
_('<li>If the <b>%(col)s</b> column <b>%(action)s</b> value: <b>%(val)s</b>') %
|
||||
dict(col=c, action=action_name, val=prepare_string_for_xml(v)))
|
||||
|
||||
# }}}
|
||||
|
||||
|
@ -262,8 +262,8 @@ class PluginConfig(QWidget): # {{{
|
||||
|
||||
self.l = l = QVBoxLayout()
|
||||
self.setLayout(l)
|
||||
self.c = c = QLabel(_('<b>Configure %s</b><br>%s') % (plugin.name,
|
||||
plugin.description))
|
||||
self.c = c = QLabel(_('<b>Configure %(name)s</b><br>%(desc)s') % dict(
|
||||
name=plugin.name, desc=plugin.description))
|
||||
c.setAlignment(Qt.AlignHCenter)
|
||||
l.addWidget(c)
|
||||
|
||||
|
@ -225,6 +225,8 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
||||
'calibre library')),
|
||||
('context-menu-device', _('The context menu for the books on '
|
||||
'the device')),
|
||||
('context-menu-cover-browser', _('The context menu for the cover '
|
||||
'browser')),
|
||||
]
|
||||
|
||||
def genesis(self, gui):
|
||||
|
@ -67,7 +67,9 @@ class CacheUpdateThread(Thread, QObject):
|
||||
self.total_changed.emit(len(raw_books))
|
||||
|
||||
for i, book_data in enumerate(raw_books):
|
||||
self.update_details.emit(_('%s of %s books processed.') % (i, len(raw_books)))
|
||||
self.update_details.emit(
|
||||
_('%(num)s of %(tot)s books processed.') % dict(
|
||||
num=i, tot=len(raw_books)))
|
||||
book = SearchResult()
|
||||
book.detail_item = ''.join(book_data.xpath('.//a/@href'))
|
||||
book.formats = ''.join(book_data.xpath('.//i/text()'))
|
||||
|
@ -394,7 +394,7 @@ class TagsModel(QAbstractItemModel): # {{{
|
||||
not fm['is_custom'] and \
|
||||
not fm['kind'] == 'user' \
|
||||
else False
|
||||
in_uc = fm['kind'] == 'user'
|
||||
in_uc = fm['kind'] == 'user' and not is_gst
|
||||
tt = key if in_uc else None
|
||||
|
||||
if collapse_model == 'first letter':
|
||||
@ -685,42 +685,35 @@ class TagsModel(QAbstractItemModel): # {{{
|
||||
|
||||
def handle_user_category_drop(self, on_node, ids, column):
|
||||
categories = self.db.prefs.get('user_categories', {})
|
||||
category = categories.get(on_node.category_key[1:], None)
|
||||
if category is None:
|
||||
cat_contents = categories.get(on_node.category_key[1:], None)
|
||||
if cat_contents is None:
|
||||
return
|
||||
cat_contents = set([(v, c) for v,c,ign in cat_contents])
|
||||
|
||||
fm_src = self.db.metadata_for_field(column)
|
||||
for id in ids:
|
||||
label = fm_src['label']
|
||||
|
||||
for id in ids:
|
||||
if not fm_src['is_custom']:
|
||||
if label == 'authors':
|
||||
items = self.db.get_authors_with_ids()
|
||||
items = [(i[0], i[1].replace('|', ',')) for i in items]
|
||||
value = self.db.authors(id, index_is_id=True)
|
||||
value = [v.replace('|', ',') for v in value.split(',')]
|
||||
elif label == 'publisher':
|
||||
items = self.db.get_publishers_with_ids()
|
||||
value = self.db.publisher(id, index_is_id=True)
|
||||
elif label == 'series':
|
||||
items = self.db.get_series_with_ids()
|
||||
value = self.db.series(id, index_is_id=True)
|
||||
else:
|
||||
items = self.db.get_custom_items_with_ids(label=label)
|
||||
if fm_src['datatype'] != 'composite':
|
||||
value = self.db.get_custom(id, label=label, index_is_id=True)
|
||||
else:
|
||||
value = self.db.get_property(id, loc=fm_src['rec_index'],
|
||||
index_is_id=True)
|
||||
if value is None:
|
||||
return
|
||||
if value:
|
||||
if not isinstance(value, list):
|
||||
value = [value]
|
||||
for val in value:
|
||||
for (v, c, id) in category:
|
||||
if v == val and c == column:
|
||||
break
|
||||
else:
|
||||
category.append([val, column, 0])
|
||||
categories[on_node.category_key[1:]] = category
|
||||
cat_contents |= set([(v, column) for v in value])
|
||||
|
||||
categories[on_node.category_key[1:]] = [[v, c, 0] for v,c in cat_contents]
|
||||
self.db.prefs.set('user_categories', categories)
|
||||
self.refresh_required.emit()
|
||||
|
||||
|
@ -384,8 +384,8 @@ class TagsView(QTreeView): # {{{
|
||||
action='delete_search', key=tag.name))
|
||||
if key.startswith('@') and not item.is_gst:
|
||||
self.context_menu.addAction(self.user_category_icon,
|
||||
_('Remove %s from category %s')%
|
||||
(display_name(tag), item.py_name),
|
||||
_('Remove %(item)s from category %(cat)s')%
|
||||
dict(item=display_name(tag), cat=item.py_name),
|
||||
partial(self.context_menu_handler,
|
||||
action='delete_item_from_user_category',
|
||||
key = key, index = tag_item))
|
||||
|
@ -94,8 +94,8 @@ def convert_single_ebook(parent, db, book_ids, auto_conversion=False, # {{{
|
||||
|
||||
msg = '%s' % '\n'.join(res)
|
||||
warning_dialog(parent, _('Could not convert some books'),
|
||||
_('Could not convert %d of %d books, because no suitable source'
|
||||
' format was found.') % (len(res), total),
|
||||
_('Could not convert %(num)d of %(tot)d books, because no suitable source'
|
||||
' format was found.') % dict(num=len(res), tot=total),
|
||||
msg).exec_()
|
||||
|
||||
return jobs, changed, bad
|
||||
@ -187,7 +187,8 @@ class QueueBulk(QProgressDialog):
|
||||
except:
|
||||
dtitle = repr(mi.title)
|
||||
self.setLabelText(_('Queueing ')+dtitle)
|
||||
desc = _('Convert book %d of %d (%s)') % (self.i, len(self.book_ids), dtitle)
|
||||
desc = _('Convert book %(num)d of %(tot)d (%(title)s)') % dict(
|
||||
num=self.i, tot=len(self.book_ids), title=dtitle)
|
||||
|
||||
args = [in_file.name, out_file.name, lrecs]
|
||||
temp_files.append(out_file)
|
||||
@ -209,8 +210,8 @@ class QueueBulk(QProgressDialog):
|
||||
|
||||
msg = '%s' % '\n'.join(res)
|
||||
warning_dialog(self.parent, _('Could not convert some books'),
|
||||
_('Could not convert %d of %d books, because no suitable '
|
||||
'source format was found.') % (len(res), len(self.book_ids)),
|
||||
_('Could not convert %(num)d of %(tot)d books, because no suitable '
|
||||
'source format was found.') % dict(num=len(res), tot=len(self.book_ids)),
|
||||
msg).exec_()
|
||||
self.parent = None
|
||||
self.jobs.reverse()
|
||||
|
@ -308,6 +308,8 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
||||
self.height())
|
||||
self.resize(self.width(), self._calculated_available_height)
|
||||
|
||||
self.build_context_menus()
|
||||
|
||||
for ac in self.iactions.values():
|
||||
try:
|
||||
ac.gui_layout_complete()
|
||||
|
@ -70,10 +70,10 @@ class UpdateNotification(QDialog):
|
||||
self.logo.setPixmap(QPixmap(I('lt.png')).scaled(100, 100,
|
||||
Qt.IgnoreAspectRatio, Qt.SmoothTransformation))
|
||||
self.label = QLabel(('<p>'+
|
||||
_('%s has been updated to version <b>%s</b>. '
|
||||
_('%(app)s has been updated to version <b>%(ver)s</b>. '
|
||||
'See the <a href="http://calibre-ebook.com/whats-new'
|
||||
'">new features</a>.'))%(
|
||||
__appname__, calibre_version))
|
||||
'">new features</a>.'))%dict(
|
||||
app=__appname__, ver=calibre_version))
|
||||
self.label.setOpenExternalLinks(True)
|
||||
self.label.setWordWrap(True)
|
||||
self.setWindowTitle(_('Update available!'))
|
||||
|
@ -492,11 +492,11 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
||||
self.set_page_number(frac)
|
||||
|
||||
def magnification_changed(self, val):
|
||||
tt = _('Make font size %s\nCurrent magnification: %.1f')
|
||||
tt = _('Make font size %(which)s\nCurrent magnification: %(mag).1f')
|
||||
self.action_font_size_larger.setToolTip(
|
||||
tt %(_('larger'), val))
|
||||
tt %dict(which=_('larger'), mag=val))
|
||||
self.action_font_size_smaller.setToolTip(
|
||||
tt %(_('smaller'), val))
|
||||
tt %dict(which=_('smaller'), mag=val))
|
||||
|
||||
def find(self, text, repeat=False, backwards=False):
|
||||
if not text:
|
||||
|
@ -569,9 +569,9 @@ def move_library(oldloc, newloc, parent, callback_on_complete):
|
||||
det = traceback.format_exc()
|
||||
error_dialog(parent, _('Invalid database'),
|
||||
_('<p>An invalid library already exists at '
|
||||
'%s, delete it before trying to move the '
|
||||
'existing library.<br>Error: %s')%(newloc,
|
||||
str(err)), det, show=True)
|
||||
'%(loc)s, delete it before trying to move the '
|
||||
'existing library.<br>Error: %(err)s')%dict(loc=newloc,
|
||||
err=str(err)), det, show=True)
|
||||
callback(None)
|
||||
return
|
||||
else:
|
||||
|
@ -31,9 +31,9 @@ class TestEmail(QDialog, TE_Dialog):
|
||||
if pa:
|
||||
self.to.setText(pa)
|
||||
if opts.relay_host:
|
||||
self.label.setText(_('Using: %s:%s@%s:%s and %s encryption')%
|
||||
(opts.relay_username, unhexlify(opts.relay_password),
|
||||
opts.relay_host, opts.relay_port, opts.encryption))
|
||||
self.label.setText(_('Using: %(un)s:%(pw)s@%(host)s:%(port)s and %(enc)s encryption')%
|
||||
dict(un=opts.relay_username, pw=unhexlify(opts.relay_password),
|
||||
host=opts.relay_host, port=opts.relay_port, enc=opts.encryption))
|
||||
|
||||
def test(self, *args):
|
||||
self.log.setPlainText(_('Sending...'))
|
||||
|
@ -54,12 +54,12 @@ class CSV_XML(CatalogPlugin): # {{{
|
||||
action = None,
|
||||
help = _('The fields to output when cataloging books in the '
|
||||
'database. Should be a comma-separated list of fields.\n'
|
||||
'Available fields: %s,\n'
|
||||
'Available fields: %(fields)s,\n'
|
||||
'plus user-created custom fields.\n'
|
||||
'Example: %s=title,authors,tags\n'
|
||||
'Example: %(opt)s=title,authors,tags\n'
|
||||
"Default: '%%default'\n"
|
||||
"Applies to: CSV, XML output formats")%(', '.join(FIELDS),
|
||||
'--fields')),
|
||||
"Applies to: CSV, XML output formats")%dict(
|
||||
fields=', '.join(FIELDS), opt='--fields')),
|
||||
|
||||
Option('--sort-by',
|
||||
default = 'id',
|
||||
@ -250,12 +250,12 @@ class BIBTEX(CatalogPlugin): # {{{
|
||||
action = None,
|
||||
help = _('The fields to output when cataloging books in the '
|
||||
'database. Should be a comma-separated list of fields.\n'
|
||||
'Available fields: %s.\n'
|
||||
'Available fields: %(fields)s.\n'
|
||||
'plus user-created custom fields.\n'
|
||||
'Example: %s=title,authors,tags\n'
|
||||
'Example: %(opt)s=title,authors,tags\n'
|
||||
"Default: '%%default'\n"
|
||||
"Applies to: BIBTEX output format")%(', '.join(FIELDS),
|
||||
'--fields')),
|
||||
"Applies to: BIBTEX output format")%dict(
|
||||
fields=', '.join(FIELDS), opt='--fields')),
|
||||
|
||||
Option('--sort-by',
|
||||
default = 'id',
|
||||
|
@ -62,7 +62,8 @@ class Tag(object):
|
||||
if self.avg_rating > 0:
|
||||
if tooltip:
|
||||
tooltip = tooltip + ': '
|
||||
tooltip = _('%sAverage rating is %3.1f')%(tooltip, self.avg_rating)
|
||||
tooltip = _('%(tt)sAverage rating is %(rating)3.1f')%dict(
|
||||
tt=tooltip, rating=self.avg_rating)
|
||||
self.tooltip = tooltip
|
||||
self.icon = icon
|
||||
self.category = category
|
||||
|
@ -121,7 +121,7 @@ class FieldMetadata(dict):
|
||||
'datatype':'series',
|
||||
'is_multiple':{},
|
||||
'kind':'field',
|
||||
'name':_('Series'),
|
||||
'name':ngettext('Series', 'Series', 2),
|
||||
'search_terms':['series'],
|
||||
'is_custom':False,
|
||||
'is_category':True,
|
||||
|
@ -92,16 +92,17 @@ def config(defaults=None):
|
||||
' By default all available formats are saved.'))
|
||||
x('template', default=DEFAULT_TEMPLATE,
|
||||
help=_('The template to control the filename and directory structure of the saved files. '
|
||||
'Default is "%s" which will save books into a per-author '
|
||||
'Default is "%(templ)s" which will save books into a per-author '
|
||||
'subdirectory with filenames containing title and author. '
|
||||
'Available controls are: {%s}')%(DEFAULT_TEMPLATE, ', '.join(FORMAT_ARGS)))
|
||||
'Available controls are: {%(controls)s}')%dict(
|
||||
templ=DEFAULT_TEMPLATE, controls=', '.join(FORMAT_ARGS)))
|
||||
x('send_template', default=DEFAULT_SEND_TEMPLATE,
|
||||
help=_('The template to control the filename and directory structure of files '
|
||||
'sent to the device. '
|
||||
'Default is "%s" which will save books into a per-author '
|
||||
'Default is "%(templ)s" which will save books into a per-author '
|
||||
'directory with filenames containing title and author. '
|
||||
'Available controls are: {%s}')%(DEFAULT_SEND_TEMPLATE, ', '.join(FORMAT_ARGS)))
|
||||
|
||||
'Available controls are: {%(controls)s}')%dict(
|
||||
templ=DEFAULT_SEND_TEMPLATE, controls=', '.join(FORMAT_ARGS)))
|
||||
x('asciiize', default=True,
|
||||
help=_('Normally, calibre will convert all non English characters into English equivalents '
|
||||
'for the file names. '
|
||||
|
@ -124,7 +124,8 @@ def render_rating(rating, url_prefix, container='span', prefix=None): # {{{
|
||||
added = 0
|
||||
if prefix is None:
|
||||
prefix = _('Average rating')
|
||||
rstring = xml(_('%s: %.1f stars')% (prefix, rating if rating else 0.0),
|
||||
rstring = xml(_('%(prefix)s: %(rating).1f stars')%dict(
|
||||
prefix=prefix, rating=rating if rating else 0.0),
|
||||
True)
|
||||
ans = ['<%s class="rating">' % (container)]
|
||||
for i in range(5):
|
||||
|
@ -171,9 +171,9 @@ def ACQUISITION_ENTRY(item, version, db, updated, CFM, CKEYS, prefix):
|
||||
no_tag_count=True)))
|
||||
series = item[FM['series']]
|
||||
if series:
|
||||
extra.append(_('SERIES: %s [%s]<br />')%\
|
||||
(xml(series),
|
||||
fmt_sidx(float(item[FM['series_index']]))))
|
||||
extra.append(_('SERIES: %(series)s [%(sidx)s]<br />')%\
|
||||
dict(series=xml(series),
|
||||
sidx=fmt_sidx(float(item[FM['series_index']]))))
|
||||
for key in CKEYS:
|
||||
mi = db.get_metadata(item[CFM['id']['rec_index']], index_is_id=True)
|
||||
name, val = mi.format_field(key)
|
||||
|
@ -96,8 +96,8 @@ html_sidebars = {
|
||||
html_favicon = 'favicon.ico'
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
# relative to this directory. They are copied after the built-in static files,
|
||||
# so a file named "default.css" will overwrite the built-in "default.css".
|
||||
html_static_path = ['resources', '../../../icons/favicon.ico']
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
|
@ -2,16 +2,16 @@
|
||||
|
||||
.. _conversion:
|
||||
|
||||
E-book Conversion
|
||||
Ebook Conversion
|
||||
===================
|
||||
|
||||
|app| has a conversion system that is designed to be very easy to use. Normally, you just
|
||||
add a book to |app|, click convert and |app| will try hard to generate output that is as
|
||||
close as possible to the input. However, |app| accepts a very large number of input formats,
|
||||
not all of which are as suitable as others for conversion to e-books. In the case of
|
||||
not all of which are as suitable as others for conversion to ebooks. In the case of
|
||||
such input formats, or if you just want greater control over the conversion system,
|
||||
|app| has a lot of options to fine tune the conversion process. Note however that |app|'s
|
||||
conversion system is not a substitute for a full blown e-book editor. To edit e-books, I
|
||||
conversion system is not a substitute for a full blown ebook editor. To edit ebooks, I
|
||||
would recommend first converting them to EPUB using |app| and then using a dedicated EPUB editor,
|
||||
like `Sigil <http://code.google.com/p/sigil/>`_ to get the book into perfect shape. You can then
|
||||
use the edited EPUB as input for conversion into other formats in |app|.
|
||||
@ -23,7 +23,7 @@ mouse over it, a tooltip will appear describing the setting.
|
||||
|
||||
.. image:: images/conv_dialog.png
|
||||
:align: center
|
||||
:alt: E-book conversion dialog
|
||||
:alt: Ebook conversion dialog
|
||||
:scale: 50
|
||||
|
||||
.. contents:: Contents
|
||||
@ -75,7 +75,7 @@ The four sub-directories are:
|
||||
input This contains the HTML output by the Input Plugin. Use this to debug the Input Plugin.
|
||||
parsed The result of pre-processing and converting to XHTML the output from the Input Plugin. Use to debug structure detection.
|
||||
structure Post structure detection, but before CSS flattening and font size conversion. Use to debug font size conversion and CSS transforms.
|
||||
processed Just before the e-book is passed to the output plugin. Use to debug the Output Plugin.
|
||||
processed Just before the ebook is passed to the output plugin. Use to debug the Output Plugin.
|
||||
========== =============
|
||||
|
||||
If you want to edit the input document a little before having |app| convert it, the best thing to
|
||||
@ -94,7 +94,7 @@ Look & Feel
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
This group of options controls various aspects of the look and feel of the converted e-book.
|
||||
This group of options controls various aspects of the look and feel of the converted ebook.
|
||||
|
||||
.. _font-size-rescaling:
|
||||
|
||||
@ -209,7 +209,7 @@ Miscellaneous
|
||||
There are a few more options in this section.
|
||||
|
||||
:guilabel:`No text justification`
|
||||
Normally, if the output format supports it, |app| will force the output e-book
|
||||
Normally, if the output format supports it, |app| will force the output ebook
|
||||
to have *justified* text (i.e., a smooth right margin). This option will turn
|
||||
off this behavior, in which case whatever justification is specified in the input document
|
||||
will be used instead.
|
||||
@ -227,7 +227,7 @@ There are a few more options in this section.
|
||||
with "Mikhail Gorbachiov". Also, note that in cases where there are multiple representations
|
||||
of a character (characters shared by Chinese and Japanese for instance) the representation used
|
||||
by the largest number of people will be used (Chinese in the previous example).
|
||||
This option is mainly useful if you are going to view the e-book on a device that does not
|
||||
This option is mainly useful if you are going to view the ebook on a device that does not
|
||||
have support for unicode.
|
||||
|
||||
:guilabel:`Input character encoding`
|
||||
@ -416,7 +416,7 @@ There are a few more options in this section.
|
||||
:guilabel:`Insert metadata as page at start of book`
|
||||
One of the great things about |app| is that it allows you to maintain very complete metadata
|
||||
about all of your books, for example, a rating, tags, comments, etc. This option will create
|
||||
a single page with all this metadata and insert it into the converted e-book, typically just
|
||||
a single page with all this metadata and insert it into the converted ebook, typically just
|
||||
after the cover. Think of it as a way to create your own customised book jacket.
|
||||
|
||||
:guilabel:`Remove first image`
|
||||
@ -432,7 +432,7 @@ Table of Contents
|
||||
When the input document has a Table of Contents in its metadata, |app| will just use that. However,
|
||||
a number of older formats either do not support a metadata based Table of Contents, or individual
|
||||
documents do not have one. In these cases, the options in this section can help you automatically
|
||||
generate a Table of Contents in the converted e-book, based on the actual content in the input document.
|
||||
generate a Table of Contents in the converted ebook, based on the actual content in the input document.
|
||||
|
||||
The first option is :guilabel:`Force use of auto-generated Table of Contents`. By checking this option
|
||||
you can have |app| override any Table of Contents found in the metadata of the input document with the
|
||||
@ -681,7 +681,7 @@ The .cbc file will then contain::
|
||||
two.cbz
|
||||
three.cbz
|
||||
|
||||
|app| will automatically convert this .cbc file into a e-book with a Table of Contents pointing to each entry in comics.txt.
|
||||
|app| will automatically convert this .cbc file into a ebook with a Table of Contents pointing to each entry in comics.txt.
|
||||
|
||||
|
||||
EPUB advanced formatting demo
|
||||
@ -695,5 +695,5 @@ EPUB from the ZIP file are::
|
||||
|
||||
ebook-convert demo.zip .epub -vv --authors "Kovid Goyal" --language en --level1-toc '//*[@class="title"]' --disable-font-rescaling --page-breaks-before / --no-default-epub-cover
|
||||
|
||||
Note that because this file explores the potential of EPUB, most of the advanced formatting is not going to work on readers less capable than |app|'s builtin EPUB viewer.
|
||||
Note that because this file explores the potential of EPUB, most of the advanced formatting is not going to work on readers less capable than |app|'s built-in EPUB viewer.
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
Writing your own plugins to extend |app|'s functionality
|
||||
====================================================================
|
||||
|
||||
|app| has a very modular design. Almost all functionality in |app| comes in the form of plugins. Plugins are used for conversion, for downloading news (though these are called recipes), for various components of the user interface, to connect to different devices, to process files when adding them to |app| and so on. You can get a complete list of all the builtin plugins in |app| by going to :guilabel:`Preferences->Plugins`.
|
||||
|app| has a very modular design. Almost all functionality in |app| comes in the form of plugins. Plugins are used for conversion, for downloading news (though these are called recipes), for various components of the user interface, to connect to different devices, to process files when adding them to |app| and so on. You can get a complete list of all the built-in plugins in |app| by going to :guilabel:`Preferences->Plugins`.
|
||||
|
||||
Here, we will teach you how to create your own plugins to add new features to |app|.
|
||||
|
||||
@ -127,7 +127,7 @@ The actual logic to implement the Interface Plugin Demo dialog.
|
||||
Getting resources from the plugin zip file
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|app|'s plugin loading system defines a couple of builtin functions that allow you to conveniently get files from the plugin zip file.
|
||||
|app|'s plugin loading system defines a couple of built-in functions that allow you to conveniently get files from the plugin zip file.
|
||||
|
||||
**get_resources(name_or_list_of_names)**
|
||||
This function should be called with a list of paths to files inside the zip file. For example to access the file icon.png in
|
||||
|
@ -53,15 +53,15 @@ from the calibre website it will be :file:`/opt/calibre/resources`. These paths
|
||||
|
||||
You should not change the files in this resources folder, as your changes will get overwritten the next time you update |app|. Instead, go to
|
||||
:guilabel:`Preferences->Advanced->Miscellaneous` and click :guilabel:`Open calibre configuration directory`. In this configuration directory, create a sub-folder called resources and place the files you want to override in it. Place the files in the appropriate sub folders, for example place images in :file:`resources/images`, etc.
|
||||
|app| will automatically use your custom file in preference to the builtin one the next time it is started.
|
||||
|app| will automatically use your custom file in preference to the built-in one the next time it is started.
|
||||
|
||||
For example, if you wanted to change the icon for the :guilabel:`Remove books` action, you would first look in the builtin resources folder and see that the relevant file is
|
||||
For example, if you wanted to change the icon for the :guilabel:`Remove books` action, you would first look in the built-in resources folder and see that the relevant file is
|
||||
:file:`resources/images/trash.png`. Assuming you have an alternate icon in PNG format called :file:`mytrash.png` you would save it in the configuration directory as :file:`resources/images/trash.png`. All the icons used by the calibre user interface are in :file:`resources/images` and its sub-folders.
|
||||
|
||||
Customizing |app| with plugins
|
||||
--------------------------------
|
||||
|
||||
|app| has a very modular design. Almost all functionality in |app| comes in the form of plugins. Plugins are used for conversion, for downloading news (though these are called recipes), for various components of the user interface, to connect to different devices, to process files when adding them to |app| and so on. You can get a complete list of all the builtin plugins in |app| by going to :guilabel:`Preferences->Plugins`.
|
||||
|app| has a very modular design. Almost all functionality in |app| comes in the form of plugins. Plugins are used for conversion, for downloading news (though these are called recipes), for various components of the user interface, to connect to different devices, to process files when adding them to |app| and so on. You can get a complete list of all the built-in plugins in |app| by going to :guilabel:`Preferences->Plugins`.
|
||||
|
||||
You can write your own plugins to customize and extend the behavior of |app|. The plugin architecture in |app| is very simple, see the tutorial :ref:`pluginstutorial`.
|
||||
|
||||
|
112
src/calibre/manual/develop.rst
Normal file → Executable file
112
src/calibre/manual/develop.rst
Normal file → Executable file
@ -6,7 +6,7 @@ Setting up a |app| development environment
|
||||
===========================================
|
||||
|
||||
|app| is completely open source, licensed under the `GNU GPL v3 <http://www.gnu.org/copyleft/gpl.html>`_.
|
||||
This means that you are free to download and modify the program to your hearts content. In this section,
|
||||
This means that you are free to download and modify the program to your heart's content. In this section,
|
||||
you will learn how to get a |app| development environment set up on the operating system of your choice.
|
||||
|app| is written primarily in `Python <http://www.python.org>`_ with some C/C++ code for speed and system interfacing.
|
||||
Note that |app| is not compatible with Python 3 and requires at least Python 2.7.
|
||||
@ -18,7 +18,7 @@ Note that |app| is not compatible with Python 3 and requires at least Python 2.7
|
||||
Design philosophy
|
||||
-------------------
|
||||
|
||||
|app| has its roots in the Unix world, which means that it's design is highly modular.
|
||||
|app| has its roots in the Unix world, which means that its design is highly modular.
|
||||
The modules interact with each other via well defined interfaces. This makes adding new features and fixing
|
||||
bugs in |app| very easy, resulting in a frenetic pace of development. Because of its roots, |app| has a
|
||||
comprehensive command line interface for all its functions, documented in :ref:`cli`.
|
||||
@ -28,7 +28,7 @@ For example, adding support for a new device to |app| typically involves writing
|
||||
a device driver plugin. You can browse the
|
||||
`built-in drivers <http://bazaar.launchpad.net/%7Ekovid/calibre/trunk/files/head%3A/src/calibre/devices/>`_. Similarly, adding support
|
||||
for new conversion formats involves writing input/output format plugins. Another example of the modular design is the :ref:`recipe system <news>` for
|
||||
fetching news. For more examples of plugins designed to add features to |app|, see the `plugin index <http://www.mobileread.com/forums/showthread.php?t=118764>`_.
|
||||
fetching news. For more examples of plugins designed to add features to |app|, see the `plugin index <http://www.mobileread.com/forums/showthread.php?p=1362767#post1362767>`_.
|
||||
|
||||
Code layout
|
||||
^^^^^^^^^^^^^^
|
||||
@ -38,15 +38,15 @@ All the |app| python code is in the ``calibre`` package. This package contains t
|
||||
* devices - All the device drivers. Just look through some of the built-in drivers to get an idea for how they work.
|
||||
|
||||
* For details, see: devices.interface which defines the interface supported by device drivers and devices.usbms which
|
||||
defines a generic driver that connects to a USBMS device. All USBMS based drivers in calibre inherit from it.
|
||||
defines a generic driver that connects to a USBMS device. All USBMS based drivers in |app| inherit from it.
|
||||
|
||||
* ebooks - All the ebook conversion/metadata code. A good starting point is ``calibre.ebooks.conversion.cli`` which is the
|
||||
module powering the :command:`ebook-convert` command. The conversion process is controlled via conversion.plumber.
|
||||
The format independent code is all in ebooks.oeb and the format dependent stuff is in ebooks.format_name.
|
||||
The format independent code is all in ebooks.oeb and the format dependent code is in ebooks.format_name.
|
||||
|
||||
* Metadata reading writing and downloading is all in ebooks.metadata
|
||||
* Metadata reading, writing, and downloading is all in ebooks.metadata
|
||||
|
||||
* library - The database backed and the content server. See library.database2 for the interface to the calibre library. library.server is the calibre Content Server.
|
||||
* library - The database back-end and the content server. See library.database2 for the interface to the |app| library. library.server is the |app| Content Server.
|
||||
* gui2 - The Graphical User Interface. GUI initialization happens in gui2.main and gui2.ui. The ebook-viewer is in gui2.viewer.
|
||||
|
||||
If you need help understanding the code, post in the `development forum <http://www.mobileread.com/forums/forumdisplay.php?f=240>`_
|
||||
@ -55,8 +55,8 @@ and you will most likely get help from one of |app|'s many developers.
|
||||
Getting the code
|
||||
------------------
|
||||
|
||||
|app| uses `Bazaar <http://bazaar-vcs.org/>`_ a distributed version control system. Bazaar is available on all the platforms |app| supports.
|
||||
After installing Bazaar, you can get the calibre source code with the command::
|
||||
|app| uses `Bazaar <http://bazaar-vcs.org/>`_, a distributed version control system. Bazaar is available on all the platforms |app| supports.
|
||||
After installing Bazaar, you can get the |app| source code with the command::
|
||||
|
||||
bzr branch lp:calibre
|
||||
|
||||
@ -68,7 +68,7 @@ to the latest code, use the command::
|
||||
Submitting your changes to be included
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If you only plan to only make a few small changes, you can make your changes and create a
|
||||
If you only plan to make a few small changes, you can make your changes and create a
|
||||
"merge directive" which you can then attach to a ticket in the |app| bug tracker for consideration. To do
|
||||
this, make your changes, then run::
|
||||
|
||||
@ -76,11 +76,11 @@ this, make your changes, then run::
|
||||
bzr send -o my-changes
|
||||
|
||||
This will create a :file:`my-changes` file in the current directory,
|
||||
simply attach that to a ticket on the |app| `bug tracker <http://calibre-ebook.com/bugs>`_.
|
||||
simply attach that to a ticket on the |app| `bug tracker <https://bugs.launchpad.net/calibre>`_.
|
||||
|
||||
If you plan to do a lot of development on |app|, then the best method is to create a
|
||||
`Launchpad <http://launchpad.net>`_ account. Once you have the account, you can use it to register
|
||||
your bzr branch created by the `bzr branch` command above with the |app| project. First run the
|
||||
`Launchpad <http://launchpad.net>`_ account. Once you have an account, you can use it to register
|
||||
your bzr branch created by the `bzr branch` command above. First run the
|
||||
following command to tell bzr about your launchpad account::
|
||||
|
||||
bzr launchpad-login your_launchpad_username
|
||||
@ -88,31 +88,31 @@ following command to tell bzr about your launchpad account::
|
||||
Now, you have to setup SSH access to Launchpad. First create an SSH public/private keypair. Then upload
|
||||
the public key to Launchpad by going to your Launchpad account page. Instructions for setting up the
|
||||
private key in bzr are at http://bazaar-vcs.org/Bzr_and_SSH. Now you can upload your branch to the |app|
|
||||
project in Launchapd by following the instructions at https://help.launchpad.net/Code/UploadingABranch.
|
||||
Now whenever you commit changes to your branch with the command::
|
||||
project in Launchpad by following the instructions at https://help.launchpad.net/Code/UploadingABranch.
|
||||
Whenever you commit changes to your branch with the command::
|
||||
|
||||
bzr commit -m "Comment describing your change"
|
||||
|
||||
I can merge it directly from you branch into the main |app| source tree. You should also keep an eye on the |app|
|
||||
Kovid can merge it directly from your branch into the main |app| source tree. You should also keep an eye on the |app|
|
||||
`development forum <http://www.mobileread.com/forums/forumdisplay.php?f=240>`. Before making major changes, you should
|
||||
discuss them in the forum or contact Kovid directly (his email address is all over the source code).
|
||||
|
||||
Windows development environment
|
||||
---------------------------------
|
||||
|
||||
Install |app| normally, using the windows installer. Then, open a Command Prompt and change to
|
||||
the previously checked out calibre code directory, for example::
|
||||
Install |app| normally, using the Windows installer. Then open a Command Prompt and change to
|
||||
the previously checked out |app| code directory. For example::
|
||||
|
||||
cd C:\Users\kovid\work\calibre
|
||||
|
||||
calibre is the directory that contains the src and resources sub directories.
|
||||
calibre is the directory that contains the src and resources sub-directories.
|
||||
|
||||
The next step is to set the environment variable ``CALIBRE_DEVELOP_FROM`` to the absolute path to the src directory.
|
||||
So, following the example above, it would be ``C:\Users\kovid\work\calibre\src``. A short
|
||||
`guide <http://docs.python.org/using/windows.html#excursus-setting-environment-variables>`_ to setting environment
|
||||
variables on windows.
|
||||
The next step is to set the environment variable ``CALIBRE_DEVELOP_FROM`` to the absolute path of the src directory.
|
||||
So, following the example above, it would be ``C:\Users\kovid\work\calibre\src``. `Here is a short
|
||||
guide <http://docs.python.org/using/windows.html#excursus-setting-environment-variables>`_ to setting environment
|
||||
variables on Windows.
|
||||
|
||||
Once you have set the environment variable, open a new Command Prompt and check that it was correctly set by using
|
||||
Once you have set the environment variable, open a new command prompt and check that it was correctly set by using
|
||||
the command::
|
||||
|
||||
echo %CALIBRE_DEVELOP_FROM%
|
||||
@ -129,14 +129,14 @@ near the top of the file. Now run the command :command:`calibredb`. The very fir
|
||||
OS X development environment
|
||||
------------------------------
|
||||
|
||||
Install |app| normally, using the provided .dmg. Then, open a Terminal and change to
|
||||
the previously checked out calibre code directory, for example::
|
||||
Install |app| normally using the provided .dmg. Then open a Terminal and change to
|
||||
the previously checked out |app| code directory, for example::
|
||||
|
||||
cd /Users/kovid/work/calibre
|
||||
|
||||
calibre is the directory that contains the src and resources sub directories. Ensure you have installed the |app| commandline tools via :guilabel:Preferences->Advanced->Miscellaneous in the |app| GUI.
|
||||
calibre is the directory that contains the src and resources sub-directories. Ensure you have installed the |app| commandline tools via :guilabel:Preferences->Advanced->Miscellaneous in the |app| GUI.
|
||||
|
||||
The next step is to set the environment variable ``CALIBRE_DEVELOP_FROM`` to the absolute path to the src directory.
|
||||
The next step is to set the environment variable ``CALIBRE_DEVELOP_FROM`` to the absolute path of the src directory.
|
||||
So, following the example above, it would be ``/Users/kovid/work/calibre/src``. Apple
|
||||
`documentation <http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPRuntimeConfig/Articles/EnvironmentVars.html#//apple_ref/doc/uid/20002093-BCIJIJBH>`_
|
||||
on how to set environment variables.
|
||||
@ -158,21 +158,21 @@ near the top of the file. Now run the command :command:`calibredb`. The very fir
|
||||
Linux development environment
|
||||
------------------------------
|
||||
|
||||
|app| is primarily developed on linux. You have two choices in setting up the development environment. You can install the
|
||||
|app| is primarily developed on Linux. You have two choices in setting up the development environment. You can install the
|
||||
|app| binary as normal and use that as a runtime environment to do your development. This approach is similar to that
|
||||
used in windows and OS X. Alternatively, you can install |app| from source. Instructions for setting up a development
|
||||
environment from source are in the INSTALL file in the source tree. Here we will address using the binary a runtime, which is the
|
||||
used in Windows and OS X. Alternatively, you can install |app| from source. Instructions for setting up a development
|
||||
environment from source are in the INSTALL file in the source tree. Here we will address using the binary at runtime, which is the
|
||||
recommended method.
|
||||
|
||||
Install the |app| using the binary installer. Then open a terminal and change to the previously checked out |app| code directory, for example::
|
||||
|
||||
cd /home/kovid/work/calibre
|
||||
|
||||
calibre is the directory that contains the src and resources sub directories.
|
||||
calibre is the directory that contains the src and resources sub-directories.
|
||||
|
||||
The next step is to set the environment variable ``CALIBRE_DEVELOP_FROM`` to the absolute path to the src directory.
|
||||
The next step is to set the environment variable ``CALIBRE_DEVELOP_FROM`` to the absolute path of the src directory.
|
||||
So, following the example above, it would be ``/home/kovid/work/calibre/src``. How to set environment variables depends on
|
||||
your linux distribution and what shell you are using.
|
||||
your Linux distribution and what shell you are using.
|
||||
|
||||
Once you have set the environment variable, open a new terminal and check that it was correctly set by using
|
||||
the command::
|
||||
@ -191,10 +191,10 @@ near the top of the file. Now run the command :command:`calibredb`. The very fir
|
||||
Having separate "normal" and "development" |app| installs on the same computer
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
The calibre source tree is very stable, it rarely breaks, but if you feel the need to run from source on a separate
|
||||
test library and run the released calibre version with your everyday library, you can achieve this easily using
|
||||
.bat files or shell scripts to launch |app|. The example below shows how to do this on windows using .bat files (the
|
||||
instructions for other platforms are the same, just use a BASH script instead of a .bat file)
|
||||
The |app| source tree is very stable and rarely breaks, but if you feel the need to run from source on a separate
|
||||
test library and run the released |app| version with your everyday library, you can achieve this easily using
|
||||
.bat files or shell scripts to launch |app|. The example below shows how to do this on Windows using .bat files (the
|
||||
instructions for other platforms are the same, just use a shell script instead of a .bat file)
|
||||
|
||||
To launch the release version of |app| with your everyday library:
|
||||
|
||||
@ -211,9 +211,9 @@ calibre-dev.bat::
|
||||
Debugging tips
|
||||
----------------
|
||||
|
||||
Running calibre code in a python debugger is not easy, unless you install from source on linux. However, python is a
|
||||
dynamically typed language with excellent facilities for introspection. I wrote the core calibre code without once
|
||||
using a debugger. There are two main strategies to debug calibre code:
|
||||
Running |app| code in a python debugger is not easy unless you install from source on Linux. However, Python is a
|
||||
dynamically typed language with excellent facilities for introspection. Kovid wrote the core |app| code without once
|
||||
using a debugger. There are two main strategies to debug |app| code:
|
||||
|
||||
Using an interactive python interpreter
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -223,15 +223,15 @@ You can insert the following two lines of code to start an interactive python se
|
||||
from calibre import ipython
|
||||
ipython(locals())
|
||||
|
||||
When running from the command line, this will start an interactive python interpreter with access to all
|
||||
When running from the command line, this will start an interactive Python interpreter with access to all
|
||||
locally defined variables (variables in the local scope). The interactive prompt even has TAB completion
|
||||
for object properties and you can use the various python facilities for introspection, such as
|
||||
for object properties and you can use the various Python facilities for introspection, such as
|
||||
:func:`dir`, :func:`type`, :func:`repr`, etc.
|
||||
|
||||
Using print statements
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
This is my favorite way to debug. Simply insert print statements at points of interest and run your program in the
|
||||
This is Kovid's favorite way to debug. Simply insert print statements at points of interest and run your program in the
|
||||
terminal. For example, you can start the GUI from the terminal as::
|
||||
|
||||
calibre-debug -g
|
||||
@ -240,11 +240,11 @@ Similarly, you can start the ebook-viewer as::
|
||||
|
||||
calibre-debug -w /path/to/file/to/be/viewed
|
||||
|
||||
Executing arbitrary scripts in the calibre python environment
|
||||
Executing arbitrary scripts in the |app| python environment
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The :command:`calibre-debug` command provides a couple of handy switches to execute your own
|
||||
code, with access to the calibre modules::
|
||||
code, with access to the |app| modules::
|
||||
|
||||
calibre-debug -c "some python code"
|
||||
|
||||
@ -252,33 +252,33 @@ is great for testing a little snippet of code on the command line. It works in t
|
||||
|
||||
calibre-debug -e myscript.py
|
||||
|
||||
can be used to execute your own python script. It works in the same way as passing the script to the python interpreter, except
|
||||
can be used to execute your own Python script. It works in the same way as passing the script to the Python interpreter, except
|
||||
that the calibre environment is fully initialized, so you can use all the calibre code in your script.
|
||||
|
||||
|
||||
Using calibre in your projects
|
||||
Using |app| in your projects
|
||||
----------------------------------------
|
||||
|
||||
It is possible to directly use calibre functions/code in your python project. Two ways exist to do this:
|
||||
It is possible to directly use |app| functions/code in your Python project. Two ways exist to do this:
|
||||
|
||||
Binary install of calibre
|
||||
Binary install of |app|
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If you have a binary install of calibre, you can use the python interpreter bundled with calibre, like this::
|
||||
If you have a binary install of |app|, you can use the Python interpreter bundled with |app|, like this::
|
||||
|
||||
calibre-debug -e /path/to/your/python/script.py
|
||||
|
||||
Source install on linux
|
||||
Source install on Linux
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
In addition to using the above technique, if you do a source install on linux,
|
||||
you can also directly import calibre, as follows::
|
||||
In addition to using the above technique, if you do a source install on Linux,
|
||||
you can also directly import |app|, as follows::
|
||||
|
||||
import init_calibre
|
||||
import calibre
|
||||
|
||||
print calibre.__version__
|
||||
|
||||
It is essential that you import the init_calibre module before any other calibre modules/packages as
|
||||
it sets up the interpreter to run calibre code.
|
||||
It is essential that you import the init_calibre module before any other |app| modules/packages as
|
||||
it sets up the interpreter to run |app| code.
|
||||
|
||||
|
@ -9,7 +9,7 @@ Frequently Asked Questions
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
E-book Format Conversion
|
||||
Ebook Format Conversion
|
||||
-------------------------
|
||||
.. contents:: Contents
|
||||
:depth: 1
|
||||
@ -112,8 +112,8 @@ How can I help get my device supported in |app|?
|
||||
If your device appears as a USB disk to the operating system, adding support for it to |app| is very easy.
|
||||
We just need some information from you:
|
||||
|
||||
* What e-book formats does your device support?
|
||||
* Is there a special directory on the device in which all e-book files should be placed?
|
||||
* What ebook formats does your device support?
|
||||
* Is there a special directory on the device in which all ebook files should be placed?
|
||||
* We also need information about your device that |app| will collect automatically. First, if your
|
||||
device supports SD cards, insert them. Then connect your device. In calibre go to :guilabel:`Preferences->Advanced->Miscellaneous`
|
||||
and click the "Debug device detection" button. This will create some debug output. Copy it to a file
|
||||
@ -259,7 +259,7 @@ This method only works on Windows XP and higher, and OS X 10.5 and higher. Linux
|
||||
How do I use |app| with my Android phone?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
First install the WordPlayer e-book reading app from the Android Marketplace onto you phone. Then simply plug your phone into the computer with a USB cable. |app| should automatically detect the phone and then you can transfer books to it by clicking the Send to Device button. |app| does not have support for every single androind device out there, so if you would like to have support for your device added, follow the instructions above for getting your device supported in |app|.
|
||||
First install the WordPlayer ebook reading app from the Android Marketplace onto you phone. Then simply plug your phone into the computer with a USB cable. |app| should automatically detect the phone and then you can transfer books to it by clicking the Send to Device button. |app| does not have support for every single androind device out there, so if you would like to have support for your device added, follow the instructions above for getting your device supported in |app|.
|
||||
|
||||
Can I access my |app| books using the web browser in my Kindle or other reading device?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@ -442,16 +442,16 @@ Miscellaneous
|
||||
Why the name calibre?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Take your pick:
|
||||
* Convertor And LIBRary for E-books
|
||||
* Convertor And LIBRary for Ebooks
|
||||
* A high *calibre* product
|
||||
* A tribute to the SONY Librie which was the first e-ink based e-book reader
|
||||
* A tribute to the SONY Librie which was the first e-ink based ebook reader
|
||||
* My wife chose it ;-)
|
||||
|
||||
|app| is pronounced as cal-i-ber *not* ca-li-bre. If you're wondering, |app| is the British/commonwealth spelling for caliber. Being Indian, that's the natural spelling for me.
|
||||
|
||||
Why does |app| show only some of my fonts on OS X?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|app| embeds fonts in ebook files it creates. E-book files support embedding only TrueType (.ttf) fonts. Most fonts on OS X systems are in .dfont format, thus they cannot be embedded. |app| shows only TrueType fonts found on your system. You can obtain many TrueType fonts on the web. Simply download the .ttf files and add them to the Library/Fonts directory in your home directory.
|
||||
|app| embeds fonts in ebook files it creates. Ebook files support embedding only TrueType (.ttf) fonts. Most fonts on OS X systems are in .dfont format, thus they cannot be embedded. |app| shows only TrueType fonts found on your system. You can obtain many TrueType fonts on the web. Simply download the .ttf files and add them to the Library/Fonts directory in your home directory.
|
||||
|
||||
|app| is not starting on Windows?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@ -462,7 +462,7 @@ There can be several causes for this:
|
||||
* Uninstall calibre
|
||||
* Reboot your computer
|
||||
* Re-install calibre. But do not start calibre from the installation wizard.
|
||||
* Temporarily disable your antivirus program (disconnect from the internet before doing so, to be safe)
|
||||
* Temporarily disable your antivirus program (disconnect from the Internet before doing so, to be safe)
|
||||
* Look inside the folder you chose for your calibre library. If you see a file named metadata.db, delete it.
|
||||
* Start calibre
|
||||
* From now on you should be able to start calibre normally.
|
||||
@ -515,7 +515,7 @@ menu, choose "Validate fonts".
|
||||
I downloaded the installer, but it is not working?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Downloading from the internet can sometimes result in a corrupted download. If the |app| installer you downloaded is not opening, try downloading it again. If re-downloading it does not work, download it from `an alternate location <http://sourceforge.net/projects/calibre/files/>`_. If the installer still doesn't work, then something on your computer is preventing it from running.
|
||||
Downloading from the Internet can sometimes result in a corrupted download. If the |app| installer you downloaded is not opening, try downloading it again. If re-downloading it does not work, download it from `an alternate location <http://sourceforge.net/projects/calibre/files/>`_. If the installer still doesn't work, then something on your computer is preventing it from running.
|
||||
|
||||
* Try temporarily disabling your antivirus program (Microsoft Security Essentials, or Kaspersky or Norton or McAfee or whatever). This is most likely the culprit if the upgrade process is hanging in the middle.
|
||||
* Try rebooting your computer and running a registry cleaner like `Wise registry cleaner <http://www.wisecleaner.com>`_.
|
||||
@ -539,7 +539,7 @@ If you want to backup the |app| configuration/plugins, you have to backup the co
|
||||
|
||||
How do I use purchased EPUB books with |app|?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Most purchased EPUB books have `DRM <http://wiki.mobileread.com/wiki/DRM>`_. This prevents |app| from opening them. You can still use |app| to store and transfer them to your e-book reader. First, you must authorize your reader on a windows machine with Adobe Digital Editions. Once this is done, EPUB books transferred with |app| will work fine on your reader. When you purchase an epub book from a website, you will get an ".acsm" file. This file should be opened with Adobe Digital Editions, which will then download the actual ".epub" e-book. The e-book file will be stored in the folder "My Digital Editions", from where you can add it to |app|.
|
||||
Most purchased EPUB books have `DRM <http://drmfree.calibre-ebook.com/about#drm>`_. This prevents |app| from opening them. You can still use |app| to store and transfer them to your ebook reader. First, you must authorize your reader on a windows machine with Adobe Digital Editions. Once this is done, EPUB books transferred with |app| will work fine on your reader. When you purchase an epub book from a website, you will get an ".acsm" file. This file should be opened with Adobe Digital Editions, which will then download the actual ".epub" ebook. The ebook file will be stored in the folder "My Digital Editions", from where you can add it to |app|.
|
||||
|
||||
I am getting a "Permission Denied" error?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
12
src/calibre/manual/glossary.rst
Normal file → Executable file
12
src/calibre/manual/glossary.rst
Normal file → Executable file
@ -6,25 +6,25 @@ Glossary
|
||||
.. glossary::
|
||||
|
||||
RSS
|
||||
**RSS** *(Really Simple Syndication)* is a web feed format that is used to publish frequently updated content, like news articles, blog posts, etc. It is a format that is particularly suited to being read by computers, and is therefore the preferred way of getting content from the web into an e-book. There are many other feed formats in use on the internet, and |app| understands most of them. In particular, it has good support for the *ATOM* format, which is commonly used for blogs.
|
||||
**RSS** *(Really Simple Syndication)* is a web feed format that is used to publish frequently updated content, like news articles, blog posts, etc. It is a format that is particularly suited to being read by computers, and is therefore the preferred way of getting content from the web into an ebook. There are many other feed formats in use on the Internet, and |app| understands most of them. In particular, it has good support for the *ATOM* format, which is commonly used for blogs.
|
||||
|
||||
recipe
|
||||
A recipe is a set of instructions that teach |app| how to convert an online news source, like a magazine or a blog into an e-book. A recipe, is essentially `python <http://www.python.org>`_ code. As such, it is capable of converting arbitrarily complex news sources into e-books. At the simplest level, it is just a set of variables such as URLs that give |app| enough information to go out onto the internet and download the news.
|
||||
A recipe is a set of instructions that teach |app| how to convert an online news source, such as a magazine or a blog, into an ebook. A recipe is essentially `Python <http://www.python.org>`_ code. As such, it is capable of converting arbitrarily complex news sources into ebooks. At the simplest level, it is just a set of variables, such as URLs, that give |app| enough information to go out onto the Internet and download the news.
|
||||
|
||||
HTML
|
||||
**HTML** *(Hyper Text Mark-Up Language)*, a subset of Standard Generalized Mark-Up Language (SGML) for electronic publishing, the specific standard used for the World Wide Web.
|
||||
**HTML** *(Hyper Text Mark-Up Language)*, a subset of Standard Generalized Mark-Up Language (SGML) for electronic publishing, is the specific standard used for the World Wide Web.
|
||||
|
||||
CSS
|
||||
**CSS** *(Cascading Style Sheets)* a language used to describe how an :term:`HTML` document should be rendered (visual styling).
|
||||
**CSS** *(Cascading Style Sheets)* is a language used to describe how an :term:`HTML` document should be rendered (visual styling).
|
||||
|
||||
API
|
||||
**API** *(Application Programming Interface)* is a source code interface that a library provides to support requests for services to be made of it by computer programs.
|
||||
|
||||
LRF
|
||||
**LRF** The e-book format that is read by the SONY e-book readers.
|
||||
**LRF** The ebook format that is read by the SONY ebook readers.
|
||||
|
||||
URL
|
||||
**URL** *(Uniform Resource Locator)* for example: ``http://example.com``
|
||||
|
||||
regexp
|
||||
**Regular expressions** provide a concise and flexible means for identifying strings of text of interest, such as particular characters, words, or patterns of characters. See `regexp syntax <http://docs.python.org/lib/re-syntax.html>`_ for the syntax of regular expressions used in python.
|
||||
**Regular expressions** provide a concise and flexible means for identifying strings of text of interest, such as particular characters, words, or patterns of characters. See `regexp syntax <http://docs.python.org/lib/re-syntax.html>`_ for the syntax of regular expressions used in Python.
|
||||
|
208
src/calibre/manual/gui.rst
Normal file → Executable file
208
src/calibre/manual/gui.rst
Normal file → Executable file
@ -9,10 +9,11 @@ The Graphical User Interface *(GUI)* provides access to all
|
||||
library management and ebook format conversion features. The basic workflow
|
||||
for using |app| is to first add books to the library from your hard disk.
|
||||
|app| will automatically try to read metadata from the books and add them
|
||||
to its internal database. Once they are in the database, you can perform a various
|
||||
to its internal database. Once they are in the database, you can perform various
|
||||
:ref:`actions` on them that include conversion from one format to another,
|
||||
transfer to the reading device, viewing on your computer, editing metadata, including covers, etc.
|
||||
Note that |app| creates copies of the files you add to it, your original files are left untouched.
|
||||
transfer to the reading device, viewing on your computer, and editing metadata.
|
||||
The latter includes modifying the cover, description, and tags among other details.
|
||||
Note that |app| creates copies of the files you add to it. Your original files are left untouched.
|
||||
|
||||
The interface is divided into various sections:
|
||||
|
||||
@ -42,25 +43,25 @@ Add books
|
||||
.. |adbi| image:: images/add_books.png
|
||||
:class: float-right-img
|
||||
|
||||
|adbi| The :guilabel:`Add books` action has five variations, accessed by the clicking the down arrow on the right side of the button.
|
||||
|adbi| The :guilabel:`Add books` action has six variations accessed by the clicking the down arrow on the right side of the button.
|
||||
|
||||
|
||||
1. **Add books from a single directory**: Opens a file chooser dialog and allows you to specify which books in a directory should be added. This action is *context sensitive*, i.e. it depends on which :ref:`catalog <catalogs>` you have selected. If you have selected the :guilabel:`Library`, books will be added to the library. If you have selected the ebook reader device, the books will be uploaded to the device, and so on.
|
||||
|
||||
2. **Add books from directories, including sub-directories (One book per directory, assumes every ebook file is the same book in a different format)**: Allows you to choose a directory. The directory and all its sub-directories are scanned recursively and any ebooks found are added to the library. The algorithm assumes that each directory contains a single book. All ebook files in a directory are assumedto be the same book in different formats. This action is the inverse of the :ref:`Save to disk <save_to_disk_multiple>` action, i.e. you can :guilabel:`Save to disk`, delete the books and re-add them with no lost information (except date).
|
||||
2. **Add books from directories, including sub-directories (One book per directory, assumes every ebook file is the same book in a different format)**: Allows you to choose a directory. The directory and all its sub-directories are scanned recursively, and any ebooks found are added to the library. |app| assumes that each directory contains a single book. All ebook files in a directory are assumed to be the same book in different formats. This action is the inverse of the :ref:`Save to disk <save_to_disk_multiple>` action, i.e. you can :guilabel:`Save to disk`, delete the books and re-add them with no lost information except for the date.
|
||||
|
||||
3. **Add books directories, including sub-directories (Multiple books per directory, assumes every ebook file is a different book)**: Allows you to choose a directory. The directory and all its sub-directories are scanned recursively and any ebooks found are added to the library. The algorithm assumes that each directory contains many books. All ebook files with the same name in a directory are assumed to be the same book in different formats. Ebooks with different names are added as different books. This action is the inverse of the :ref:`Save to disk <save_to_disk_single>` action, i.e. you can :guilabel:`Save to disk`, delete the books and re-add them with no lost information (except date).
|
||||
3. **Add books from directories, including sub-directories (Multiple books per directory, assumes every ebook file is a different book)**: Allows you to choose a directory. The directory and all its sub-directories are scanned recursively and any ebooks found are added to the library. |app| assumes that each directory contains many books. All ebook files with the same name in a directory are assumed to be the same book in different formats. Ebooks with different names are added as different books. This action is the inverse of the :ref:`Save to disk <save_to_disk_single>` action, i.e. you can :guilabel:`Save to disk`, delete the books and re-add them with no lost information except for the date.
|
||||
|
||||
4. **Add empty book. (Book Entry with blank formats)**: Allows you to create a blank book record. This can be used to then manually fill out the information about a book that you may not have yet in your collection.
|
||||
4. **Add empty book. (Book Entry with no formats)**: Allows you to create a blank book record. This can be used to then manually fill out the information about a book that you may not have yet in your collection.
|
||||
|
||||
5. **Add by ISBN**: Allows you to add one or more books by entering just their ISBN into a list or pasting the list of ISBNs from your clipboard.
|
||||
5. **Add from ISBN**: Allows you to add one or more books by entering their ISBNs.
|
||||
|
||||
6. **Add files to selected book records**: Allows you to add or update the files associated with an existing book in your library.
|
||||
|
||||
The :guilabel:`Add books` action can read metadata from a wide variety of e-book formats. In addition it tries to guess metadata from the filename.
|
||||
The :guilabel:`Add books` action can read metadata from a wide variety of ebook formats. In addition, it tries to guess metadata from the filename.
|
||||
See the :ref:`config_filename_metadata` section, to learn how to configure this.
|
||||
|
||||
To add an additional format for an existing book, use the :ref:`edit_meta_information` action.
|
||||
To add an additional format for an existing book use the :ref:`edit_meta_information` action.
|
||||
|
||||
.. _edit_meta_information:
|
||||
|
||||
@ -69,41 +70,41 @@ Edit metadata
|
||||
.. |emii| image:: images/edit_meta_information.png
|
||||
:class: float-right-img
|
||||
|
||||
|emii| The :guilabel:`Edit metadata` action has six variations, which can be accessed by clicking the down arrow on the right side of the button.
|
||||
|emii| The :guilabel:`Edit metadata` action has four variations which can be accessed by clicking the down arrow on the right side of the button.
|
||||
|
||||
1. **Edit metadata individually**: This allows you to edit the metadata of books one-by-one, with the option of fetching metadata, including covers from the internet. It also allows you to add/remove particular ebook formats from a book.
|
||||
2. **Edit metadata in bulk**: This allows you to edit common metadata fields for large numbers of books simulataneously. It operates on all the books you have selected in the :ref:`Library view <search_sort>`.
|
||||
3. **Download metadata and covers**: Downloads metadata and covers (if available), for the books that are selected in the book list.
|
||||
4. **Merge Book Records**: Gives you the capability of merging the metadata and formats of two or more book records together. You can choose to either delete or keep the records that were not clicked first.
|
||||
1. **Edit metadata individually**: Allows you to edit the metadata of books one-by-one with the option of fetching metadata, including covers, from the Internet. It also allows you to add or remove particular ebook formats from a book.
|
||||
2. **Edit metadata in bulk**: Allows you to edit common metadata fields for large numbers of books simulataneously. It operates on all the books you have selected in the :ref:`Library view <search_sort>`.
|
||||
3. **Download metadata and covers**: Downloads metadata and covers (if available) for the books that are selected in the book list.
|
||||
4. **Merge book records**: Gives you the capability of merging the metadata and formats of two or more book records. You can choose to either delete or keep the records that were not clicked first.
|
||||
|
||||
For more details see :ref:`metadata`.
|
||||
|
||||
.. _convert_ebooks:
|
||||
|
||||
Convert e-books
|
||||
Convert books
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
.. |cei| image:: images/convert_ebooks.png
|
||||
:class: float-right-img
|
||||
|
||||
|cei| Ebooks can be converted from a number of formats into whatever format your e-book reader prefers.
|
||||
Note that ebooks you purchase will typically have `Digital Rights Management <http://bugs.calibre-ebook.com/wiki/DRM>`_ *(DRM)*.
|
||||
|app| will not convert these ebooks. For many DRM formats, it is easy to remove the DRM, but as this may be illegal,
|
||||
you have to find tools to liberate your books yourself and then use |app| to convert them.
|
||||
|cei| Ebooks can be converted from a number of formats into whatever format your ebook reader prefers.
|
||||
Many ebooks available for purchase will be protected by `Digital Rights Management <http://drmfree.calibre-ebook.com/about#drm>`_ *(DRM)* technology.
|
||||
|app| will not convert these ebooks. It is easy to remove the DRM from many formats, but as this may be illegal,
|
||||
you will have to find tools to liberate your books yourself and then use |app| to convert them.
|
||||
|
||||
For most people, conversion should be a simple 1-click affair. But if you want to learn more about the conversion process, see :ref:`conversion`.
|
||||
For most people, conversion should be a simple one-click affair. If you want to learn more about the conversion process, see :ref:`conversion`.
|
||||
|
||||
The :guilabel:`Convert E-books` action has three variations, accessed by the arrow next to the button.
|
||||
The :guilabel:`Convert books` action has three variations, accessed by the arrow next to the button.
|
||||
|
||||
1. **Convert individually**: This will allow you to specify conversion options to customize the conversion of each selected ebook.
|
||||
1. **Convert individually**: Allows you to specify conversion options to customize the conversion of each selected ebook.
|
||||
|
||||
2. **Bulk convert**: This allows you to specify options only once to convert a number of ebooks in bulk.
|
||||
2. **Bulk convert**: Allows you to specify options only once to convert a number of ebooks in bulk.
|
||||
|
||||
3. **Create catalog**: This action allows you to generate a complete listing with all metadata of the books in your library,
|
||||
in several formats, like XML, CSV, BiBTeX, EPUB and MOBI. The catalog will contain all the books showing in the library view currently,
|
||||
so you can use the search features to limit the books to be catalogued. In addition, if you select multiple books using the mouse,
|
||||
only those books will be added to the catalog. If you generate the catalog in an e-book format such as EPUB or MOBI,
|
||||
the next time you connect your e-book reader, the catalog will be automatically sent to the device.
|
||||
For details on how catalogs work, see `here <http://www.mobileread.com/forums/showthread.php?p=755468#post755468>`.
|
||||
3. **Create a catalog of the books in your calibre library**: Allows you to generate a complete listing of the books in your library, including all metadata,
|
||||
in several formats such as XML, CSV, BiBTeX, EPUB and MOBI. The catalog will contain all the books currently showing in the library view.
|
||||
This allows you to use the search features to limit the books to be catalogued. In addition, if you select multiple books using the mouse,
|
||||
only those books will be added to the catalog. If you generate the catalog in an ebook format such as EPUB or MOBI,
|
||||
the next time you connect your ebook reader the catalog will be automatically sent to the device.
|
||||
For more information on how catalogs work, read the `catalog creation tutorial <http://www.mobileread.com/forums/showthread.php?p=755468#post755468>`_at MobileRead.
|
||||
|
||||
.. _view:
|
||||
|
||||
@ -112,7 +113,7 @@ View
|
||||
.. |vi| image:: images/view.png
|
||||
:class: float-right-img
|
||||
|
||||
|vi| The :guilabel:`View` action displays the book in an ebook viewer program. |app| has a builtin viewer for the most e-book formats.
|
||||
|vi| The :guilabel:`View` action displays the book in an ebook viewer program. |app| has a built-in viewer for many ebook formats.
|
||||
For other formats it uses the default operating system application. You can configure which formats should open with the internal viewer via
|
||||
Preferences->Behavior. If a book has more than one format, you can view a particular format by clicking the down arrow
|
||||
on the right of the :guilabel:`View` button.
|
||||
@ -130,11 +131,11 @@ Send to device
|
||||
1. **Send to main memory**: The selected books are transferred to the main memory of the ebook reader.
|
||||
2. **Send to card (A)**: The selected books are transferred to the storage card (A) on the ebook reader.
|
||||
3. **Send to card (B)**: The selected books are transferred to the storage card (B) on the ebook reader.
|
||||
4. **Send and delete from library>**: The selected books are transferred to the selected storage location on the device, and then **deleted** from the Library.
|
||||
5. **Send Specific format>**: The selected books are transferred to the selected storage location on the device, in the format that you specify.
|
||||
6. **Eject device**: The device is detached from |app|.
|
||||
7. **Set default send to device action>**: This action allows you to Specify which of the option 1) through 6) above will be the default action when you click the main button.
|
||||
8. **Fetch Annotations**: This is an experimental action which will transfer annotations you may have made on an ebook on your device, and add those annotations to the comments metadata of the book in the |app| library
|
||||
4. **Send specific format to**: The selected books are transferred to the selected storage location on the device, in the format that you specify.
|
||||
5. **Eject device**: Detaches the device from |app|.
|
||||
6. **Set default send to device action**: Allows you to specify which of the options, 1 through 5 above or 7 below, will be the default action when you click the main button.
|
||||
7. **Send and delete from library**: The selected books are transferred to the selected storage location on the device and then **deleted** from the Library.
|
||||
8. **Fetch Annotations (experimental)**: Transfers annotations you may have made on an ebook on your device to the comments metadata of the book in the |app| library.
|
||||
|
||||
You can control the file name and folder structure of files sent to the device by setting up a template in
|
||||
:guilabel:`Preferences->Import/Export->Sending books to devices`. Also see :ref:`templatelangcalibre`.
|
||||
@ -152,9 +153,9 @@ The :guilabel:`Fetch news` action uses simple recipes (10-15 lines of code) for
|
||||
|
||||
The :guilabel:`Fetch news` action has three variations, accessed by clicking the down arrow on the right of the button.
|
||||
|
||||
1. **Schedule news download**: This action allows you to schedule the download of of your selected news sources from a list of hundreds of available. Scheduling can be set individually for each news source you select and the scheduling is flexible allowing you to select specific days of the week or a frequency of days between downloads.
|
||||
2. **Add a custom news service**: This action allows you to create a simple recipe for downloading news from a custom news site that you wish to access. Creating the recipe can be as simple as specifying an RSS news feed URL, or you can be more prescriptive by creating python based code for the task, see :ref:`news`.
|
||||
3. **Download all scheduled news sources**: This action causes |app| to immediately begin to download all news sources that you have previously scheduled.
|
||||
1. **Schedule news download**: Allows you to schedule the download of of your selected news sources from a list of hundreds available. Scheduling can be set individually for each news source you select and the scheduling is flexible allowing you to select specific days of the week or a frequency of days between downloads.
|
||||
2. **Add a custom news source**: Allows you to create a simple recipe for downloading news from a custom news site that you wish to access. Creating the recipe can be as simple as specifying an RSS news feed URL, or you can be more prescriptive by creating Python-based code for the task. For more information see :ref:`news`.
|
||||
3. **Download all scheduled news sources**: Causes |app| to immediately begin downloading all news sources that you have scheduled.
|
||||
|
||||
|
||||
.. _library:
|
||||
@ -164,16 +165,16 @@ Library
|
||||
.. |lii| image:: images/library.png
|
||||
:class: float-right-img
|
||||
|
||||
|lii| The :guilabel:`Library` action allows you to create, switch between, rename or remove a Library. |app| allows you to create as many libraries as you wish. You could for instance create a fiction library, a non fiction library, a foreign language library, a project library, basically any structure that suits your needs. Libraries are the highest organizational structure within |app|, each library has its own set of books, tags, categories and base storage location.
|
||||
|lii| The :guilabel:`Library` action allows you to create, switch between, rename or remove a Library. |app| allows you to create as many libraries as you wish. You could, for instance, create a fiction library, a non-fiction library, a foreign language library, a project library, or any structure that suits your needs. Libraries are the highest organizational structure within |app|. Each library has its own set of books, tags, categories and base storage location.
|
||||
|
||||
1. **Switch/Create library**: This action allows you to; a) connect to a pre-existing |app| library at another location from your currently open library, b) Create and empty library at a new location or, c) Move the current Library to a newly specified location.
|
||||
2. **Quick Switch**: This action allows you to switch between libraries that have been registered or created within |app|.
|
||||
3. **Rename Library**: This action allows you to rename a Library.
|
||||
4. **Remove Library**: This action allows you to unregister a library from |app|.
|
||||
5. **<library name>**: Actions 5, 6 etc .. give you immediate switch access between multiple Libraries that you have created or attached to. This list contains only the 5 most frequently used libraries. For the complete list, use the Quick Switch menu.
|
||||
6. **Library Maintenance**: This action allows you to check the current library for data consistency issues and restore the current libraries' database from backups.
|
||||
1. **Switch/create library...**: Allows you to; a) connect to a pre-existing |app| library at another location, b) create an empty library at a new location or, c) move the current library to a newly specified location.
|
||||
2. **Quick switch**: Allows you to switch between libraries that have been registered or created within |app|.
|
||||
3. **Rename library**: Allows you to rename a Library.
|
||||
4. **Delete library**: Allows you to unregister a library from |app|.
|
||||
5. **<library name>**: Actions 5, 6 etc... give you immediate switch access between multiple libraries that you have created or attached to. This list contains only the 5 most frequently used libraries. For the complete list, use the Quick Switch menu.
|
||||
6. **Library maintenance**: Allows you to check the current library for data consistency issues and restore the current library's database from backups.
|
||||
|
||||
.. note:: Metadata about your ebooks like title/author/tags/etc. is stored in a single file in your |app| library folder called metadata.db. If this file gets corrupted (a very rare event), you can lose the metadata. Fortunately, |app| automatically backs up the metadata for every individual book in the book's folder as an .opf file. By using the Restore Library action under Library Maintenance described above, you can have |app| rebuild the metadata.db file from the individual .opf files for you.
|
||||
.. note:: Metadata about your ebooks, e.g. title, author, and tags, is stored in a single file in your |app| library folder called metadata.db. If this file gets corrupted (a very rare event), you can lose the metadata. Fortunately, |app| automatically backs up the metadata for every individual book in the book's folder as an OPF file. By using the Restore Library action under Library Maintenance described above, you can have |app| rebuild the metadata.db file from the individual OPF files for you.
|
||||
|
||||
You can copy or move books between different libraries (once you have more than one library setup) by right clicking on the book and selecting the action :guilabel:`Copy to library`.
|
||||
|
||||
@ -185,7 +186,7 @@ Device
|
||||
:class: float-right-img
|
||||
|
||||
|dvi| The :guilabel:`Device` action allows you to view the books in the main memory or storage cards of your device, or to eject the device (detach it from |app|).
|
||||
This icon shows up automatically on the main |app| toolbar when you connect a supported device. You can click on it to see the books on your device. You can also drag and drop books from your |app| library onto the icon to transfer them to your device. Conversely, you can drag and drop books from your device onto the |app| icon on the toolbar to transfer books from your device to the |app| library.
|
||||
This icon shows up automatically on the main |app| toolbar when you connect a supported device. You can click on it to see the books on your device. You can also drag and drop books from your |app| library onto the icon to transfer them to your device. Conversely, you can drag and drop books from your device onto the library icon on the toolbar to transfer books from your device to the |app| library.
|
||||
|
||||
|
||||
.. _save_to_disk:
|
||||
@ -199,7 +200,7 @@ Save to disk
|
||||
|
||||
.. _save_to_disk_multiple:
|
||||
|
||||
1. **Save to disk**: This will save the selected books to disk organized in directories. The directory structure looks like::
|
||||
1. **Save to disk**: Saves the selected books to disk organized in directories. The directory structure looks like::
|
||||
|
||||
Author_(sort)
|
||||
Title
|
||||
@ -210,17 +211,15 @@ Save to disk
|
||||
|
||||
.. _save_to_disk_single:
|
||||
|
||||
2. **Save to disk in a single directory**: The selected books are saved to disk in a single directory.
|
||||
2. **Save to disk in a single directory**: Saves the selected books to disk in a single directory.
|
||||
|
||||
For 1. and 2. All available formats as well as metadata is stored to disk for each selected book. Metadata is stored in an OPF file.
|
||||
For 1. and 2., all available formats, as well as metadata, are stored to disk for each selected book. Metadata is stored in an OPF file. Saved books can be re-imported to the library without any loss of information by using the :ref:`Add books <add_books>` action.
|
||||
|
||||
Saved books can be re-imported to the library without any loss of information by using the :ref:`Add books <add_books>` action.
|
||||
3. **Save only *<your preferred>* format to disk**: Saves the selected books to disk in the directory structure as shown in (1.) but only in your preferred ebook format. You can set your preferred format in :guilabel:`Preferences->Behaviour->Preferred output format`
|
||||
|
||||
3. **Save only *<your preferred>* format to disk**: The selected books are saved to disk in the directory structure as shown in (1.) but only in your preferred ebook format you can set <your preferred> format in :guilabel:`Preferences->Behaviour->Preferred output format`
|
||||
4. **Save only *<your preferred>* format to disk in a single directory**: Saves the selected books to disk in a single directory but only in your preferred ebook format. You can set your preferred format in :guilabel:`Preferences->Behaviour->Preferred output format`
|
||||
|
||||
4. **Save only *<your preferred>* format to disk in a single directory**: The selected books are saved to disk in a single directory but only in <your preferred> ebook format you can set <your preferred> format in :guilabel:`Preferences->Behaviour->Preferred output format`
|
||||
|
||||
5. **Save single format to disk ..**: The selected books are saved to disk in the directory structure as shown in (1.) but only in the format you select from the pop-out list. There are currently 35 formats available and new ones are being added all the time.
|
||||
5. **Save single format to disk...**: Saves the selected books to disk in the directory structure as shown in (1.) but only in the format you select from the pop-out list.
|
||||
|
||||
.. _connect_share:
|
||||
|
||||
@ -229,17 +228,17 @@ Connect/Share
|
||||
.. |csi| image:: images/connect_share.png
|
||||
:class: float-right-img
|
||||
|
||||
|csi| The :guilabel:`Connect/Share` action allows you to manually connect to a device or folder on your computer, it also allows you to set up you |app| library for access via a web browser, or email.
|
||||
|csi| The :guilabel:`Connect/Share` action allows you to manually connect to a device or folder on your computer. It also allows you to set up you |app| library for access via a web browser or email.
|
||||
|
||||
The :guilabel:`Connect/Share` action has four variations, accessed by clicking the down arrow on the right of the button.
|
||||
|
||||
1. **Connect to folder**: This action allows you to connect to any folder on your computer as though it were a device and use all the facilities |app| has for devices with that folder. Useful if your device cannot be supported by |app| but is available as a USB disk.
|
||||
1. **Connect to folder**: Allows you to connect to any folder on your computer as though it were a device and use all the facilities |app| has for devices with that folder. Useful if your device cannot be supported by |app| but is available as a USB disk.
|
||||
|
||||
2. **Connect to iTunes**: Allows you to connect to your iTunes books database as though it were a device. Once the books are sent to iTunes, you can then use iTunes to make them available on your various iDevices. Useful if you would rather not have |app| send books to your iDevice directly.
|
||||
2. **Connect to iTunes**: Allows you to connect to your iTunes books database as though it were a device. Once the books are sent to iTunes, you can use iTunes to make them available to your various iDevices. This is useful if you would rather not have |app| send books to your iDevice directly.
|
||||
|
||||
3. **Start Content Server**: This action causes |app| to start up its built-in web server. When this is started, your |app| library will be accessible via a web browser from the internet (if you choose). You can configure how the web server is accessed by setting preferences at :guilabel:`Preferences->Sharing->Sharing over the net`
|
||||
3. **Start Content Server**: Starts |app|'s built-in web server. When started, your |app| library will be accessible via a web browser from the Internet (if you choose). You can configure how the web server is accessed by setting preferences at :guilabel:`Preferences->Sharing->Sharing over the net`
|
||||
|
||||
4. **Setup email based sharing of books**: This action allows you to setup |app| to share books (and news feeds) by email. After setting up email addresses for this option |app| will send news updates and book updates to the entered email addresses. You can configure how the |app| sends email by setting preferences at :guilabel:`Preferences->Sharing->Sharing books by email`. Once you have setup one or more email addresses, this menu entry get replaced by menu entries to send books to the setup email addresses.
|
||||
4. **Setup email based sharing of books**: Allows sharing of books and news feeds by email. After setting up email addresses for this option, |app| will send news updates and book updates to the entered email addresses. You can configure how |app| sends email by setting preferences at :guilabel:`Preferences->Sharing->Sharing books by email`. Once you have set up one or more email addresses, this menu entry will be replaced by menu entries to send books to the configured email addresses.
|
||||
|
||||
.. _remove_books:
|
||||
|
||||
@ -248,29 +247,36 @@ Remove books
|
||||
.. |rbi| image:: images/remove_books.png
|
||||
:class: float-right-img
|
||||
|
||||
|rbi| The :guilabel:`Remove books` action **deletes books permanently**, so use it with care. It is *context sensitive*, i.e. it depends on which :ref:`catalog <catalogs>` you have selected. If you have selected the :guilabel:`Library`, books will be removed from the library. If you have selected the ebook reader device, the books will be removed from the device. To remove only a particular format for a given book use the :ref:`edit_meta_information` action. Remove books also has five variations which can be accessed by clicking the down arrow on the right side of the button.
|
||||
|rbi| The :guilabel:`Remove books` action **deletes books permanently**, so use it with care. It is *context sensitive*, i.e. it depends on which :ref:`catalog <catalogs>` you have selected. If you have selected the :guilabel:`Library`, books will be removed from the library. If you have selected the ebook reader device, books will be removed from the device. To remove only a particular format for a given book use the :ref:`edit_meta_information` action. Remove books also has five variations which can be accessed by clicking the down arrow on the right side of the button.
|
||||
|
||||
1. **Remove Selected Books**: Allows you to **permanently** remove all books that are selected in the book list.
|
||||
1. **Remove selected books**: Allows you to **permanently** remove all books that are selected in the book list.
|
||||
|
||||
2. **Remove files of a specified format from selected books..**: Allows you to **permanently** remove ebook files of a specified format, from books that are selected in the book list.
|
||||
2. **Remove files of a specific format from selected books...**: Allows you to **permanently** remove ebook files of a specified format from books that are selected in the book list.
|
||||
|
||||
3. **Remove all files of a specified format, except..**: Allows you to **permanently** remove ebook files of a multiple formats except a given format, from books that are selected in the book list.
|
||||
3. **Remove all formats from selected books, except...**: Allows you to **permanently** remove ebook files of any format except a specified format from books that are selected in the book list.
|
||||
|
||||
4. **Remove covers from selected books**: Allows you to **permanently** remove cover images files, from books that are selected in the book list.
|
||||
3. **Remove all formats from selected books**: Allows you to **permanently** remove all ebook files from books that are selected in the book list. Only the metadata will remain.
|
||||
|
||||
5. **Remove matching books from device**: Allows you to remove ebook files from a connected device, that match the books that are selected in the book list.
|
||||
4. **Remove covers from selected books**: Allows you to **permanently** remove cover image files from books that are selected in the book list.
|
||||
|
||||
5. **Remove matching books from device**: Allows you to remove ebook files from a connected device that match the books that are selected in the book list.
|
||||
|
||||
.. note::
|
||||
Note that when you use Remove books to delete books from your |app| library, the book record is permanently deleted, but, on (Windows and OS X) the files are placed into the recycle bin, so you can recover them if you change your mind.
|
||||
Note that when you use Remove books to delete books from your |app| library, the book record is permanently deleted, but on Windows and OS X the files are placed into the recycle bin. This allows you to recover them if you change your mind.
|
||||
|
||||
.. _configuration:
|
||||
|
||||
Preferences
|
||||
---------------
|
||||
.. |cbi| image:: images/preferences.png
|
||||
:class: float-right-img
|
||||
|
||||
The Preferences Action allows you to change the way various aspects of |app| work. To access it, click the |cbi|.
|
||||
You can also re-run the Welcome Wizard by clicking the arrow next to the preferences button.
|
||||
|cbi| The :guilabel:`Preferences` action allows you to change the way various aspects of |app| work. It has four variations, accessed by clicking the down arrow on the right of the button.
|
||||
|
||||
1. **Preferences**: Allows you to change the way various aspects of |app| work. Clicking the button also performs this action.
|
||||
2. **Run welcome wizard**: Allows you to start the Welcome Wizard which appeared the first time you started |app|.
|
||||
3. **Get plugins to enhance |app|**: Opens a new windows that shows plugins for |app|. These plugins are developed by third parties to extend |app|'s functionality.
|
||||
4. **Restart in debug mode**: Allows you to enable a debugging mode that can assist the |app| developers in solving problems you encounter with the program. For most users this should remain disabled unless instructed by a developer to enable it.
|
||||
|
||||
.. _catalogs:
|
||||
|
||||
@ -281,12 +287,11 @@ Catalogs
|
||||
|
||||
A *catalog* is a collection of books. |app| can manage two types of different catalogs:
|
||||
|
||||
1. **Library**: This is a collection of books stored in your |app| library on your computer
|
||||
1. **Library**: This is a collection of books stored in your |app| library on your computer.
|
||||
|
||||
2. **Device**: This is a collection of books stored in the main memory of your ebook reader. It will be available when you connect the reader to your computer.
|
||||
- In addition, you can see the books on the storage card (if any) in your reader device.
|
||||
2. **Device**: This is a collection of books stored in your ebook reader. It will be available when you connect the reader to your computer.
|
||||
|
||||
Many operations, like Adding books, deleting, viewing, etc. are context sensitive. So, for example, if you click the View button when you have the **Device** catalog selected, |app| will open the files on the device to view. If you have the **Library** catalog selected, files in your |app| library will be opened instead.
|
||||
Many operations, such as adding books, deleting, viewing, etc., are context sensitive. So, for example, if you click the View button when you have the **Device** catalog selected, |app| will open the files on the device to view. If you have the **Library** catalog selected, files in your |app| library will be opened instead.
|
||||
|
||||
.. _search_sort:
|
||||
|
||||
@ -297,7 +302,7 @@ Search & Sort
|
||||
|
||||
The Search & Sort section allows you to perform several powerful actions on your book collections.
|
||||
|
||||
* You can sort them by title, author, date, rating etc. by clicking on the column titles. You can also sub-sort (i.e. sort on multiple columns). For example, if you click on the title column and then the author column, the book will be sorted by author and then all the entries for the same author will be sorted by title.
|
||||
* You can sort them by title, author, date, rating, etc. by clicking on the column titles. You can also sub-sort, i.e. sort on multiple columns. For example, if you click on the title column and then the author column, the book will be sorted by author and then all the entries for the same author will be sorted by title.
|
||||
|
||||
* You can search for a particular book or set of books using the search bar. More on that below.
|
||||
|
||||
@ -327,21 +332,21 @@ are available in the LRF format. Some more examples::
|
||||
format:epub publisher:feedbooks.com
|
||||
|
||||
Searches are by default 'contains'. An item matches if the search string appears anywhere in the indicated metadata.
|
||||
Two other kinds of searches are available: equality search and search using regular expressions.
|
||||
Two other kinds of searches are available: equality search and search using `regular expressions <http://en.wikipedia.org/wiki/Regular_expression>`_.
|
||||
|
||||
Equality searches are indicated by prefixing the search string with an equals sign (=). For example, the query
|
||||
``tag:"=science"`` will match "science", but not "science fiction" or "hard science". Regular expression searches are
|
||||
indicated by prefixing the search string with a tilde (~). Any python-compatible regular expression can
|
||||
be used. Note that backslashes use to escape special characters in reqular expressions must be doubled, because single backslashes will be removed during query parsing. For example, to match a literal parenthesis, you must enter ``\\(``. Regular expression searches are contains searches unless the expression contains anchors.
|
||||
indicated by prefixing the search string with a tilde (~). Any `python-compatible regular expression <http://docs.python.org/library/re.html>`_ can
|
||||
be used. Note that backslashes used to escape special characters in reqular expressions must be doubled because single backslashes will be removed during query parsing. For example, to match a literal parenthesis you must enter ``\\(``. Regular expression searches are 'contains' searches unless the expression contains anchors.
|
||||
|
||||
Should you need to search for a string with a leading equals or tilde, prefix the string with a backslash.
|
||||
|
||||
Enclose search strings with quotes (") if the string contains parenthesis or spaces. For example, to search
|
||||
for the tag ``Science Fiction``, you would need to search for ``tag:"=science fiction"``. If you search for
|
||||
``tag:=science fiction``, you will find all books with the tag 'science' and containing the word 'fiction' in any
|
||||
for the tag ``Science Fiction`` you would need to search for ``tag:"=science fiction"``. If you search for
|
||||
``tag:=science fiction`` you will find all books with the tag 'science' and containing the word 'fiction' in any
|
||||
metadata.
|
||||
|
||||
You can build advanced search queries easily using the :guilabel:`Advanced Search Dialog`, accessed by
|
||||
You can build advanced search queries easily using the :guilabel:`Advanced Search Dialog` accessed by
|
||||
clicking the button |sbi|.
|
||||
|
||||
Available fields for searching are: ``tag, title, author, publisher, series, series_index, rating, cover,
|
||||
@ -354,10 +359,13 @@ The syntax for searching for dates is::
|
||||
pubdate:=2009 Will find all books published in 2009
|
||||
|
||||
If the date is ambiguous, the current locale is used for date comparison. For example, in an mm/dd/yyyy
|
||||
locale, 2/1/2009 is interpreted as 1 Feb 2009. In a dd/mm/yyyy locale, it is interpreted as 2 Jan 2009. Some
|
||||
locale 2/1/2009 is interpreted as 1 Feb 2009. In a dd/mm/yyyy locale it is interpreted as 2 Jan 2009. Some
|
||||
special date strings are available. The string ``today`` translates to today's date, whatever it is. The
|
||||
strings ``yesterday`` and ``thismonth`` also work. In addition, the string ``daysago`` can be used to compare
|
||||
to a date some number of days ago, for example: date:>10daysago, date:<=45daysago.
|
||||
to a date some number of days ago. For example::
|
||||
|
||||
date:>10daysago
|
||||
date:<=45daysago
|
||||
|
||||
You can search for books that have a format of a certain size like this::
|
||||
|
||||
@ -369,7 +377,7 @@ Dates and numeric fields support the relational operators ``=`` (equals), ``>``
|
||||
Rating fields are considered to be numeric. For example, the search ``rating:>=3`` will find all books rated 3
|
||||
or higher.
|
||||
|
||||
You can search for the number of items in multiple-valued fields such as tags). These searches begin with the character ``#``, then use the same syntax as numeric fields. For example, to find all books with more than 4 tags, use ``tags:#>4``. To find all books with exactly 10 tags, use ``tags:#=10``.
|
||||
You can search for the number of items in multiple-valued fields such as tags. These searches begin with the character ``#``, then use the same syntax as numeric fields. For example, to find all books with more than 4 tags use ``tags:#>4``. To find all books with exactly 10 tags use ``tags:#=10``.
|
||||
|
||||
Series indices are searchable. For the standard series, the search name is 'series_index'. For
|
||||
custom series columns, use the column search name followed by _index. For example, to search the indices for a
|
||||
@ -378,7 +386,7 @@ Series indices are numbers, so you can use the relational operators described ab
|
||||
|
||||
The special field ``search`` is used for saved searches. So if you save a search with the name
|
||||
"My spouse's books" you can enter ``search:"My spouse's books"`` in the search bar to reuse the saved
|
||||
search. More about saving searches, below.
|
||||
search. More about saving searches below.
|
||||
|
||||
You can search for the absence or presence of a field using the special "true" and "false" values. For example::
|
||||
|
||||
@ -418,9 +426,9 @@ Identifiers (e.g., isbn, doi, lccn etc) also use an extended syntax. First, note
|
||||
Saving searches
|
||||
-----------------
|
||||
|
||||
|app| has a useful feature, it allows you to save a search you use frequently under a special name and then re-use that search with a single click. To do this, create your search, either by typing it in the search bar, or using the Tag Browser. Then, type the name you would like to give to the search in the Saved Searches box next to the search bar and click the plus icon next to the saved searches box to save the search.
|
||||
|app| allows you to save a frequently used search under a special name and then reuse that search with a single click. To do this, create your search either by typing it in the search bar or using the Tag Browser. Then type the name you would like to give to the search in the Saved Searches box next to the search bar. Click the plus icon next to the saved searches box to save the search.
|
||||
|
||||
Now, you can access your saved search in the Tag Browser under "Searches". A single click will allow you to re-use any arbitrarily complex search easily, without needing to re-create it.
|
||||
Now you can access your saved search in the Tag Browser under "Searches". A single click will allow you to reuse any arbitrarily complex search easily, without needing to re-create it.
|
||||
|
||||
.. _config_filename_metadata:
|
||||
|
||||
@ -431,7 +439,7 @@ that you add to the library. The default regular expression is::
|
||||
|
||||
title - author
|
||||
|
||||
i.e., it will assumes that all character up to the first ``-`` are the title of the book and subsequent characters are the author of the book. For example, the filename::
|
||||
i.e., it assumes that all characters up to the first ``-`` are the title of the book and subsequent characters are the author of the book. For example, the filename::
|
||||
|
||||
Foundation and Earth - Isaac Asimov.txt
|
||||
|
||||
@ -447,7 +455,7 @@ Book Details
|
||||
.. image:: images/book_details.png
|
||||
:align: center
|
||||
|
||||
The Book Details display shows you extra information and the cover for the currently selected book.
|
||||
The Book Details display shows extra information and the cover for the currently selected book.
|
||||
|
||||
.. _jobs:
|
||||
|
||||
@ -460,15 +468,19 @@ Tag Browser
|
||||
|
||||
The Tag Browser allows you to easily browse your collection by Author/Tags/Series/etc. If you click on any item in the Tag Browser, for example the author name Isaac Asimov, then the list of books to the right is restricted to showing books by that author. You can click on category names as well. For example, clicking on "Series" will show you all books in any series.
|
||||
|
||||
The first click on an item will restrict the list of books to those that contain/match the item. Continuing the above example, clicking on Isaac Asimov will show books by that author. Clicking again on the item will change what is shown, depending on whether the item has children (see sub-categories and hierarchical items below). Continuing the Isaac Asimov example, clicking again on Isaac Asimov will restrict the list of books to those not by Isaac Asimov. A third click will remove the restriction, showing all books. If you hold down the Ctrl or Shift keys and click on multiple items, then restrictions based on multiple items are created. For example you could hold Ctrl and click on the tags History and Europe for find books on European history. The Tag Browser works by constructing search expressions that are automatically entered into the Search bar. Looking at what the Tag Browser generates is a good way to learn how to construct basic search expressions.
|
||||
The first click on an item will restrict the list of books to those that contain or match the item. Continuing the above example, clicking on Isaac Asimov will show books by that author. Clicking again on the item will change what is shown, depending on whether the item has children (see sub-categories and hierarchical items below). Continuing the Isaac Asimov example, clicking again on Isaac Asimov will restrict the list of books to those not by Isaac Asimov. A third click will remove the restriction, showing all books. If you hold down the Ctrl or Shift keys and click on multiple items, then restrictions based on multiple items are created. For example you could hold Ctrl and click on the tags History and Europe for finding books on European history. The Tag Browser works by constructing search expressions that are automatically entered into the Search bar. Looking at what the Tag Browser generates is a good way to learn how to construct basic search expressions.
|
||||
|
||||
Items in the Tag browser have their icons partially colored. The amount of color depends on the average rating of the books in that category. So for example if the books by Isaac Asimov have an average of four stars, the icon for Isaac Asimov in the Tag Browser will be 4/5th colored. You can hover your mouse over the icon to see the average rating.
|
||||
|
||||
The outer-level items in the tag browser such as Authors and Series are called categories. You can create your own categories, called User Categories, which are useful for organizing items. For example, you can use the User Categories Editor (push the Manage User Categories button) to create a user category called Favorite Authors, then put the items for your favorites into the category. User categories can have sub-categories. For example, the user category Favorites.Authors is a sub-category of Favorites. You might also have Favorites.Series, in which case there will be two sub-categories under Favorites. Sub-categories can be created by right-clicking on a user category, choosing "Add sub-category to ...", and entering the sub-category name; or by using the User Categories Editor by entering names like the Favorites example above.
|
||||
The outer-level items in the tag browser, such as Authors and Series, are called categories. You can create your own categories, called User Categories, which are useful for organizing items. For example, you can use the User Categories Editor (click the Manage User Categories button) to create a user category called Favorite Authors, then put the items for your favorites into the category. User categories can have sub-categories. For example, the user category Favorites.Authors is a sub-category of Favorites. You might also have Favorites.Series, in which case there will be two sub-categories under Favorites. Sub-categories can be created by right-clicking on a user category, choosing "Add sub-category to ...", and entering the sub-category name; or by using the User Categories Editor by entering names like the Favorites example above.
|
||||
|
||||
You can search user categories in the same way as built-in categories, by clicking on them. There are four different searches cycled through by clicking: "everything matching an item in the category" indicated by a single green plus sign, "everything matching an item in the category or its sub-categories" indicated by two green plus signs, "everything not matching an item in the category" shown by a single red minus sign, and "everything not matching an item in the category or its sub-categories" shown by two red minus signs.
|
||||
You can search user categories in the same way as built-in categories, by clicking on them. There are four different searches cycled through by clicking:
|
||||
1. "everything matching an item in the category" indicated by a single green plus sign.
|
||||
2. "everything matching an item in the category or its sub-categories" indicated by two green plus signs.
|
||||
3. "everything not matching an item in the category" shown by a single red minus sign.
|
||||
4. "everything not matching an item in the category or its sub-categories" shown by two red minus signs.
|
||||
|
||||
It is also possible to create hierarchies inside some of the text categories such as tags, series, and custom columns. These hierarchies show with the small triangle, permitting the sub-items to be hidden. To use hierarchies of items in a category, you must first go to Preferences / Look & Feel and enter the category name(s) into the "Categories with hierarchical items" box. Once this is done, items in that category that contain periods will be shown using the small triangle. For example, assume you create a custom column called "Genre" and indicate that it contains hierarchical items. Once done, items such as Mystery.Thriller and Mystery.English will display as Mystery with the small triangle next to it. Clicking on the triangle will show Thriller and English as sub-items. See :ref:`Managing subgroups of books, for example "genre" <subgroups-tutorial>` for more information.
|
||||
It is also possible to create hierarchies inside some of the text categories such as tags, series, and custom columns. These hierarchies show with the small triangle, permitting the sub-items to be hidden. To use hierarchies of items in a category, you must first go to Preferences->Look & Feel and enter the category name(s) into the "Categories with hierarchical items" box. Once this is done, items in that category that contain periods will be shown using the small triangle. For example, assume you create a custom column called "Genre" and indicate that it contains hierarchical items. Once done, items such as Mystery.Thriller and Mystery.English will display as Mystery with the small triangle next to it. Clicking on the triangle will show Thriller and English as sub-items. See :ref:`Managing subgroups of books, for example "genre" <subgroups-tutorial>` for more information.
|
||||
|
||||
Hierarchical items (items with children) use the same four 'click-on' searches as user categories. Items that do not have children use two of the searches: "everything matching" and "everything not matching".
|
||||
|
||||
@ -476,23 +488,23 @@ You can drag and drop items in the Tag browser onto user categories to add them
|
||||
|
||||
There is a search bar at the top of the Tag Browser that allows you to easily find any item in the Tag Browser. In addition, you can right click on any item and choose one of several operations. Some examples are to hide the it, rename it, or open a "Manage x" dialog that allows you to manage items of that kind. For example, the "Manage Authors" dialog allows you to rename authors and control how their names are sorted.
|
||||
|
||||
You can control how items are sorted in the Tag browser via the box at the bottom of the Tag Browser. You can choose to sort by name, average rating or popularity (popularity is the number of books with an item in your library; for example; the popularity of Isaac Asimov is the number of book sin your library by Isaac Asimov).
|
||||
You can control how items are sorted in the Tag browser via the box at the bottom of the Tag Browser. You can choose to sort by name, average rating or popularity (popularity is the number of books with an item in your library; for example, the popularity of Isaac Asimov is the number of books in your library by Isaac Asimov).
|
||||
|
||||
Jobs
|
||||
-----
|
||||
.. image:: images/jobs.png
|
||||
:class: float-left-img
|
||||
|
||||
The Jobs panel shows you the number of currently running jobs. Jobs are tasks that run in a separate process, they include converting ebooks and talking to your reader device. You can click on the jobs panel to access the list of jobs. Once a job has completed, by double-clicking it in the list, you can see a detailed log from that job. This is useful to debug jobs that may not have completed successfully.
|
||||
The Jobs panel shows the number of currently running jobs. Jobs are tasks that run in a separate process. They include converting ebooks and talking to your reader device. You can click on the jobs panel to access the list of jobs. Once a job has completed you can see a detailed log from that job by double-clicking it in the list. This is useful to debug jobs that may not have completed successfully.
|
||||
|
||||
Keyboard Shortcuts
|
||||
---------------------
|
||||
|
||||
Calibre has several keyboard shortcuts to save you time and mouse movement. These shortcuts are active in the book list view (when you're not editing the details of a particular book), and most of them affect the title you have selected. The |app| e-book viewer has its own shortcuts, which can be customised by clicking the Preferences button in the viewer.
|
||||
Calibre has several keyboard shortcuts to save you time and mouse movement. These shortcuts are active in the book list view (when you're not editing the details of a particular book), and most of them affect the title you have selected. The |app| ebook viewer has its own shortcuts which can be customised by clicking the Preferences button in the viewer.
|
||||
|
||||
.. note::
|
||||
|
||||
Note: The Calibre keyboard shortcuts do not require a modifier key (Command, Option, Control etc.), unless specifically noted. You only need to press the letter key, e.g. E to edit.
|
||||
Note: The Calibre keyboard shortcuts do not require a modifier key (Command, Option, Control, etc.), unless specifically noted. You only need to press the letter key, e.g. E to edit.
|
||||
|
||||
.. list-table:: Keyboard Shortcuts
|
||||
:widths: 10 100
|
||||
@ -535,7 +547,7 @@ Calibre has several keyboard shortcuts to save you time and mouse movement. Thes
|
||||
* - :kbd:`Alt+Shift+T`
|
||||
- Toggle Tag Browser
|
||||
* - :kbd:`Alt+A`
|
||||
- Show books by the Same author as the current book
|
||||
- Show books by the same author as the current book
|
||||
* - :kbd:`Alt+T`
|
||||
- Show books with the same tags as current book
|
||||
* - :kbd:`Alt+P`
|
||||
|
20
src/calibre/manual/index.rst
Normal file → Executable file
20
src/calibre/manual/index.rst
Normal file → Executable file
@ -3,23 +3,21 @@
|
||||
|app| User Manual
|
||||
===============================================
|
||||
|
||||
|app| is an e-book library manager. It can view, convert and catalog e-books in most of the major e-book formats. It can also talk to a few e-book reader devices. It can go out to the internet and fetch metadata for your books. It can download newspapers and convert them into e-books for convenient reading. It is cross platform, running on Linux, Windows and OS X.
|
||||
|app| is an ebook library manager. It can view, convert and catalog ebooks in most of the major ebook formats. It can also talk to many ebook reader devices. It can go out to the Internet and fetch metadata for your books. It can download newspapers and convert them into ebooks for convenient reading. It is cross platform, running on Linux, Windows and OS X.
|
||||
|
||||
So you've just started |app|. What do you do now? Well, before |app| can do anything with your ebooks, it first has to know about them. So drag and drop a few e-book files into |app|, or click the "Add books" button and browse for the ebooks you want to work with. Once you've added the books, they will show up in the main view looking something like this:
|
||||
You've just started |app|. What do you do now? Before |app| can do anything with your ebooks, it first has to know about them. Drag and drop a few ebook files into |app|, or click the "Add books" button and browse for the ebooks you want to work with. Once you've added the books, they will show up in the main view looking something like this:
|
||||
|
||||
.. image:: images/added_books.png
|
||||
|
||||
Once you've admired the list of books you just added to your heart's content, you'll probably want to actually read one. In order to do that you'll have to convert the book to a format your reader understands. When first running |app|, the Welcome Wizard starts and it will have setup calibre for your reader device. Conversion is a breeze, just select the book you want to convert, and click the "Convert E-book" button. Ignore all the options for now and just click "OK". The little icon in the bottom right corner will start spinning. Once it's finished spinning, your converted book is ready. Click the "View" button to read the book.
|
||||
Once you've admired the list of books you just added to your heart's content, you'll probably want to read one. In order to do that you'll have to convert the book to a format your reader understands. When first running |app|, the Welcome Wizard starts and will set up calibre for your reader device. Conversion is a breeze. Just select the book you want to convert then click the "Convert books" button. Ignore all the options for now and click "OK". The little icon in the bottom right corner will start spinning. Once it's finished spinning, your converted book is ready. Click the "View" button to read the book.
|
||||
|
||||
Now if you want to read the book on your reader, just connect it to the computer, wait till calibre detects it (10-20secs) and then click the "Send to device" button. Once the icon stops spinning again, disconnect your reader and read away! If you didn't convert the book in the previous step, |app| will auto convert it to the format your reader device understands.
|
||||
If you want to read the book on your reader, connect it to the computer, wait till calibre detects it (10-20 seconds) and then click the "Send to device" button. Once the icon stops spinning again, disconnect your reader and read away! If you didn't convert the book in the previous step, |app| will auto convert it to the format your reader device understands.
|
||||
|
||||
To get started with more advanced usage, you should read about the :ref:`Graphical User Interface <gui>`. For even more power and versatility, learn the :ref:`Command Line Interface <cli>`.
|
||||
|
||||
You will find the list of :ref:`Frequently Asked Questions <faq>` useful as well.
|
||||
To get started with more advanced usage, you should read about the :ref:`Graphical User Interface <gui>`. For even more power and versatility, learn the :ref:`Command Line Interface <cli>`. You will find the list of :ref:`Frequently Asked Questions <faq>` useful as well.
|
||||
|
||||
.. only:: online
|
||||
|
||||
An e-book version of this User Manual is available in `EPUB format <calibre.epub>`_.
|
||||
An ebook version of this user manual is available in `EPUB format <calibre.epub>`_.
|
||||
|
||||
Sections
|
||||
------------
|
||||
@ -56,7 +54,7 @@ Adding your favorite news website to |app|
|
||||
|
||||
news
|
||||
|
||||
The |app| e-book viewer
|
||||
The |app| ebook viewer
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. toctree::
|
||||
@ -64,7 +62,7 @@ The |app| e-book viewer
|
||||
|
||||
viewer
|
||||
|
||||
Customizing |app|'s e-book conversion
|
||||
Customizing |app|'s ebook conversion
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. toctree::
|
||||
@ -72,7 +70,7 @@ Customizing |app|'s e-book conversion
|
||||
|
||||
conversion
|
||||
|
||||
Editing e-book metadata
|
||||
Editing ebook metadata
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. toctree::
|
||||
|
@ -2,14 +2,14 @@
|
||||
|
||||
.. _metadata:
|
||||
|
||||
Editing E-book Metadata
|
||||
Editing Ebook Metadata
|
||||
========================
|
||||
|
||||
.. contents:: Contents
|
||||
:depth: 2
|
||||
:local:
|
||||
|
||||
E-books come in all shapes and sizes and more often than not, their metadata (things like title/author/series/publisher) is incomplete or incorrect.
|
||||
Ebooks come in all shapes and sizes and more often than not, their metadata (things like title/author/series/publisher) is incomplete or incorrect.
|
||||
The simplest way to change metadata in |app| is to simply double click on an entry and type in the correct replacement.
|
||||
For more sophisticated, "power editing" use the edit metadata tools discussed below.
|
||||
|
||||
|
26
src/calibre/manual/news.rst
Normal file → Executable file
26
src/calibre/manual/news.rst
Normal file → Executable file
@ -5,7 +5,7 @@
|
||||
Adding your favorite news website
|
||||
==================================
|
||||
|
||||
|app| has a powerful, flexible and easy-to-use framework for downloading news from the internet and converting it into an e-book. In the following, I will show you by means of examples, how to get news from various websites.
|
||||
|app| has a powerful, flexible and easy-to-use framework for downloading news from the Internet and converting it into an ebook. The following will show you, by means of examples, how to get news from various websites.
|
||||
|
||||
To gain an understanding of how to use the framework, follow the examples in the order listed below:
|
||||
|
||||
@ -23,27 +23,27 @@ If your news source is simple enough, |app| may well be able to fetch it complet
|
||||
portfolio.com
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
*portfolio.com* is the website for *Condé Nast Portfolio*, a business related magazine. In order to download articles from the magazine and convert them to e-books, we rely on the :term:`RSS` feeds of portfolio.com. A list of such feeds is available at http://www.portfolio.com/rss/.
|
||||
*portfolio.com* is the website for *Condé Nast Portfolio*, a business related magazine. In order to download articles from the magazine and convert them to ebooks, we rely on the :term:`RSS` feeds of portfolio.com. A list of such feeds is available at http://www.portfolio.com/rss/.
|
||||
|
||||
Lets pick a couple of feeds that look interesting:
|
||||
|
||||
#. Business Travel: http://feeds.portfolio.com/portfolio/businesstravel
|
||||
#. Tech Observer: http://feeds.portfolio.com/portfolio/thetechobserver
|
||||
|
||||
I got the URLs by clicking the little orange RSS icon next to each feed name. To make |app| download the feeds and convert them into an e-book, you should click the :guilabel:`Fetch news` button and then the :guilabel:`Add a custom news source` menu item. A dialog similar to that shown below should open up.
|
||||
I got the URLs by clicking the little orange RSS icon next to each feed name. To make |app| download the feeds and convert them into an ebook, you should click the :guilabel:`Fetch news` button and then the :guilabel:`Add a custom news source` menu item. A dialog similar to that shown below should open up.
|
||||
|
||||
.. image:: images/custom_news.png
|
||||
:align: center
|
||||
|
||||
First enter ``Portfolio`` into the :guilabel:`Recipe title` field. This will be the title of the e-book that will be created from the articles in the above feeds.
|
||||
First enter ``Portfolio`` into the :guilabel:`Recipe title` field. This will be the title of the ebook that will be created from the articles in the above feeds.
|
||||
|
||||
The next two fields (:guilabel:`Oldest article` and :guilabel:`Max. number of articles`) allow you some control over how many articles should be downloaded from each feed, and they are pretty self explanatory.
|
||||
|
||||
To add the feeds to the recipe, enter the feed title and the feed URL and click the :guilabel:`Add feed` button. Once you have added both feeds, simply click the :guilabel:`Add/update recipe` button and you're done! Close the dialog.
|
||||
|
||||
To test your new :term:`recipe`, click the :guilabel:`Fetch news` button and in the :guilabel:`Custom news sources` sub-menu click :guilabel:`Portfolio`. After a couple of minutes, the newly downloaded Portfolio e-book will appear in the main library view (if you have your reader connected, it will be put onto the reader instead of into the library). Select it and hit the :guilabel:`View` button to read!
|
||||
To test your new :term:`recipe`, click the :guilabel:`Fetch news` button and in the :guilabel:`Custom news sources` sub-menu click :guilabel:`Portfolio`. After a couple of minutes, the newly downloaded Portfolio ebook will appear in the main library view (if you have your reader connected, it will be put onto the reader instead of into the library). Select it and hit the :guilabel:`View` button to read!
|
||||
|
||||
The reason this worked so well, with so little effort is that *portfolio.com* provides *full-content* :term:`RSS` feeds, i.e., the article content is embedded in the feed itself. For most news sources that provide news in this fashion, with *full-content* feeds, you don't need any more effort to convert them to e-books. Now we will look at a news source that does not provide full content feeds. In such feeds, the full article is a webpage and the feed only contains a link to the webpage with a short summary of the article.
|
||||
The reason this worked so well, with so little effort is that *portfolio.com* provides *full-content* :term:`RSS` feeds, i.e., the article content is embedded in the feed itself. For most news sources that provide news in this fashion, with *full-content* feeds, you don't need any more effort to convert them to ebooks. Now we will look at a news source that does not provide full content feeds. In such feeds, the full article is a webpage and the feed only contains a link to the webpage with a short summary of the article.
|
||||
|
||||
.. _bbc:
|
||||
|
||||
@ -55,19 +55,19 @@ Lets try the following two feeds from *The BBC*:
|
||||
#. News Front Page: http://newsrss.bbc.co.uk/rss/newsonline_world_edition/front_page/rss.xml
|
||||
#. Science/Nature: http://newsrss.bbc.co.uk/rss/newsonline_world_edition/science/nature/rss.xml
|
||||
|
||||
Follow the procedure outlined in :ref:`portfolio` to create a recipe for *The BBC* (using the feeds above). Looking at the downloaded e-book, we see that |app| has done a creditable job of extracting only the content you care about from each article's webpage. However, the extraction process is not perfect. Sometimes it leaves in undesirable content like menus and navigation aids or it removes content that should have been left alone, like article headings. In order, to have perfect content extraction, we will need to customize the fetch process, as described in the next section.
|
||||
Follow the procedure outlined in :ref:`portfolio` to create a recipe for *The BBC* (using the feeds above). Looking at the downloaded ebook, we see that |app| has done a creditable job of extracting only the content you care about from each article's webpage. However, the extraction process is not perfect. Sometimes it leaves in undesirable content like menus and navigation aids or it removes content that should have been left alone, like article headings. In order, to have perfect content extraction, we will need to customize the fetch process, as described in the next section.
|
||||
|
||||
Customizing the fetch process
|
||||
--------------------------------
|
||||
|
||||
When you want to perfect the download process, or download content from a particularly complex website, you can avail yourself of all the power and flexibility of the :term:`recipe` framework. In order to do that, in the :guilabel:`Add custom news sources` dialog, simply click the :guilabel:`Switch to Advanced mode` button.
|
||||
|
||||
The easiest and often most productive customization is to use the print version of the online articles. The print version typically has much less cruft and translates much more smoothly to an e-book. Let's try to use the print version of the articles from *The BBC*.
|
||||
The easiest and often most productive customization is to use the print version of the online articles. The print version typically has much less cruft and translates much more smoothly to an ebook. Let's try to use the print version of the articles from *The BBC*.
|
||||
|
||||
Using the print version of bbc.co.uk
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The first step is to look at the e-book we downloaded previously from :ref:`bbc`. At the end of each article, in the e-book is a little blurb telling you where the article was downloaded from. Copy and paste that URL into a browser. Now on the article webpage look for a link that points to the "Printable version". Click it to see the print version of the article. It looks much neater! Now compare the two URLs. For me they were:
|
||||
The first step is to look at the ebook we downloaded previously from :ref:`bbc`. At the end of each article, in the ebook is a little blurb telling you where the article was downloaded from. Copy and paste that URL into a browser. Now on the article webpage look for a link that points to the "Printable version". Click it to see the print version of the article. It looks much neater! Now compare the two URLs. For me they were:
|
||||
|
||||
Article URL
|
||||
http://news.bbc.co.uk/2/hi/science/nature/7312016.stm
|
||||
@ -98,7 +98,7 @@ This is python, so indentation is important. After you've added the lines, it sh
|
||||
|
||||
In the above, ``def print_version(self, url)`` defines a *method* that is called by |app| for every article. ``url`` is the URL of the original article. What ``print_version`` does is take that url and replace it with the new URL that points to the print version of the article. To learn about `python <http://www.python.org>`_ see the `tutorial <http://docs.python.org/tut/>`_.
|
||||
|
||||
Now, click the :guilabel:`Add/update recipe` button and your changes will be saved. Re-download the e-book. You should have a much improved e-book. One of the problems with the new version is that the fonts on the print version webpage are too small. This is automatically fixed when converting to an e-book, but even after the fixing process, the font size of the menus and navigation bar to become too large relative to the article text. To fix this, we will do some more customization, in the next section.
|
||||
Now, click the :guilabel:`Add/update recipe` button and your changes will be saved. Re-download the ebook. You should have a much improved ebook. One of the problems with the new version is that the fonts on the print version webpage are too small. This is automatically fixed when converting to an ebook, but even after the fixing process, the font size of the menus and navigation bar to become too large relative to the article text. To fix this, we will do some more customization, in the next section.
|
||||
|
||||
Replacing article styles
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@ -131,7 +131,7 @@ This can be removed by adding::
|
||||
|
||||
remove_tags = [dict(name='div', attrs={'class':'footer'})]
|
||||
|
||||
to the recipe. Finally, lets replace some of the :term:`CSS` that we disabled earlier, with our own :term:`CSS` that is suitable for conversion to an e-book::
|
||||
to the recipe. Finally, lets replace some of the :term:`CSS` that we disabled earlier, with our own :term:`CSS` that is suitable for conversion to an ebook::
|
||||
|
||||
extra_css = '.headline {font-size: x-large;} \n .fact { padding-top: 10pt }'
|
||||
|
||||
@ -234,7 +234,7 @@ We see several new features in this :term:`recipe`. First, we have::
|
||||
|
||||
timefmt = ' [%a, %d %b, %Y]'
|
||||
|
||||
This sets the displayed time on the front page of the created e-book to be in the format,
|
||||
This sets the displayed time on the front page of the created ebook to be in the format,
|
||||
``Day, Day_Number Month, Year``. See :attr:`timefmt <calibre.web.feeds.news.BasicNewsRecipe.timefmt>`.
|
||||
|
||||
Then we see a group of directives to cleanup the downloaded :term:`HTML`::
|
||||
@ -254,7 +254,7 @@ The next interesting feature is::
|
||||
|
||||
``needs_subscription = True`` tells |app| that this recipe needs a username and password in order to access the content. This causes, |app| to ask for a username and password whenever you try to use this recipe. The code in :meth:`calibre.web.feeds.news.BasicNewsRecipe.get_browser` actually does the login into the NYT website. Once logged in, |app| will use the same, logged in, browser instance to fetch all content. See `mechanize <http://wwwsearch.sourceforge.net/mechanize/>`_ to understand the code in ``get_browser``.
|
||||
|
||||
The next new feature is the :meth:`calibre.web.feeds.news.BasicNewsRecipe.parse_index` method. Its job is to go to http://www.nytimes.com/pages/todayspaper/index.html and fetch the list of articles that appear in *todays* paper. While more complex than simply using :term:`RSS`, the recipe creates an e-book that corresponds very closely to the days paper. ``parse_index`` makes heavy use of `BeautifulSoup <http://www.crummy.com/software/BeautifulSoup/documentation.html>`_ to parse the daily paper webpage.
|
||||
The next new feature is the :meth:`calibre.web.feeds.news.BasicNewsRecipe.parse_index` method. Its job is to go to http://www.nytimes.com/pages/todayspaper/index.html and fetch the list of articles that appear in *todays* paper. While more complex than simply using :term:`RSS`, the recipe creates an ebook that corresponds very closely to the days paper. ``parse_index`` makes heavy use of `BeautifulSoup <http://www.crummy.com/software/BeautifulSoup/documentation.html>`_ to parse the daily paper webpage.
|
||||
|
||||
The final new feature is the :meth:`calibre.web.feeds.news.BasicNewsRecipe.preprocess_html` method. It can be used to perform arbitrary transformations on every downloaded HTML page. Here it is used to bypass the ads that the nytimes shows you before each article.
|
||||
|
||||
|
@ -51,19 +51,19 @@ collection_template = '''\
|
||||
'''
|
||||
|
||||
ABOUT='''\
|
||||
calibre is the one stop solution for all your e-book needs. It was created
|
||||
originally by Kovid Goyal, to help him manage his e-book collection and is now
|
||||
very actively developed by an international community of e-book enthusiasts.
|
||||
calibre is the one stop solution for all your ebook needs. It was created
|
||||
originally by Kovid Goyal, to help him manage his ebook collection and is now
|
||||
very actively developed by an international community of ebook enthusiasts.
|
||||
|
||||
Its goal is to empower you, the user, to do whatever you like with the e-books
|
||||
Its goal is to empower you, the user, to do whatever you like with the ebooks
|
||||
in your collection. You can convert them to many different formats, read them
|
||||
on your computer, send them to many different devices, edit their metadata
|
||||
and covers, etc.
|
||||
|
||||
calibre also allows you to download news from a variety of different sources all
|
||||
over the internet and read conveniently in e-books form. In keeping with its
|
||||
over the Internet and read conveniently in ebooks form. In keeping with its
|
||||
philosophy of empowering the user, it has a simple system to allow you to add
|
||||
your own favorite news sources. In fact, most the builtin news sources in
|
||||
your own favorite news sources. In fact, most the built-in news sources in
|
||||
calibre were originally contributed by users.
|
||||
'''
|
||||
|
||||
|
@ -179,7 +179,7 @@ The example shows several things:
|
||||
|
||||
* program mode is used if the expression begins with ``:'`` and ends with ``'``. Anything else is assumed to be single-function.
|
||||
* the variable ``$`` stands for the field the expression is operating upon, ``#series`` in this case.
|
||||
* functions must be given all their arguments. There is no default value. For example, the standard builtin functions must be given an additional initial parameter indicating the source field, which is a significant difference from single-function mode.
|
||||
* functions must be given all their arguments. There is no default value. For example, the standard built-in functions must be given an additional initial parameter indicating the source field, which is a significant difference from single-function mode.
|
||||
* white space is ignored and can be used anywhere within the expression.
|
||||
* constant strings are enclosed in matching quotes, either ``'`` or ``"``.
|
||||
|
||||
|
@ -11,10 +11,10 @@ PREAMBLE = '''\
|
||||
|
||||
.. _templaterefcalibre:
|
||||
|
||||
Reference for all builtin template language functions
|
||||
Reference for all built-in template language functions
|
||||
========================================================
|
||||
|
||||
Here, we document all the builtin functions available in the |app| template language. Every function is implemented as a class in python and you can click the source links to see the source code, in case the documentation is insufficient. The functions are arranged in logical groups by type.
|
||||
Here, we document all the built-in functions available in the |app| template language. Every function is implemented as a class in python and you can click the source links to see the source code, in case the documentation is insufficient. The functions are arranged in logical groups by type.
|
||||
|
||||
.. contents::
|
||||
:depth: 2
|
||||
|
2
src/calibre/manual/tutorials.rst
Normal file → Executable file
2
src/calibre/manual/tutorials.rst
Normal file → Executable file
@ -6,7 +6,7 @@
|
||||
Tutorials
|
||||
=======================================================
|
||||
|
||||
Here you will find tutorials to get you started using |app|'s more advanced features, like XPath and templates.
|
||||
Here you will find tutorials to get you started using |app|'s more advanced features, such as XPath and templates.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
30
src/calibre/manual/viewer.rst
Normal file → Executable file
30
src/calibre/manual/viewer.rst
Normal file → Executable file
@ -2,10 +2,10 @@
|
||||
|
||||
.. _viewer:
|
||||
|
||||
The E-book Viewer
|
||||
The Ebook Viewer
|
||||
=============================
|
||||
|
||||
|app| includes a built-in E-book viewer that can view all the major e-book formats.
|
||||
|app| includes a built-in ebook viewer that can view all the major ebook formats.
|
||||
The viewer is highly customizable and has many advanced features.
|
||||
|
||||
.. contents::
|
||||
@ -16,11 +16,11 @@ Starting the viewer
|
||||
--------------------
|
||||
|
||||
You can view any of the books in your |app| library by selecting the book and pressing the View button. This
|
||||
will open up the book in the e-book viewer. You can also launch the viewer by itself, from the Start menu in windows
|
||||
will open up the book in the ebook viewer. You can also launch the viewer by itself from the Start menu in Windows
|
||||
or using the command :command:`ebook-viewer` in Linux and OS X (you have to install the command line tools on OS X
|
||||
first by going to :guilabel:`Preferences->Advanced->Miscellaneous`).
|
||||
|
||||
Navigating around an e-book
|
||||
Navigating around an ebook
|
||||
-----------------------------
|
||||
|
||||
.. |pni| image:: images/prev_next.png
|
||||
@ -35,7 +35,7 @@ Navigating around an e-book
|
||||
|
||||
|
||||
You can "turn pages" in a book by using the :guilabel:`Page Next` and :guilabel:`Page Previous` buttons |pni|, or by pressing
|
||||
the Page Down/Page Up keys. Unlike most e-book viewers, |app| does not force you to view books in paged mode. You can
|
||||
the Page Down/Page Up keys. Unlike most ebook viewers, |app| does not force you to view books in paged mode. You can
|
||||
scroll by amounts less than a page by using the scroll bar or various customizable keyboard shortcuts.
|
||||
|
||||
Bookmarks
|
||||
@ -43,27 +43,27 @@ Bookmarks
|
||||
|
||||
When you are in the middle of a book and close the viewer, it will remember where you stopped reading and return there
|
||||
the next time you open the book. You can also set bookmarks in the book by using the Bookmark button |bookmi|. When viewing EPUB format
|
||||
books, these bookmarks are actually saved in the EPUB file itself, so you can add bookmarks, then send the file to a friend and
|
||||
when they open the file, they will be able to see your bookmarks.
|
||||
books, these bookmarks are actually saved in the EPUB file itself. You can add bookmarks, then send the file to a friend.
|
||||
When they open the file, they will be able to see your bookmarks.
|
||||
|
||||
Table of Contents
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If the book you are reading defines a Table of Contents, you can access it by pressing the Table of Contents button |toci|.
|
||||
This will bring up a list of sections in the book and you can click on any of them to jump to that portion of the book.
|
||||
This will bring up a list of sections in the book. You can click on any of them to jump to that portion of the book.
|
||||
|
||||
Navigating by location
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
E-books, unlike paper books have no concept of pages. Instead,
|
||||
Ebooks, unlike paper books, have no concept of pages. Instead,
|
||||
as you read through the book, you will notice that your position in the book is displayed in the upper left corner in a box
|
||||
like this |navposi|. This is both your current position and the total length of the book. These numbers are independent of the screen size and font
|
||||
size you are viewing the boko at, and they play a similar role to page numbers in paper books.
|
||||
size you are viewing the book at, and they play a similar role to page numbers in paper books.
|
||||
You can enter any number you like to go to the corresponding location in the book.
|
||||
|
||||
|app| also has a very handy
|
||||
reference mode. You can turn it on by clicking the Reference Mode button |refmi|. Once you do this, every time you move your
|
||||
mouse over a paragraph, calibre will display a unique number made up of the section and paragraph numbers.
|
||||
mouse over a paragraph, |app| will display a unique number made up of the section and paragraph numbers.
|
||||
|
||||
.. image:: images/ref_mode.png
|
||||
:align: center
|
||||
@ -72,8 +72,8 @@ You can use this number to unambiguously refer to parts of the books when discus
|
||||
in other works. You can enter these numbers in the box marked Go to at the top of the window to go to a particular
|
||||
reference location.
|
||||
|
||||
If you click on links inside the e-book to take you to different parts of the book, like an endnote, you can use the back and forward buttons
|
||||
in the top left corner to return to where you were. These button behave just like those in a web browser.
|
||||
If you click on links inside the ebook to take you to different parts of the book, such as an endnote, you can use the back and forward buttons
|
||||
in the top left corner to return to where you were. These buttons behave just like those in a web browser.
|
||||
|
||||
Customizing the look and feel of your reading experience
|
||||
------------------------------------------------------------
|
||||
@ -90,7 +90,7 @@ by the viewer to ones you like as well as the default font size when the viewer
|
||||
|
||||
More advanced customization can be achieved by the User Stylesheet setting. This is a stylesheet you can set that will be applied
|
||||
to every book. Using it you can do things like have white text on a black background, change paragraph styles, text justification, etc.
|
||||
For examples if custom stylesheets used by |app|'s users, see `the forums <http://www.mobileread.com/forums/showthread.php?t=51500>`_.
|
||||
For examples of custom stylesheets used by |app|'s users, see `the forums <http://www.mobileread.com/forums/showthread.php?t=51500>`_.
|
||||
|
||||
Dictionary lookup
|
||||
-------------------
|
||||
@ -101,6 +101,6 @@ server at ``dict.org`` to lookup words. The definition is displayed in a small b
|
||||
Copying text and images
|
||||
-------------------------
|
||||
|
||||
You can select text and images by dragging the content with your mouse and then right click to copy to the clipboard.
|
||||
You can select text and images by dragging the content with your mouse and then right clicking to copy to the clipboard.
|
||||
The copied material can be pasted into another application as plain text and images.
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user