mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Merge from trunk
This commit is contained in:
commit
3fc513ec2a
@ -19,6 +19,74 @@
|
||||
# new recipes:
|
||||
# - title:
|
||||
|
||||
- version: 0.8.4
|
||||
date: 2011-06-03
|
||||
|
||||
new features:
|
||||
- title: "New and much simpler interface for specifying column coloring via Preferences->Look & Feel->Column Coloring"
|
||||
|
||||
- title: "Driver for Trekstor eBook Player 5M, Samsung Galaxy SII I9100, Motorola Defy and miBuk GAMMA 6.2"
|
||||
tickets: [792091, 791216]
|
||||
|
||||
- title: "Get Books: Add EpubBud, WH Smits and E-book Shoppe stores"
|
||||
|
||||
- title: "When deleting 'all formats except ...', do not delete if it leaves a book with no formats"
|
||||
|
||||
- title: "Change default toolbar to make it a little more new user friendly. The icons have been re-arranged and now the text is always visiblke by default. You can change that in Preferences->Look & Feeel and Preferences->Toolbar"
|
||||
|
||||
- title: "Windows installer: Remember and use previous settings for installing desktop icons, adding to path, etc. This makes the installer a little slower, complaints should go to Microsoft."
|
||||
|
||||
- title: "Template language: Add str_in_list and on_device formatter functions. Make debugging templates a little easier"
|
||||
|
||||
- title: "Allow the user to specify formatting for number type custom columns"
|
||||
|
||||
bug fixes:
|
||||
- title: "Fix typo in NOOK TSR driver that prevented it from working on windows"
|
||||
|
||||
- title: "Fix quotes in identifiers causing Tag Browser to be blank."
|
||||
tickets: [791044]
|
||||
|
||||
- title: "Speedup auto complete when there are lots of items (>2500) the downside being that non ASCII characters are not sorted correctly. The threshold can be controlled via Preferences->Tweaks"
|
||||
tickets: [792191]
|
||||
|
||||
- title: "RTF Output: Fix handling of curly brackets"
|
||||
tickets: [791805]
|
||||
|
||||
- title: "Fix searching in Get Books not working with non ASCII characters"
|
||||
tickets: [791788]
|
||||
|
||||
- title: "Fix excessive memory consumption when moving very large files during a metadata change"
|
||||
tickets: [791806]
|
||||
|
||||
- title: "Fix series index being overwritten even when series is turned off in bulk metadata download"
|
||||
tickets: [789990]
|
||||
|
||||
- title: "Fix regression in templates where id and other non standard fields no longer worked."
|
||||
|
||||
- title: "EPUB Output: Fix crash caused by ids with non-ascii characters in them"
|
||||
|
||||
- title: "Try to preserve the timestamps of files in a ZIP container"
|
||||
|
||||
- title: "After adding books always select the most recently added book."
|
||||
tickets: [789343]
|
||||
|
||||
improved recipes:
|
||||
- bild.de
|
||||
- CNN
|
||||
- BBC News (fast)
|
||||
- Dilema Veche
|
||||
|
||||
new recipes:
|
||||
- title: Metro UK
|
||||
author: Dave Asbury
|
||||
|
||||
- title: Alt om Herning and Version2.dk
|
||||
author: Rasmus Lauritsen
|
||||
|
||||
- title: Observatorul cultural
|
||||
author: song2
|
||||
|
||||
|
||||
- version: 0.8.3
|
||||
date: 2011-05-27
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
"uppercase": "def evaluate(self, formatter, kwargs, mi, locals, val):\n return val.upper()\n",
|
||||
"strcat": "def evaluate(self, formatter, kwargs, mi, locals, *args):\n i = 0\n res = ''\n for i in range(0, len(args)):\n res += args[i]\n return res\n",
|
||||
"in_list": "def evaluate(self, formatter, kwargs, mi, locals, val, sep, pat, fv, nfv):\n l = [v.strip() for v in val.split(sep) if v.strip()]\n if l:\n for v in l:\n if re.search(pat, v, flags=re.I):\n return fv\n return nfv\n",
|
||||
"multiply": "def evaluate(self, formatter, kwargs, mi, locals, x, y):\n x = float(x if x else 0)\n y = float(y if y else 0)\n return unicode(x * y)\n",
|
||||
"not": "def evaluate(self, formatter, kwargs, mi, locals, *args):\n i = 0\n while i < len(args):\n if args[i]:\n return '1'\n i += 1\n return ''\n",
|
||||
"ifempty": "def evaluate(self, formatter, kwargs, mi, locals, val, value_if_empty):\n if val:\n return val\n else:\n return value_if_empty\n",
|
||||
"booksize": "def evaluate(self, formatter, kwargs, mi, locals):\n if mi.book_size is not None:\n try:\n return str(mi.book_size)\n except:\n pass\n return ''\n",
|
||||
"select": "def evaluate(self, formatter, kwargs, mi, locals, val, key):\n if not val:\n return ''\n vals = [v.strip() for v in val.split(',')]\n for v in vals:\n if v.startswith(key+':'):\n return v[len(key)+1:]\n return ''\n",
|
||||
@ -27,9 +27,10 @@
|
||||
"sublist": "def evaluate(self, formatter, kwargs, mi, locals, val, start_index, end_index, sep):\n if not val:\n return ''\n si = int(start_index)\n ei = int(end_index)\n val = val.split(sep)\n try:\n if ei == 0:\n return sep.join(val[si:])\n else:\n return sep.join(val[si:ei])\n except:\n return ''\n",
|
||||
"test": "def evaluate(self, formatter, kwargs, mi, locals, val, value_if_set, value_not_set):\n if val:\n return value_if_set\n else:\n return value_not_set\n",
|
||||
"eval": "def evaluate(self, formatter, kwargs, mi, locals, template):\n from formatter import eval_formatter\n template = template.replace('[[', '{').replace(']]', '}')\n return eval_formatter.safe_format(template, locals, 'EVAL', None)\n",
|
||||
"not": "def evaluate(self, formatter, kwargs, mi, locals, *args):\n i = 0\n while i < len(args):\n if args[i]:\n return '1'\n i += 1\n return ''\n",
|
||||
"format_date": "def evaluate(self, formatter, kwargs, mi, locals, val, format_string):\n if not val:\n return ''\n try:\n dt = parse_date(val)\n s = format_date(dt, format_string)\n except:\n s = 'BAD DATE'\n return s\n",
|
||||
"multiply": "def evaluate(self, formatter, kwargs, mi, locals, x, y):\n x = float(x if x else 0)\n y = float(y if y else 0)\n return unicode(x * y)\n",
|
||||
"format_date": "def evaluate(self, formatter, kwargs, mi, locals, val, format_string):\n if not val or val == 'None':\n return ''\n try:\n dt = parse_date(val)\n s = format_date(dt, format_string)\n except:\n s = 'BAD DATE'\n return s\n",
|
||||
"capitalize": "def evaluate(self, formatter, kwargs, mi, locals, val):\n return capitalize(val)\n",
|
||||
"identifier_in_list": "def evaluate(self, formatter, kwargs, mi, locals, val, ident, fv, nfv):\n l = [v.strip() for v in val.split(',') if v.strip()]\n (id, _, regexp) = ident.partition(':')\n if not id:\n return nfv\n id += ':'\n if l:\n for v in l:\n if v.startswith(id):\n if not regexp or re.search(regexp, v[len(id):], flags=re.I):\n return fv\n return nfv\n",
|
||||
"count": "def evaluate(self, formatter, kwargs, mi, locals, val, sep):\n return unicode(len(val.split(sep)))\n",
|
||||
"lowercase": "def evaluate(self, formatter, kwargs, mi, locals, val):\n return val.lower()\n",
|
||||
"substr": "def evaluate(self, formatter, kwargs, mi, locals, str_, start_, end_):\n return str_[int(start_): len(str_) if int(end_) == 0 else int(end_)]\n",
|
||||
@ -38,5 +39,5 @@
|
||||
"ondevice": "def evaluate(self, formatter, kwargs, mi, locals):\n if mi.ondevice_col:\n return _('Yes')\n return ''\n",
|
||||
"assign": "def evaluate(self, formatter, kwargs, mi, locals, target, value):\n locals[target] = value\n return value\n",
|
||||
"raw_field": "def evaluate(self, formatter, kwargs, mi, locals, name):\n return unicode(getattr(mi, name, None))\n",
|
||||
"cmp": "def evaluate(self, formatter, kwargs, mi, locals, x, y, lt, eq, gt):\n x = float(x if x else 0)\n y = float(y if y else 0)\n if x < y:\n return lt\n if x == y:\n return eq\n return gt\n"
|
||||
"cmp": "def evaluate(self, formatter, kwargs, mi, locals, x, y, lt, eq, gt):\n x = float(x if x and x != 'None' else 0)\n y = float(y if y and y != 'None' else 0)\n if x < y:\n return lt\n if x == y:\n return eq\n return gt\n"
|
||||
}
|
@ -187,7 +187,6 @@ msgstr ""
|
||||
|
||||
'''%dict(appname=__appname__, version=version, year=time.strftime('%Y'))
|
||||
|
||||
|
||||
def usage(code, msg=''):
|
||||
print >> sys.stderr, __doc__ % globals()
|
||||
if msg:
|
||||
|
@ -4,7 +4,7 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
__appname__ = u'calibre'
|
||||
numeric_version = (0, 8, 3)
|
||||
numeric_version = (0, 8, 4)
|
||||
__version__ = u'.'.join(map(unicode, numeric_version))
|
||||
__author__ = u"Kovid Goyal <kovid@kovidgoyal.net>"
|
||||
|
||||
|
@ -33,7 +33,7 @@ class Matches(QAbstractItemModel):
|
||||
|
||||
total_changed = pyqtSignal(int)
|
||||
|
||||
HEADERS = [_('Cover'), _('Title'), _('Price'), _('DRM'), _('Store'), _('')]
|
||||
HEADERS = [_('Cover'), _('Title'), _('Price'), _('DRM'), _('Store'), '']
|
||||
HTML_COLS = (1, 4)
|
||||
|
||||
def __init__(self, cover_thread_count=2, detail_thread_count=4):
|
||||
@ -58,7 +58,7 @@ class Matches(QAbstractItemModel):
|
||||
self.search_filter = SearchFilter()
|
||||
self.cover_pool = CoverThreadPool(cover_thread_count)
|
||||
self.details_pool = DetailsThreadPool(detail_thread_count)
|
||||
|
||||
|
||||
self.filter_results_dispatcher = FunctionDispatcher(self.filter_results)
|
||||
self.got_result_details_dispatcher = FunctionDispatcher(self.got_result_details)
|
||||
|
||||
|
@ -127,10 +127,12 @@ class Rule(object): # {{{
|
||||
val, lt, eq, gt)
|
||||
|
||||
def multiple_condition(self, col, action, val, sep):
|
||||
if not sep or sep == '|':
|
||||
sep = ','
|
||||
if action == 'is set':
|
||||
return "test('%s', '1', '')"%col
|
||||
return "test(field('%s'), '1', '')"%col
|
||||
if action == 'is not set':
|
||||
return "test('%s', '', '1')"%col
|
||||
return "test(field('%s'), '', '1')"%col
|
||||
if action == 'has':
|
||||
return "str_in_list(field('%s'), '%s', \"%s\", '1', '')"%(col, sep, val)
|
||||
if action == 'does not have':
|
||||
@ -142,9 +144,9 @@ class Rule(object): # {{{
|
||||
|
||||
def text_condition(self, col, action, val):
|
||||
if action == 'is set':
|
||||
return "test('%s', '1', '')"%col
|
||||
return "test(field('%s'), '1', '')"%col
|
||||
if action == 'is not set':
|
||||
return "test('%s', '', '1')"%col
|
||||
return "test(field('%s'), '', '1')"%col
|
||||
if action == 'is':
|
||||
return "strcmp(field('%s'), \"%s\", '', '1', '')"%(col, val)
|
||||
if action == 'is not':
|
||||
@ -181,7 +183,9 @@ def rule_from_template(fm, template):
|
||||
def conditionable_columns(fm):
|
||||
for key in fm:
|
||||
m = fm[key]
|
||||
if m.get('name', False) and m['kind'] == 'field':
|
||||
dt = m['datatype']
|
||||
if m.get('name', False) and dt in ('bool', 'int', 'float', 'rating', 'series',
|
||||
'comments', 'text', 'enumeration', 'datetime', 'composite'):
|
||||
if key == 'sort':
|
||||
yield 'title_sort'
|
||||
else:
|
||||
|
@ -74,10 +74,7 @@ Edit metadata
|
||||
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. **Download only metadata**: Downloads only metadata (if available), for the books that are selected in the book list.
|
||||
5. **Download only covers**: Downloads only covers (if available), for the books that are selected in the book list.
|
||||
6. **Download only social metadata**: Downloads only social metadata such as tags and reviews (if available), for the books that are selected in the book list.
|
||||
7. **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.
|
||||
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.
|
||||
|
||||
For more details see :ref:`metadata`.
|
||||
|
||||
|
@ -11,7 +11,7 @@ You can "install" calibre onto a USB stick that you can take with you and use on
|
||||
* Run a Mobile Calibre installation with both the Calibre binaries and your ebook library resident on a USB disk or other portable media. In particular it is not necessary to have Calibre installed on the Windows PC that is to run Calibre. This batch file also does not care what drive letter is assigned when you plug in the USB device. It also will not affect any settings on the host machine being a completely self-contained Calibre installation.
|
||||
* Run a networked Calibre installation optimised for performance when the ebook files are located on a networked share.
|
||||
|
||||
If you find setting up the bat file too challenging, there is a third party portable calibre build available at `portableapps.com http://portableapps.com`_.
|
||||
If you find setting up the bat file too challenging, there is a third party portable calibre build available at `portableapps.com <http://portableapps.com>`_.
|
||||
|
||||
This calibre-portable.bat file is intended for use on Windows based systems, but the principles are easily adapted for use on Linux or OS X based systems. Note that calibre requires the Microsoft Visual C++ 2008 runtimes to run. Most windows computers have them installed already, but it may be a good idea to have the installer for installing them on your USB stick. The installer is available from `Microsoft <http://www.microsoft.com/downloads/details.aspx?FamilyID=9b2da534-3e03-4391-8a4d-074b9f2bc1bf&displaylang=en>`_.
|
||||
|
||||
|
49
src/calibre/manual/templates/search.html
Normal file
49
src/calibre/manual/templates/search.html
Normal file
@ -0,0 +1,49 @@
|
||||
{#
|
||||
basic/search.html
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Template for the search page, using google adsense search
|
||||
|
||||
:copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
#}
|
||||
{% extends "layout.html" %}
|
||||
{% set title = _('Search') %}
|
||||
{% block extrahead %}
|
||||
<link rel="stylesheet" href="http://www.google.com/cse/style/look/default.css" type="text/css" />
|
||||
{{ super() }}
|
||||
{% endblock %}
|
||||
{% block body %}
|
||||
<h1 id="search-documentation">{{ _('Search') }}</h1>
|
||||
<div id="fallback" class="admonition warning">
|
||||
<script type="text/javascript">$('#fallback').hide();</script>
|
||||
<p>
|
||||
{% trans %}Please activate JavaScript to enable the search
|
||||
functionality.{% endtrans %}
|
||||
</p>
|
||||
</div>
|
||||
<p>
|
||||
{% trans %}From here you can search these documents. Enter your search
|
||||
words into the box below and click "search". Note that the search
|
||||
function will automatically search for all of the words. Pages
|
||||
containing fewer words won't appear in the result list.{% endtrans %}
|
||||
</p>
|
||||
<div id="cse" style="width: 100%;">Loading</div>
|
||||
<script src="http://www.google.com/jsapi" type="text/javascript"></script>
|
||||
<script type="text/javascript">
|
||||
google.load('search', '1');
|
||||
google.setOnLoadCallback(function() {
|
||||
var customSearchControl = new google.search.CustomSearchControl('partner-pub-2595272032872519:0657791363');
|
||||
customSearchControl.setResultSetSize(google.search.Search.FILTERED_CSE_RESULTSET);
|
||||
var options = new google.search.DrawOptions();
|
||||
options.setAutoComplete(true);
|
||||
customSearchControl.draw('cse', options);
|
||||
var params = $.getQueryParameters();
|
||||
if (params.q) {
|
||||
var query = params.q[0];
|
||||
$('input[name="search"]')[0].value = query;
|
||||
$('input.gsc-search-button')[0].click();
|
||||
}
|
||||
}, true);
|
||||
</script>
|
||||
{% endblock %}
|
File diff suppressed because it is too large
Load Diff
@ -6,7 +6,7 @@ __docformat__ = 'restructuredtext en'
|
||||
'''
|
||||
Manage application-wide preferences.
|
||||
'''
|
||||
import os, re, cPickle, base64, datetime, json, plistlib
|
||||
import os, cPickle, base64, datetime, json, plistlib
|
||||
from copy import deepcopy
|
||||
from optparse import OptionParser as _OptionParser
|
||||
from optparse import IndentedHelpFormatter
|
||||
@ -22,7 +22,7 @@ if False:
|
||||
# Make pyflakes happy
|
||||
Config, ConfigProxy, Option, OptionValues, StringConfig
|
||||
OptionSet, ConfigInterface, read_tweaks, write_tweaks
|
||||
read_raw_tweaks, tweaks, plugin_dir
|
||||
read_raw_tweaks, tweaks, plugin_dir, prefs
|
||||
|
||||
def check_config_write_access():
|
||||
return os.access(config_dir, os.W_OK) and os.access(config_dir, os.X_OK)
|
||||
@ -353,71 +353,4 @@ class JSONConfig(XMLConfig):
|
||||
|
||||
|
||||
|
||||
def migrate():
|
||||
if hasattr(os, 'geteuid') and os.geteuid() == 0:
|
||||
return
|
||||
p = prefs
|
||||
if p.get('migrated'):
|
||||
return
|
||||
|
||||
from PyQt4.QtCore import QSettings, QVariant
|
||||
class Settings(QSettings):
|
||||
|
||||
def __init__(self, name='calibre2'):
|
||||
QSettings.__init__(self, QSettings.IniFormat, QSettings.UserScope,
|
||||
'kovidgoyal.net', name)
|
||||
|
||||
def get(self, key, default=None):
|
||||
try:
|
||||
key = str(key)
|
||||
if not self.contains(key):
|
||||
return default
|
||||
val = str(self.value(key, QVariant()).toByteArray())
|
||||
if not val:
|
||||
return None
|
||||
return cPickle.loads(val)
|
||||
except:
|
||||
return default
|
||||
|
||||
s, migrated = Settings(), set([])
|
||||
all_keys = set(map(unicode, s.allKeys()))
|
||||
from calibre.gui2 import config, dynamic
|
||||
def _migrate(key, safe=None, from_qvariant=None, p=config):
|
||||
try:
|
||||
if key not in all_keys:
|
||||
return
|
||||
if safe is None:
|
||||
safe = re.sub(r'[^0-9a-zA-Z]', '_', key)
|
||||
val = s.get(key)
|
||||
if from_qvariant is not None:
|
||||
val = getattr(s.value(key), from_qvariant)()
|
||||
p.set(safe, val)
|
||||
except:
|
||||
pass
|
||||
finally:
|
||||
migrated.add(key)
|
||||
|
||||
|
||||
_migrate('database path', p=prefs)
|
||||
_migrate('filename pattern', p=prefs)
|
||||
_migrate('network timeout', p=prefs)
|
||||
_migrate('isbndb.com key', p=prefs)
|
||||
|
||||
_migrate('frequently used directories')
|
||||
_migrate('send to device by default')
|
||||
_migrate('save to disk single format')
|
||||
_migrate('confirm delete')
|
||||
_migrate('show text in toolbar')
|
||||
_migrate('new version notification')
|
||||
_migrate('use roman numerals for series number')
|
||||
_migrate('cover flow queue length')
|
||||
_migrate('LRF conversion defaults')
|
||||
_migrate('LRF ebook viewer options')
|
||||
|
||||
for key in all_keys - migrated:
|
||||
if key.endswith(': un') or key.endswith(': pw'):
|
||||
_migrate(key, p=dynamic)
|
||||
p.set('migrated', True)
|
||||
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user