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
ee685cdac8
@ -6,7 +6,7 @@ __docformat__ = 'restructuredtext en'
|
||||
|
||||
'''
|
||||
Contains various tweaks that affect calibre behavior. Only edit this file if
|
||||
you know what you are dong. If you delete this file, it will be recreated from
|
||||
you know what you are doing. If you delete this file, it will be recreated from
|
||||
defaults.
|
||||
'''
|
||||
|
||||
|
BIN
resources/images/news/la_razon_bo.png
Normal file
BIN
resources/images/news/la_razon_bo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 356 B |
BIN
resources/images/news/los_tiempos_bo.png
Normal file
BIN
resources/images/news/los_tiempos_bo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 464 B |
64
resources/recipes/la_razon_bo.recipe
Normal file
64
resources/recipes/la_razon_bo.recipe
Normal file
@ -0,0 +1,64 @@
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2010, Darko Miletic <darko.miletic at gmail.com>'
|
||||
'''
|
||||
www.la-razon.com
|
||||
'''
|
||||
|
||||
from calibre import strftime
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class LaRazon_Bol(BasicNewsRecipe):
|
||||
title = 'La Razón - Bolivia'
|
||||
__author__ = 'Darko Miletic'
|
||||
description = 'El diario nacional de Bolivia'
|
||||
publisher = 'Praxsis S.R.L.'
|
||||
category = 'news, politics, Bolivia'
|
||||
oldest_article = 1
|
||||
max_articles_per_feed = 200
|
||||
no_stylesheets = True
|
||||
encoding = 'cp1252'
|
||||
use_embedded_content = False
|
||||
language = 'es'
|
||||
publication_type = 'newspaper'
|
||||
delay = 1
|
||||
remove_empty_feeds = True
|
||||
cover_url = strftime('http://www.la-razon.com/portadas/%Y%m%d_LaRazon.jpg')
|
||||
masthead_url = 'http://www.la-razon.com/imagenes/logo.jpg'
|
||||
extra_css = """ body{font-family: Arial,Helvetica,sans-serif }
|
||||
img{margin-bottom: 0.4em}
|
||||
.noticia-titulo{font-family: Georgia,"Times New Roman",Times,serif}
|
||||
.lead{font-weight: bold; font-size: 0.8em}
|
||||
"""
|
||||
|
||||
conversion_options = {
|
||||
'comment' : description
|
||||
, 'tags' : category
|
||||
, 'publisher' : publisher
|
||||
, 'language' : language
|
||||
}
|
||||
|
||||
keep_only_tags = [dict(name='div', attrs={'class':['noticia-titulo','noticia-desarrollo']})]
|
||||
remove_tags = [dict(name=['meta','link','form','iframe','embed','object'])]
|
||||
remove_attributes = ['width','height']
|
||||
|
||||
feeds = [
|
||||
(u'Editorial' , u'http://www.la-razon.com/rss_editorial.php' )
|
||||
,(u'Opinión' , u'http://www.la-razon.com/rss_opinion.php' )
|
||||
,(u'Nacional' , u'http://www.la-razon.com/rss_nacional.php' )
|
||||
,(u'Economia' , u'http://www.la-razon.com/rss_economia.php' )
|
||||
,(u'Ciudades' , u'http://www.la-razon.com/rss_ciudades.php' )
|
||||
,(u'Sociedad' , u'http://www.la-razon.com/rss_sociedad.php' )
|
||||
,(u'Mundo' , u'http://www.la-razon.com/rss_sociedad.php' )
|
||||
,(u'La Revista' , u'http://www.la-razon.com/rss_larevista.php' )
|
||||
,(u'Sociales' , u'http://www.la-razon.com/rss_sociales.php' )
|
||||
,(u'Mia' , u'http://www.la-razon.com/rss_mia.php' )
|
||||
,(u'Marcas' , u'http://www.la-razon.com/rss_marcas.php' )
|
||||
,(u'Escape' , u'http://www.la-razon.com/rss_escape.php' )
|
||||
,(u'El Financiero' , u'http://www.la-razon.com/rss_financiero.php')
|
||||
,(u'Tendencias' , u'http://www.la-razon.com/rss_tendencias.php')
|
||||
]
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
for item in soup.findAll(style=True):
|
||||
del item['style']
|
||||
return soup
|
63
resources/recipes/los_tiempos_bo.recipe
Normal file
63
resources/recipes/los_tiempos_bo.recipe
Normal file
@ -0,0 +1,63 @@
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2010, Darko Miletic <darko.miletic at gmail.com>'
|
||||
'''
|
||||
www.lostiempos.com
|
||||
'''
|
||||
|
||||
from calibre import strftime
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class LosTiempos_Bol(BasicNewsRecipe):
|
||||
title = 'Los Tiempos - Bolivia'
|
||||
__author__ = 'Darko Miletic'
|
||||
description = 'El periódico de mayor circulación en la ciudad de Cochabamba, Bolivia'
|
||||
publisher = 'Los Tiempos'
|
||||
category = 'news, politics, Bolivia'
|
||||
oldest_article = 1
|
||||
max_articles_per_feed = 200
|
||||
no_stylesheets = True
|
||||
encoding = 'cp1252'
|
||||
use_embedded_content = False
|
||||
language = 'es'
|
||||
publication_type = 'newspaper'
|
||||
delay = 1
|
||||
remove_empty_feeds = True
|
||||
cover_url = strftime('http://www.lostiempos.com/media_recortes/%Y/%m/%d/portada_md_1.jpg')
|
||||
masthead_url = 'http://www.lostiempos.com/img_stat/logo_tiempos_sin_beta.jpg'
|
||||
extra_css = """ body{font-family: Arial,Helvetica,sans-serif }
|
||||
img{margin-bottom: 0.4em}
|
||||
h1,.hora,.breadcum,.pie_foto{font-family: Georgia,"Times New Roman",Times,serif}
|
||||
.hora,.breadcum,.pie_foto{font-size: small}
|
||||
.en_gris,.pie_foto{color: #666666}
|
||||
"""
|
||||
|
||||
conversion_options = {
|
||||
'comment' : description
|
||||
, 'tags' : category
|
||||
, 'publisher' : publisher
|
||||
, 'language' : language
|
||||
}
|
||||
|
||||
keep_only_tags = [dict(name='div', attrs={'id':'articulo'})]
|
||||
remove_tags = [
|
||||
dict(name=['meta','link','form','iframe','embed','object','hr'])
|
||||
,dict(attrs={'class':['caja_fonts sin_border_bot','pub']})
|
||||
]
|
||||
remove_attributes = ['width','height']
|
||||
|
||||
feeds = [
|
||||
(u'Nacional' , u'http://www.lostiempos.com/rss/lostiempos-nacional.xml' )
|
||||
,(u'Local' , u'http://www.lostiempos.com/rss/lostiempos-local.xml' )
|
||||
,(u'Deportes' , u'http://www.lostiempos.com/rss/lostiempos-deportes.xml' )
|
||||
,(u'Economía' , u'http://www.lostiempos.com/rss/lostiempos-economia.xml' )
|
||||
,(u'Internacional' , u'http://www.lostiempos.com/rss/lostiempos-internacional.xml' )
|
||||
,(u'Vida y Futuro' , u'http://www.lostiempos.com/rss/lostiempos-vida-y-futuro.xml' )
|
||||
,(u'Tragaluz' , u'http://www.lostiempos.com/rss/lostiempos-tragaluz.xml' )
|
||||
,(u'Opiniones' , u'http://www.lostiempos.com/rss/lostiempos-opiniones.xml' )
|
||||
]
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
for item in soup.findAll(style=True):
|
||||
del item['style']
|
||||
return soup
|
||||
|
@ -6,7 +6,6 @@ nspm.rs
|
||||
|
||||
import re
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
from calibre.ebooks.BeautifulSoup import Tag
|
||||
|
||||
class Nspm(BasicNewsRecipe):
|
||||
title = 'Nova srpska politicka misao'
|
||||
@ -22,14 +21,14 @@ class Nspm(BasicNewsRecipe):
|
||||
encoding = 'utf-8'
|
||||
language = 'sr'
|
||||
delay = 2
|
||||
publication_type = 'magazine'
|
||||
publication_type = 'magazine'
|
||||
masthead_url = 'http://www.nspm.rs/templates/jsn_epic_pro/images/logol.jpg'
|
||||
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: "Times New Roman", serif1, serif}
|
||||
.article_description{font-family: Arial, sans1, sans-serif}
|
||||
img{margin-top:0.5em; margin-bottom: 0.7em}
|
||||
.author{color: #990000; font-weight: bold}
|
||||
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: "Times New Roman", serif1, serif}
|
||||
.article_description{font-family: Arial, sans1, sans-serif}
|
||||
img{margin-top:0.5em; margin-bottom: 0.7em}
|
||||
.author{color: #990000; font-weight: bold}
|
||||
.author,.createdate{font-size: 0.9em} """
|
||||
|
||||
conversion_options = {
|
||||
@ -68,4 +67,4 @@ class Nspm(BasicNewsRecipe):
|
||||
def preprocess_html(self, soup):
|
||||
for item in soup.body.findAll(style=True):
|
||||
del item['style']
|
||||
return self.adeify_images(soup)
|
||||
return self.adeify_images(soup)
|
||||
|
34
resources/recipes/yahoo_news.recipe
Normal file
34
resources/recipes/yahoo_news.recipe
Normal file
@ -0,0 +1,34 @@
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
import re
|
||||
|
||||
class YahooNews(BasicNewsRecipe):
|
||||
title = 'Yahoo News'
|
||||
__author__ = 'Starson17'
|
||||
description = 'Yahoo-Science'
|
||||
language = 'en'
|
||||
use_embedded_content= False
|
||||
no_stylesheets = True
|
||||
linearize_tables = True
|
||||
oldest_article = 24
|
||||
remove_javascript = True
|
||||
remove_empty_feeds = True
|
||||
max_articles_per_feed = 10
|
||||
|
||||
feeds = [#There are dozens of other feeds at http://news.yahoo.com/rss
|
||||
(u'Top Stories', u'http://rss.news.yahoo.com/rss/topstories'),
|
||||
(u'Science', u'http://rss.news.yahoo.com/rss/science')
|
||||
]
|
||||
|
||||
keep_only_tags = [dict(name='div', attrs={'id':'yn-story'})]
|
||||
|
||||
remove_tags = [dict(name='div', attrs={'class':['hd', 'ft', 'yn-share-social']}),
|
||||
dict(name='div', attrs={'id':['yn-story-minor-media']})]
|
||||
|
||||
preprocess_regexps = [(re.compile(r'<span>Play Video</span>', re.DOTALL),lambda match: '<span></span>')]
|
||||
|
||||
extra_css = '''
|
||||
h1{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:large;}
|
||||
h2{font-family:Arial,Helvetica,sans-serif; font-weight:normal;font-size:small;}
|
||||
p{font-family:Arial,Helvetica,sans-serif;font-size:small;}
|
||||
body{font-family:Helvetica,Arial,sans-serif;font-size:small;}
|
||||
'''
|
@ -19,8 +19,8 @@ class ANDROID(USBMS):
|
||||
|
||||
VENDOR_ID = {
|
||||
# HTC
|
||||
0x0bb4 : { 0x0c02 : [0x100, 0x227], 0x0c01 : [0x100, 0x227], 0x0ff9
|
||||
: [0x0100, 0x227]},
|
||||
0x0bb4 : { 0x0c02 : [0x100, 0x0227], 0x0c01 : [0x100, 0x0227], 0x0ff9
|
||||
: [0x0100, 0x0227, 0x0226]},
|
||||
|
||||
# Motorola
|
||||
0x22b8 : { 0x41d9 : [0x216], 0x2d67 : [0x100], 0x41db : [0x216],
|
||||
|
@ -95,11 +95,11 @@ class ITUNES(DriverBase):
|
||||
|
||||
# Product IDs:
|
||||
# 0x1291 iPod Touch
|
||||
# 0x1292 iPhone 3G
|
||||
# 0x1293 iPod Touch 2G
|
||||
# 0x1299 iPod Touch 3G
|
||||
# 0x1292 iPhone 3G
|
||||
# 0x1294 iPhone 3GS
|
||||
# 0x1297 iPhone 4
|
||||
# 0x1299 iPod Touch 3G
|
||||
# 0x129a iPad
|
||||
VENDOR_ID = [0x05ac]
|
||||
PRODUCT_ID = [0x1292,0x1293,0x1294,0x1297,0x1299,0x129a]
|
||||
@ -1029,8 +1029,13 @@ class ITUNES(DriverBase):
|
||||
sys.stdout.write("\n")
|
||||
sys.stdout.flush()
|
||||
|
||||
# This doesn't seem to work with Device, just Library
|
||||
if False:
|
||||
'''
|
||||
Preferred
|
||||
Disabled because op_status.Tracks never returns a value after adding file
|
||||
This would be the preferred approach (as under OSX)
|
||||
It works in _add_library_book()
|
||||
'''
|
||||
if DEBUG:
|
||||
sys.stdout.write(" waiting for handle to added '%s' ..." % metadata.title)
|
||||
sys.stdout.flush()
|
||||
@ -1044,15 +1049,19 @@ class ITUNES(DriverBase):
|
||||
print
|
||||
added = op_status.Tracks[0]
|
||||
else:
|
||||
# This approach simply scans Library|Books for the book we just added
|
||||
|
||||
# Try the calibre metadata first
|
||||
'''
|
||||
Hackish
|
||||
Search Library|Books for the book we just added
|
||||
PDF file name is added title - need to search for base filename w/o extension
|
||||
'''
|
||||
format = fpath.rpartition('.')[2].lower()
|
||||
base_fn = fpath.rpartition(os.sep)[2]
|
||||
base_fn = base_fn.rpartition('.')[0]
|
||||
db_added = self._find_device_book(
|
||||
{'title': metadata.title,
|
||||
'author': metadata.authors[0],
|
||||
'uuid': metadata.uuid,
|
||||
'format': fpath.rpartition('.')[2].lower()})
|
||||
|
||||
{ 'title': base_fn if format == 'pdf' else metadata.title,
|
||||
'author': metadata.authors[0],
|
||||
'uuid': metadata.uuid,
|
||||
'format': format})
|
||||
return db_added
|
||||
|
||||
def _add_library_book(self,file, metadata):
|
||||
@ -1087,7 +1096,12 @@ class ITUNES(DriverBase):
|
||||
sys.stdout.write("\n")
|
||||
sys.stdout.flush()
|
||||
|
||||
if False:
|
||||
if True:
|
||||
'''
|
||||
Preferable
|
||||
Originally disabled because op_status.Tracks never returned a value
|
||||
after adding file. Seems to be working with iTunes 9.2.1.5 06 Aug 2010
|
||||
'''
|
||||
if DEBUG:
|
||||
sys.stdout.write(" waiting for handle to added '%s' ..." % metadata.title)
|
||||
sys.stdout.flush()
|
||||
@ -1100,12 +1114,19 @@ class ITUNES(DriverBase):
|
||||
print
|
||||
added = op_status.Tracks[0]
|
||||
else:
|
||||
# This approach simply scans Library|Books for the book we just added
|
||||
'''
|
||||
Hackish
|
||||
Search Library|Books for the book we just added
|
||||
PDF file name is added title - need to search for base filename w/o extension
|
||||
'''
|
||||
format = file.rpartition('.')[2].lower()
|
||||
base_fn = file.rpartition(os.sep)[2]
|
||||
base_fn = base_fn.rpartition('.')[0]
|
||||
added = self._find_library_book(
|
||||
{ 'title': metadata.title,
|
||||
{ 'title': base_fn if format == 'pdf' else metadata.title,
|
||||
'author': metadata.author[0],
|
||||
'uuid': metadata.uuid,
|
||||
'format': file.rpartition('.')[2].lower()})
|
||||
'format': format})
|
||||
return added
|
||||
|
||||
def _add_new_copy(self, fpath, metadata):
|
||||
@ -1820,7 +1841,6 @@ class ITUNES(DriverBase):
|
||||
except:
|
||||
if DEBUG:
|
||||
self.log.error(" error generating thumb for '%s', caching empty marker" % book.Name)
|
||||
self._dump_hex(data[:32])
|
||||
thumb_data = None
|
||||
# Cache the empty cover
|
||||
zfw.writestr(thumb_path,'None')
|
||||
|
@ -9,6 +9,7 @@ from threading import Thread
|
||||
from calibre import prints
|
||||
from calibre.utils.config import OptionParser
|
||||
from calibre.utils.logging import default_log
|
||||
from calibre.utils.titlecase import titlecase
|
||||
from calibre.customize import Plugin
|
||||
from calibre.ebooks.metadata.covers import check_for_cover
|
||||
|
||||
@ -384,6 +385,16 @@ def search(title=None, author=None, publisher=None, isbn=None, isbndb_key=None,
|
||||
if r.pubdate is None:
|
||||
r.pubdate = pubdate
|
||||
|
||||
def fix_case(x):
|
||||
if x and x.isupper():
|
||||
x = titlecase(x)
|
||||
return x
|
||||
|
||||
for r in results:
|
||||
r.title = fix_case(r.title)
|
||||
if r.authors:
|
||||
r.authors = list(map(fix_case, r.authors))
|
||||
|
||||
return results, [(x.name, x.exception, x.tb) for x in fetchers]
|
||||
|
||||
def get_social_metadata(mi, verbose=0):
|
||||
|
@ -296,6 +296,17 @@ class AddAction(object): # {{{
|
||||
self.library_view.model().db.import_book(MetaInformation(None), [])
|
||||
self.library_view.model().books_added(num)
|
||||
|
||||
def add_isbns(self, isbns):
|
||||
from calibre.ebooks.metadata import MetaInformation
|
||||
ids = set([])
|
||||
for x in isbns:
|
||||
mi = MetaInformation(None)
|
||||
mi.isbn = x
|
||||
ids.add(self.library_view.model().db.import_book(mi, []))
|
||||
self.library_view.model().books_added(len(isbns))
|
||||
self.do_download_metadata(ids)
|
||||
|
||||
|
||||
def files_dropped(self, paths):
|
||||
to_device = self.stack.currentIndex() != 0
|
||||
self._add_books(paths, to_device)
|
||||
@ -342,6 +353,12 @@ class AddAction(object): # {{{
|
||||
def add_filesystem_book(self, paths, allow_device=True):
|
||||
self._add_filesystem_book(paths, allow_device=allow_device)
|
||||
|
||||
def add_from_isbn(self, *args):
|
||||
from calibre.gui2.dialogs.add_from_isbn import AddFromISBN
|
||||
d = AddFromISBN(self)
|
||||
if d.exec_() == d.Accepted:
|
||||
self.add_isbns(d.isbns)
|
||||
|
||||
def add_books(self, *args):
|
||||
'''
|
||||
Add books from the local filesystem to either the library or the device.
|
||||
@ -625,6 +642,13 @@ class EditMetadataAction(object): # {{{
|
||||
return
|
||||
db = self.library_view.model().db
|
||||
ids = [db.id(row.row()) for row in rows]
|
||||
self.do_download_metadata(ids, covers=covers,
|
||||
set_metadata=set_metadata,
|
||||
set_social_metadata=set_social_metadata)
|
||||
|
||||
def do_download_metadata(self, ids, covers=True, set_metadata=True,
|
||||
set_social_metadata=None):
|
||||
db = self.library_view.model().db
|
||||
if set_social_metadata is None:
|
||||
get_social_metadata = config['get_social_metadata']
|
||||
else:
|
||||
@ -931,7 +955,7 @@ class SaveToDiskAction(object): # {{{
|
||||
lpath = self.library_view.model().db.library_path.replace('/', os.sep)
|
||||
if dpath.startswith(lpath):
|
||||
return error_dialog(self, _('Not allowed'),
|
||||
_('You are tying to save files into the calibre '
|
||||
_('You are trying to save files into the calibre '
|
||||
'library. This can cause corruption of your '
|
||||
'library. Save to disk is meant to export '
|
||||
'files from your calibre library elsewhere.'), show=True)
|
@ -63,7 +63,7 @@
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="opt_force_max_line_length">
|
||||
<property name="text">
|
||||
<string>Force maximum line lenght</string>
|
||||
<string>Force maximum line length</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
40
src/calibre/gui2/dialogs/add_from_isbn.py
Normal file
40
src/calibre/gui2/dialogs/add_from_isbn.py
Normal file
@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
from PyQt4.Qt import QDialog, QApplication
|
||||
|
||||
from calibre.gui2.dialogs.add_from_isbn_ui import Ui_Dialog
|
||||
from calibre.ebooks.metadata import check_isbn
|
||||
|
||||
class AddFromISBN(QDialog, Ui_Dialog):
|
||||
|
||||
def __init__(self, parent=None):
|
||||
QDialog.__init__(self, parent)
|
||||
self.setupUi(self)
|
||||
|
||||
self.isbns = []
|
||||
self.paste_button.clicked.connect(self.paste)
|
||||
|
||||
def paste(self, *args):
|
||||
app = QApplication.instance()
|
||||
c = app.clipboard()
|
||||
txt = unicode(c.text()).strip()
|
||||
if txt:
|
||||
old = unicode(self.isbn_box.toPlainText()).strip()
|
||||
new = old + '\n' + txt
|
||||
self.isbn_box.setPlainText(new)
|
||||
|
||||
def accept(self, *args):
|
||||
for line in unicode(self.isbn_box.toPlainText()).strip().splitlines():
|
||||
if line:
|
||||
isbn = check_isbn(line)
|
||||
if isbn is not None:
|
||||
isbn = isbn.upper()
|
||||
if isbn not in self.isbns:
|
||||
self.isbns.append(isbn)
|
||||
QDialog.accept(self, *args)
|
||||
|
90
src/calibre/gui2/dialogs/add_from_isbn.ui
Normal file
90
src/calibre/gui2/dialogs/add_from_isbn.ui
Normal file
@ -0,0 +1,90 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Dialog</class>
|
||||
<widget class="QDialog" name="Dialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>678</width>
|
||||
<height>430</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Add books by ISBN</string>
|
||||
</property>
|
||||
<property name="windowIcon">
|
||||
<iconset resource="../../../../resources/images.qrc">
|
||||
<normaloff>:/images/add_book.svg</normaloff>:/images/add_book.svg</iconset>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QPlainTextEdit" name="isbn_box"/>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string><p>Enter a list of ISBNs in the box to the left, one per line. calibre will automatically create entries for books based on the ISBN and download metadata and covers for them.<p>Any invalid ISBNs in the list will be ignored.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QPushButton" name="paste_button">
|
||||
<property name="text">
|
||||
<string>&Paste from clipboard</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="../../../../resources/images.qrc"/>
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>Dialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>Dialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
@ -18,7 +18,7 @@ from calibre.gui2 import error_dialog, config, gprefs, \
|
||||
open_url, open_local_file, \
|
||||
ALL_COLUMNS, NONE, info_dialog, choose_files, \
|
||||
warning_dialog, ResizableDialog, question_dialog
|
||||
from calibre.utils.config import prefs
|
||||
from calibre.utils.config import prefs, read_raw_tweaks, write_tweaks
|
||||
from calibre.ebooks import BOOK_EXTENSIONS
|
||||
from calibre.ebooks.oeb.iterator import is_supported
|
||||
from calibre.library.server import server_config
|
||||
@ -514,8 +514,18 @@ class ConfigDialog(ResizableDialog, Ui_Dialog):
|
||||
self.opt_toolbar_text.setCurrentIndex(idx)
|
||||
self.reset_confirmation_button.clicked.connect(self.reset_confirmation)
|
||||
|
||||
deft, curt = read_raw_tweaks()
|
||||
self.current_tweaks.setPlainText(curt)
|
||||
self.default_tweaks.setPlainText(deft)
|
||||
self.restore_tweaks_to_default_button.clicked.connect(self.restore_tweaks_to_default)
|
||||
|
||||
self.category_view.setCurrentIndex(self.category_view.model().index_for_name(initial_category))
|
||||
|
||||
def restore_tweaks_to_default(self, *args):
|
||||
deft, curt = read_raw_tweaks()
|
||||
self.current_tweaks.setPlainText(deft)
|
||||
|
||||
|
||||
def reset_confirmation(self):
|
||||
from calibre.gui2 import dynamic
|
||||
for key in dynamic.keys():
|
||||
@ -687,6 +697,22 @@ class ConfigDialog(ResizableDialog, Ui_Dialog):
|
||||
self.input_order.insertItem(idx-1, self.input_order.takeItem(idx))
|
||||
self.input_order.setCurrentRow(idx-1)
|
||||
|
||||
def set_tweaks(self):
|
||||
raw = unicode(self.current_tweaks.toPlainText())
|
||||
raw = re.sub(r'(?m)^#.*fileencoding.*', '# ', raw)
|
||||
try:
|
||||
exec raw
|
||||
except:
|
||||
import traceback
|
||||
error_dialog(self, _('Invalid tweaks'),
|
||||
_('The tweaks you entered are invalid, try resetting the'
|
||||
' tweaks to default and changing them one by one until'
|
||||
' you find the invalid setting.'),
|
||||
det_msg=traceback.format_exc(), show=True)
|
||||
return False
|
||||
write_tweaks(raw)
|
||||
return True
|
||||
|
||||
def down_input(self):
|
||||
idx = self.input_order.currentRow()
|
||||
if idx < self.input_order.count()-1:
|
||||
@ -852,6 +878,8 @@ class ConfigDialog(ResizableDialog, Ui_Dialog):
|
||||
return
|
||||
if not self.add_save.save_settings():
|
||||
return
|
||||
if not self.set_tweaks():
|
||||
return
|
||||
wl = self.opt_worker_limit.value()
|
||||
if wl%2 != 0:
|
||||
wl += 1
|
||||
|
@ -719,61 +719,192 @@
|
||||
<widget class="QWidget" name="page_2">
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>&Maximum number of waiting worker processes (needs restart):</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>opt_worker_limit</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QSpinBox" name="opt_worker_limit">
|
||||
<property name="minimum">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>10000</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>2</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<widget class="QPushButton" name="compact_button">
|
||||
<property name="text">
|
||||
<string>&Check database integrity</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0" colspan="2">
|
||||
<widget class="QPushButton" name="button_osx_symlinks">
|
||||
<property name="text">
|
||||
<string>&Install command line tools</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="2">
|
||||
<widget class="QPushButton" name="button_open_config_dir">
|
||||
<property name="text">
|
||||
<string>Open calibre &configuration directory</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="opt_enforce_cpu_limit">
|
||||
<property name="text">
|
||||
<string>Limit the max. simultaneous jobs to the available CPU &cores</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QPushButton" name="device_detection_button">
|
||||
<property name="text">
|
||||
<string>Debug &device detection</string>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="tab">
|
||||
<attribute name="title">
|
||||
<string>&Miscellaneous</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_9">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>&Maximum number of waiting worker processes (needs restart):</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>opt_worker_limit</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QSpinBox" name="opt_worker_limit">
|
||||
<property name="minimum">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>10000</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>2</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="opt_enforce_cpu_limit">
|
||||
<property name="text">
|
||||
<string>Limit the max. simultaneous jobs to the available CPU &cores</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<spacer name="verticalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>79</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<widget class="QPushButton" name="device_detection_button">
|
||||
<property name="text">
|
||||
<string>Debug &device detection</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<spacer name="verticalSpacer_6">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>80</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="5" column="0" colspan="2">
|
||||
<widget class="QPushButton" name="compact_button">
|
||||
<property name="text">
|
||||
<string>&Check database integrity</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<spacer name="verticalSpacer_7">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>79</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="7" column="0" colspan="2">
|
||||
<widget class="QPushButton" name="button_open_config_dir">
|
||||
<property name="text">
|
||||
<string>Open calibre &configuration directory</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<spacer name="verticalSpacer_8">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>80</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="9" column="0" colspan="2">
|
||||
<widget class="QPushButton" name="button_osx_symlinks">
|
||||
<property name="text">
|
||||
<string>&Install command line tools</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="0">
|
||||
<spacer name="verticalSpacer_9">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>79</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_2">
|
||||
<attribute name="title">
|
||||
<string>&Tweaks</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_18">
|
||||
<property name="text">
|
||||
<string>Values for the tweaks are shown below. Edit them to change the behavior of calibre</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_6">
|
||||
<property name="title">
|
||||
<string>All available tweaks</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_11">
|
||||
<item row="0" column="0">
|
||||
<widget class="QPlainTextEdit" name="default_tweaks">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_7">
|
||||
<property name="title">
|
||||
<string>&Current tweaks</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_10">
|
||||
<item row="0" column="0">
|
||||
<widget class="QPlainTextEdit" name="current_tweaks"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="restore_tweaks_to_default_button">
|
||||
<property name="text">
|
||||
<string>&Restore to defaults</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
@ -6,7 +6,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>730</width>
|
||||
<width>768</width>
|
||||
<height>342</height>
|
||||
</rect>
|
||||
</property>
|
||||
@ -51,6 +51,9 @@
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="recalc_author_sort">
|
||||
<property name="toolTip">
|
||||
<string>Reset all the author sort values to a value automatically generated from the author. Exactly how this value is automatically generated can be controlled via Preferences->Advanced->Tweaks</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Recalculate all author sort values</string>
|
||||
</property>
|
||||
|
@ -538,8 +538,10 @@ class MainWindowMixin(object):
|
||||
self.add_menu.addAction(_('Add books from directories, including '
|
||||
'sub directories (Multiple books per directory, assumes every '
|
||||
'ebook file is a different book)'), self.add_recursive_multiple)
|
||||
self.add_menu.addSeparator()
|
||||
self.add_menu.addAction(_('Add Empty book. (Book entry with no '
|
||||
'formats)'), self.add_empty)
|
||||
self.add_menu.addAction(_('Add from ISBN'), self.add_from_isbn)
|
||||
self.action_add.setMenu(self.add_menu)
|
||||
self.action_add.triggered.connect(self.add_books)
|
||||
self.action_del.triggered.connect(self.delete_books)
|
||||
|
@ -146,7 +146,8 @@ class SearchBox2(QComboBox):
|
||||
self._in_a_search = False
|
||||
if event.key() in (Qt.Key_Return, Qt.Key_Enter):
|
||||
self.do_search()
|
||||
self.timer.start(1500)
|
||||
if self.as_you_type:
|
||||
self.timer.start(1500)
|
||||
|
||||
def mouse_released(self, event):
|
||||
self.normalize_state()
|
||||
|
@ -675,9 +675,7 @@ class Wizard(QWizard):
|
||||
self.connect(self.library_page, SIGNAL('retranslate()'),
|
||||
self.retranslate)
|
||||
self.finish_page = FinishPage()
|
||||
bt = unicode(self.buttonText(self.FinishButton)).replace('&', '')
|
||||
t = unicode(self.finish_page.finish_text.text())
|
||||
self.finish_page.finish_text.setText(t%bt)
|
||||
self.set_finish_text()
|
||||
self.kindle_page = KindlePage()
|
||||
self.stanza_page = StanzaPage()
|
||||
self.word_player_page = WordPlayerPage()
|
||||
@ -702,6 +700,7 @@ class Wizard(QWizard):
|
||||
for pid in self.pageIds():
|
||||
page = self.page(pid)
|
||||
page.retranslateUi(page)
|
||||
self.set_finish_text()
|
||||
|
||||
def accept(self):
|
||||
pages = map(self.page, self.visitedPages())
|
||||
@ -715,6 +714,13 @@ class Wizard(QWizard):
|
||||
def completed(self, newloc):
|
||||
return QWizard.accept(self)
|
||||
|
||||
def set_finish_text(self, *args):
|
||||
bt = unicode(self.buttonText(self.FinishButton)).replace('&', '')
|
||||
t = unicode(self.finish_page.finish_text.text())
|
||||
if '%s' in t:
|
||||
self.finish_page.finish_text.setText(t%bt)
|
||||
|
||||
|
||||
def wizard(parent=None):
|
||||
w = Wizard(parent)
|
||||
return w
|
||||
|
@ -15,6 +15,7 @@ from calibre import prepare_string_for_xml
|
||||
# Hackish - ignoring sentences ending or beginning in numbers to avoid
|
||||
# confusion with decimal points.
|
||||
lost_cr_pat = re.compile('([a-z])([\.\?!])([A-Z])')
|
||||
lost_cr_exception_pat = re.compile(r'(Ph\.D)|(D\.Phil)|((Dr|Mr|Mrs|Ms)\.[A-Z])')
|
||||
|
||||
def comments_to_html(comments):
|
||||
'''
|
||||
@ -51,6 +52,8 @@ def comments_to_html(comments):
|
||||
return '\n'.join(parts)
|
||||
|
||||
# Explode lost CRs to \n\n
|
||||
comments = lost_cr_exception_pat.sub(lambda m: m.group().replace('.',
|
||||
'.\r'), comments)
|
||||
for lost_cr in lost_cr_pat.finditer(comments):
|
||||
comments = comments.replace(lost_cr.group(),
|
||||
'%s%s\n\n%s' % (lost_cr.group(1),
|
||||
|
@ -1673,6 +1673,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
||||
self.data.refresh_ids(self, [id]) # Needed to update format list and size
|
||||
if notify:
|
||||
self.notify('add', [id])
|
||||
return id
|
||||
|
||||
def get_top_level_move_items(self):
|
||||
items = set(os.listdir(self.library_path))
|
||||
|
@ -12,7 +12,7 @@ from ctypes import Structure as _Structure, c_char_p, c_uint, c_void_p, POINTER,
|
||||
from tempfile import NamedTemporaryFile
|
||||
from StringIO import StringIO
|
||||
|
||||
from calibre import iswindows, load_library, CurrentDir, prints
|
||||
from calibre import iswindows, load_library, CurrentDir
|
||||
from calibre.ptempfile import TemporaryDirectory
|
||||
|
||||
_librar_name = 'libunrar'
|
||||
|
@ -30,8 +30,8 @@ Environment variables
|
||||
Tweaks
|
||||
------------
|
||||
|
||||
Tweaks are small changes that you can specify to control various aspects of |app|'s behavior. You specify them by editing the 2tweaks.py file in the config directory.
|
||||
The default tweaks.py file is reproduced below
|
||||
Tweaks are small changes that you can specify to control various aspects of |app|'s behavior. You can change them by going to Preferences->Advanced->Tweaks.
|
||||
The default values for the tweaks are reproduced below
|
||||
|
||||
.. literalinclude:: ../../../resources/default_tweaks.py
|
||||
|
||||
|
@ -277,7 +277,8 @@ In |app|, you would instead use tags to mark genre and read status and then just
|
||||
|
||||
Why doesn't |app| have a column for foo?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|app| is designed to have columns for the most frequently and widely used fields. If it does not have a coulmn for your favorite field, you can always add a tag to the book for that piece of information. |app| also supports a general purpose "comments" fields for longer items.
|
||||
|app| is designed to have columns for the most frequently and widely used fields. In addition, you can add any columns you like. Columns can be added via Preferences->Interface.
|
||||
Watch the tutorial `UI Power tips <http://calibre-ebook.com/demo#tutorials>`_ to learn how to create your own columns.
|
||||
|
||||
How do I move my |app| library from one computer to another?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -247,6 +247,7 @@ You can search for the absence or presence of a field using the special "true" a
|
||||
cover:false will give you all books without a cover
|
||||
series:true will give you all books that belong to a series
|
||||
comments:false will give you all books with an empty comment
|
||||
format:false will give you all books with no actual files (empty records)
|
||||
|
||||
Yes/no custom columns are searchable. Searching for ``false``, ``empty``, or ``blank`` will find all books
|
||||
with undefined values in the column. Searching for ``true`` will find all books that do not have undefined
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -703,16 +703,21 @@ if prefs['installation_uuid'] is None:
|
||||
prefs['installation_uuid'] = str(uuid.uuid4())
|
||||
|
||||
# Read tweaks
|
||||
def read_tweaks():
|
||||
def read_raw_tweaks():
|
||||
make_config_dir()
|
||||
default_tweaks = P('default_tweaks.py', data=True)
|
||||
tweaks_file = os.path.join(config_dir, 'tweaks.py')
|
||||
if not os.path.exists(tweaks_file):
|
||||
with open(tweaks_file, 'wb') as f:
|
||||
f.write(default_tweaks)
|
||||
with open(tweaks_file, 'rb') as f:
|
||||
return default_tweaks, f.read()
|
||||
|
||||
def read_tweaks():
|
||||
default_tweaks, tweaks = read_raw_tweaks()
|
||||
l, g = {}, {}
|
||||
try:
|
||||
exec open(tweaks_file, 'rb') in g, l
|
||||
exec tweaks in g, l
|
||||
except:
|
||||
print 'Failed to load custom tweaks file'
|
||||
traceback.print_exc()
|
||||
@ -721,6 +726,13 @@ def read_tweaks():
|
||||
dl.update(l)
|
||||
return dl
|
||||
|
||||
def write_tweaks(raw):
|
||||
make_config_dir()
|
||||
tweaks_file = os.path.join(config_dir, 'tweaks.py')
|
||||
with open(tweaks_file, 'wb') as f:
|
||||
f.write(raw)
|
||||
|
||||
|
||||
tweaks = read_tweaks()
|
||||
|
||||
def migrate():
|
||||
|
@ -194,7 +194,7 @@ class Image(_magick.Image): # {{{
|
||||
|
||||
# }}}
|
||||
|
||||
def create_canvas(width, height, bgcolor):
|
||||
def create_canvas(width, height, bgcolor='white'):
|
||||
canvas = Image()
|
||||
canvas.create_canvas(int(width), int(height), str(bgcolor))
|
||||
return canvas
|
||||
|
Loading…
x
Reference in New Issue
Block a user