mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
0.8.5 update
This commit is contained in:
commit
60c0f68578
@ -19,6 +19,67 @@
|
||||
# new recipes:
|
||||
# - title:
|
||||
|
||||
- version: 0.8.5
|
||||
date: 2011-06-10
|
||||
|
||||
new features:
|
||||
- title: "A new 'portable' calibre build, useful if you like to carry around calibre and its library on a USB key"
|
||||
type: major
|
||||
description: "For details, see: http://calibre-ebook.com/download_portable"
|
||||
|
||||
- title: "E-book viewer: Remember the last used font size multiplier."
|
||||
tickets: [774343]
|
||||
|
||||
- title: "Preliminary support for the Kobo Touch. Drivers for the ZTE v9 tablet, Samsung S2, Notion Ink Adam and PocketBook 360+"
|
||||
|
||||
- title: "When downloading metadata merge rather than replace tags"
|
||||
|
||||
- title: "Edit metadata dialog: When pasting in an ISBN, if not valid ISBN if present on the clipboard popup a box for the user to enter the ISBN"
|
||||
|
||||
- title: "Windows build: Add code to load .pyd python extensions from a zip file. This allows many more files in the calibre installation to be zipped up, speeding up the installer."
|
||||
- title: "Add an action to remove all formats from the selected books to the remove books button"
|
||||
|
||||
|
||||
bug fixes:
|
||||
- title: "Various minor bug fixes to the column coloring code"
|
||||
|
||||
- title: "Fix the not() template function"
|
||||
|
||||
- title: "Nook Color/TSR: When sending books to the storage card place them in the My Files/Books subdirectory. Also do not upload cover thumbnails as users report that the NC/TSR don't use them."
|
||||
tickets: [792842]
|
||||
|
||||
- title: "Get Books: Update plugins for Amazon and B&N stores to handle website changes. Enable some stores by default on first run. Add Zixo store"
|
||||
tickets: [792762]
|
||||
|
||||
- title: "Comic Input: Replace the # character in filenames as it can cause problem with conversion/vieweing."
|
||||
tickets: [792723]
|
||||
|
||||
- title: "When writing files to zipfile, reset timestamp if it doesn't fit in 1980's vintage storage structures"
|
||||
|
||||
- title: "Amazon metadata plugin: Fix parsing of published date from amazon.de when it has februar in it"
|
||||
|
||||
improved recipes:
|
||||
- Ambito
|
||||
- GoComics
|
||||
- Le Monde Diplomatique
|
||||
- Max Planck
|
||||
- express.de
|
||||
|
||||
new recipes:
|
||||
- title: Ambito Financiero
|
||||
author: Darko Miletic
|
||||
|
||||
- title: Stiin Tas Technica
|
||||
author: Silviu Cotoara
|
||||
|
||||
- title: "Metro News NL"
|
||||
author: DrMerry
|
||||
|
||||
- title: "Brigitte.de, Polizeipresse DE and Heise Online"
|
||||
author: schuster
|
||||
|
||||
|
||||
|
||||
- version: 0.8.4
|
||||
date: 2011-06-03
|
||||
|
||||
|
@ -1,7 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008-2009, Darko Miletic <darko.miletic at gmail.com>'
|
||||
__copyright__ = '2008-2011, Darko Miletic <darko.miletic at gmail.com>'
|
||||
'''
|
||||
ambito.com
|
||||
'''
|
||||
@ -11,51 +9,56 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
class Ambito(BasicNewsRecipe):
|
||||
title = 'Ambito.com'
|
||||
__author__ = 'Darko Miletic'
|
||||
description = 'Informacion Libre las 24 horas'
|
||||
publisher = 'Ambito.com'
|
||||
category = 'news, politics, Argentina'
|
||||
description = 'Ambito.com con noticias del Diario Ambito Financiero de Buenos Aires'
|
||||
publisher = 'Editorial Nefir S.A.'
|
||||
category = 'news, politics, economy, finances, Argentina'
|
||||
oldest_article = 2
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets = True
|
||||
encoding = 'iso-8859-1'
|
||||
cover_url = 'http://www.ambito.com/img/logo_.jpg'
|
||||
remove_javascript = True
|
||||
encoding = 'cp1252'
|
||||
masthead_url = 'http://www.ambito.com/img/logo_.jpg'
|
||||
use_embedded_content = False
|
||||
language = 'es_AR'
|
||||
publication_type = 'newsportal'
|
||||
extra_css = """
|
||||
body{font-family: "Trebuchet MS",Verdana,sans-serif}
|
||||
.volanta{font-size: small}
|
||||
.t2_portada{font-size: xx-large; font-family: Georgia,serif; color: #026698}
|
||||
"""
|
||||
|
||||
html2lrf_options = [
|
||||
'--comment', description
|
||||
, '--category', category
|
||||
, '--publisher', publisher
|
||||
]
|
||||
|
||||
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"'
|
||||
conversion_options = {
|
||||
'comment' : description
|
||||
, 'tags' : category
|
||||
, 'publisher' : publisher
|
||||
, 'language' : language
|
||||
}
|
||||
|
||||
keep_only_tags = [dict(name='div', attrs={'align':'justify'})]
|
||||
|
||||
remove_tags = [dict(name=['object','link'])]
|
||||
remove_tags = [dict(name=['object','link','embed','iframe','meta','link','table','img'])]
|
||||
remove_attributes = ['align']
|
||||
|
||||
feeds = [
|
||||
(u'Principales Noticias', u'http://www.ambito.com/rss/noticiasp.asp' )
|
||||
,(u'Economia' , u'http://www.ambito.com/rss/noticias.asp?S=Econom%EDa' )
|
||||
,(u'Politica' , u'http://www.ambito.com/rss/noticias.asp?S=Pol%EDtica' )
|
||||
,(u'Informacion General' , u'http://www.ambito.com/rss/noticias.asp?S=Informaci%F3n%20General')
|
||||
,(u'Agro' , u'http://www.ambito.com/rss/noticias.asp?S=Agro' )
|
||||
,(u'Campo' , u'http://www.ambito.com/rss/noticias.asp?S=Agro' )
|
||||
,(u'Internacionales' , u'http://www.ambito.com/rss/noticias.asp?S=Internacionales' )
|
||||
,(u'Deportes' , u'http://www.ambito.com/rss/noticias.asp?S=Deportes' )
|
||||
,(u'Espectaculos' , u'http://www.ambito.com/rss/noticias.asp?S=Espect%E1culos' )
|
||||
,(u'Tecnologia' , u'http://www.ambito.com/rss/noticias.asp?S=Tecnologia' )
|
||||
,(u'Salud' , u'http://www.ambito.com/rss/noticias.asp?S=Salud' )
|
||||
,(u'Tecnologia' , u'http://www.ambito.com/rss/noticias.asp?S=Tecnolog%EDa' )
|
||||
,(u'Ambito Nacional' , u'http://www.ambito.com/rss/noticias.asp?S=Ambito%20Nacional' )
|
||||
]
|
||||
|
||||
def print_version(self, url):
|
||||
return url.replace('http://www.ambito.com/noticia.asp?','http://www.ambito.com/noticias/imprimir.asp?')
|
||||
return url.replace('/noticia.asp?','/noticias/imprimir.asp?')
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
mtag = '<meta http-equiv="Content-Language" content="es-AR"/>'
|
||||
soup.head.insert(0,mtag)
|
||||
for item in soup.findAll(style=True):
|
||||
del item['style']
|
||||
for item in soup.findAll('a'):
|
||||
str = item.string
|
||||
if str is None:
|
||||
str = self.tag_to_string(item)
|
||||
item.replaceWith(str)
|
||||
return soup
|
||||
|
||||
language = 'es_AR'
|
||||
|
87
recipes/ambito_financiero.recipe
Normal file
87
recipes/ambito_financiero.recipe
Normal file
@ -0,0 +1,87 @@
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2011, Darko Miletic <darko.miletic at gmail.com>'
|
||||
'''
|
||||
ambito.com/diario
|
||||
'''
|
||||
|
||||
import time
|
||||
from calibre import strftime
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class Ambito_Financiero(BasicNewsRecipe):
|
||||
title = 'Ambito Financiero'
|
||||
__author__ = 'Darko Miletic'
|
||||
description = 'Informacion Libre las 24 horas'
|
||||
publisher = 'Editorial Nefir S.A.'
|
||||
category = 'news, politics, economy, Argentina'
|
||||
no_stylesheets = True
|
||||
encoding = 'cp1252'
|
||||
masthead_url = 'http://www.ambito.com/diario/img/logo_af.gif'
|
||||
publication_type = 'newspaper'
|
||||
needs_subscription = 'optional'
|
||||
use_embedded_content = False
|
||||
language = 'es_AR'
|
||||
PREFIX = 'http://www.ambito.com'
|
||||
INDEX = PREFIX + '/diario/index.asp'
|
||||
LOGIN = PREFIX + '/diario/login/entrada.asp'
|
||||
extra_css = """
|
||||
body{font-family: "Trebuchet MS",Verdana,sans-serif}
|
||||
.volanta{font-size: small}
|
||||
.t2_portada{font-size: xx-large; font-family: Georgia,serif; color: #026698}
|
||||
"""
|
||||
|
||||
conversion_options = {
|
||||
'comment' : description
|
||||
, 'tags' : category
|
||||
, 'publisher' : publisher
|
||||
, 'language' : language
|
||||
}
|
||||
|
||||
keep_only_tags = [dict(name='div', attrs={'align':'justify'})]
|
||||
remove_tags = [dict(name=['object','link','embed','iframe','meta','link','table','img'])]
|
||||
remove_attributes = ['align']
|
||||
|
||||
def get_browser(self):
|
||||
br = BasicNewsRecipe.get_browser()
|
||||
br.open(self.INDEX)
|
||||
if self.username is not None and self.password is not None:
|
||||
br.open(self.LOGIN)
|
||||
br.select_form(name='frmlogin')
|
||||
br['USER_NAME'] = self.username
|
||||
br['USER_PASS'] = self.password
|
||||
br.submit()
|
||||
return br
|
||||
|
||||
def print_version(self, url):
|
||||
return url.replace('/diario/noticia.asp?','/noticias/imprimir.asp?')
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
for item in soup.findAll(style=True):
|
||||
del item['style']
|
||||
for item in soup.findAll('a'):
|
||||
str = item.string
|
||||
if str is None:
|
||||
str = self.tag_to_string(item)
|
||||
item.replaceWith(str)
|
||||
return soup
|
||||
|
||||
def parse_index(self):
|
||||
soup = self.index_to_soup(self.INDEX)
|
||||
cover_item = soup.find('img',attrs={'class':'fotodespliegue'})
|
||||
if cover_item:
|
||||
self.cover_url = self.PREFIX + cover_item['src']
|
||||
articles = []
|
||||
checker = []
|
||||
for feed_link in soup.findAll('a', attrs={'class':['t0_portada','t2_portada','bajada']}):
|
||||
url = self.PREFIX + feed_link['href']
|
||||
title = self.tag_to_string(feed_link)
|
||||
date = strftime("%a, %d %b %Y %H:%M:%S +0000",time.gmtime())
|
||||
if url not in checker:
|
||||
checker.append(url)
|
||||
articles.append({
|
||||
'title' :title
|
||||
,'date' :date
|
||||
,'url' :url
|
||||
,'description':u''
|
||||
})
|
||||
return [(self.title, articles)]
|
BIN
recipes/icons/ambito_financiero.png
Normal file
BIN
recipes/icons/ambito_financiero.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 508 B |
BIN
recipes/icons/observatorul_cultural.png
Normal file
BIN
recipes/icons/observatorul_cultural.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
BIN
recipes/icons/stiintasitehnica.png
Normal file
BIN
recipes/icons/stiintasitehnica.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 703 B |
56
recipes/stiintasitehnica.recipe
Normal file
56
recipes/stiintasitehnica.recipe
Normal file
@ -0,0 +1,56 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#!/usr/bin/env python
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = u'2011, Silviu Cotoar\u0103'
|
||||
'''
|
||||
stiintasitehnica.com
|
||||
'''
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class Stiintasitehnica(BasicNewsRecipe):
|
||||
title = u'\u0218tiin\u021b\u0103 \u015fi Tehnic\u0103'
|
||||
__author__ = u'Silviu Cotoar\u0103'
|
||||
description = u'\u0218tiin\u021b\u0103 \u015fi Tehnic\u0103'
|
||||
publisher = u'\u0218tiin\u021b\u0103 \u015fi Tehnic\u0103'
|
||||
oldest_article = 50
|
||||
language = 'ro'
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
category = u'Ziare,Reviste,Stiinta,Tehnica'
|
||||
encoding = 'utf-8'
|
||||
cover_url = 'http://www.stiintasitehnica.com/images/logo.jpg'
|
||||
|
||||
conversion_options = {
|
||||
'comments' : description
|
||||
,'tags' : category
|
||||
,'language' : language
|
||||
,'publisher' : publisher
|
||||
}
|
||||
|
||||
keep_only_tags = [
|
||||
dict(name='div', attrs={'id':'mainColumn2'})
|
||||
]
|
||||
|
||||
remove_tags = [
|
||||
dict(name='span', attrs={'class':['redEar']})
|
||||
, dict(name='table', attrs={'class':['connect_widget_interactive_area']})
|
||||
, dict(name='div', attrs={'class':['panel-overlay']})
|
||||
, dict(name='div', attrs={'id':['pointer']})
|
||||
, dict(name='img', attrs={'class':['nav-next', 'nav-prev']})
|
||||
, dict(name='table', attrs={'class':['connect_widget_interactive_area']})
|
||||
, dict(name='hr', attrs={'class':['dotted']})
|
||||
]
|
||||
|
||||
remove_tags_after = [
|
||||
dict(name='hr', attrs={'class':['dotted']})
|
||||
]
|
||||
|
||||
feeds = [
|
||||
(u'Feeds', u'http://www.stiintasitehnica.com/rss/stiri.xml')
|
||||
]
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
return self.adeify_images(soup)
|
@ -4,7 +4,7 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
__appname__ = u'calibre'
|
||||
numeric_version = (0, 8, 4)
|
||||
numeric_version = (0, 8, 5)
|
||||
__version__ = u'.'.join(map(unicode, numeric_version))
|
||||
__author__ = u"Kovid Goyal <kovid@kovidgoyal.net>"
|
||||
|
||||
|
@ -355,11 +355,17 @@ def remove_plugin(plugin_or_name):
|
||||
name = getattr(plugin_or_name, 'name', plugin_or_name)
|
||||
plugins = config['plugins']
|
||||
removed = False
|
||||
if name in plugins.keys():
|
||||
if name in plugins:
|
||||
removed = True
|
||||
zfp = plugins[name]
|
||||
if os.path.exists(zfp):
|
||||
os.remove(zfp)
|
||||
try:
|
||||
zfp = os.path.join(plugin_dir, name+'.zip')
|
||||
if os.path.exists(zfp):
|
||||
os.remove(zfp)
|
||||
zfp = plugins[name]
|
||||
if os.path.exists(zfp):
|
||||
os.remove(zfp)
|
||||
except:
|
||||
pass
|
||||
plugins.pop(name)
|
||||
config['plugins'] = plugins
|
||||
initialize_plugins()
|
||||
@ -495,8 +501,15 @@ def initialize_plugins():
|
||||
builtin_names]
|
||||
for p in conflicts:
|
||||
remove_plugin(p)
|
||||
for zfp in list(config['plugins'].itervalues()) + builtin_plugins:
|
||||
external_plugins = config['plugins']
|
||||
for zfp in list(external_plugins) + builtin_plugins:
|
||||
try:
|
||||
if not isinstance(zfp, type):
|
||||
# We have a plugin name
|
||||
pname = zfp
|
||||
zfp = os.path.join(plugin_dir, zfp+'.zip')
|
||||
if not os.path.exists(zfp):
|
||||
zfp = external_plugins[pname]
|
||||
try:
|
||||
plugin = load_plugin(zfp) if not isinstance(zfp, type) else zfp
|
||||
except PluginNotFound:
|
||||
|
@ -21,7 +21,7 @@ class KOBO(USBMS):
|
||||
name = 'Kobo Reader Device Interface'
|
||||
gui_name = 'Kobo Reader'
|
||||
description = _('Communicate with the Kobo Reader')
|
||||
author = 'Timothy Legge and Kovid Goyal'
|
||||
author = 'Timothy Legge'
|
||||
version = (1, 0, 9)
|
||||
|
||||
dbversion = 0
|
||||
@ -37,8 +37,8 @@ class KOBO(USBMS):
|
||||
CAN_SET_METADATA = ['collections']
|
||||
|
||||
VENDOR_ID = [0x2237]
|
||||
PRODUCT_ID = [0x4161]
|
||||
BCD = [0x0110, 0x0323]
|
||||
PRODUCT_ID = [0x4161, 0x4163]
|
||||
BCD = [0x0110, 0x0323, 0x0326]
|
||||
|
||||
VENDOR_NAME = ['KOBO_INC', 'KOBO']
|
||||
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = ['.KOBOEREADER', 'EREADER']
|
||||
|
@ -77,7 +77,6 @@ class NOOK(USBMS):
|
||||
with open('%s.jpg' % os.path.join(path, filename), 'wb') as coverfile:
|
||||
coverfile.write(coverdata)
|
||||
|
||||
|
||||
def sanitize_path_components(self, components):
|
||||
return [x.replace('#', '_') for x in components]
|
||||
|
||||
@ -110,6 +109,11 @@ class NOOK_COLOR(NOOK):
|
||||
def upload_cover(self, path, filename, metadata, filepath):
|
||||
pass
|
||||
|
||||
def get_carda_ebook_dir(self, for_upload=False):
|
||||
if for_upload:
|
||||
return 'My Files/Books'
|
||||
return ''
|
||||
|
||||
class NOOK_TSR(NOOK):
|
||||
gui_name = _('Nook Simple')
|
||||
description = _('Communicate with the Nook TSR eBook reader.')
|
||||
@ -117,9 +121,15 @@ class NOOK_TSR(NOOK):
|
||||
PRODUCT_ID = [0x003]
|
||||
BCD = [0x216]
|
||||
|
||||
EBOOK_DIR_MAIN = EBOOK_DIR_CARD_A = 'My Files/Books'
|
||||
EBOOK_DIR_MAIN = 'My Files/Books'
|
||||
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = 'EBOOK_DISK'
|
||||
|
||||
def upload_cover(self, path, filename, metadata, filepath):
|
||||
pass
|
||||
|
||||
def get_carda_ebook_dir(self, for_upload=False):
|
||||
if for_upload:
|
||||
return 'My Files/Books'
|
||||
return ''
|
||||
|
||||
|
||||
|
@ -837,6 +837,9 @@ class Device(DeviceConfig, DevicePlugin):
|
||||
def get_main_ebook_dir(self, for_upload=False):
|
||||
return self.EBOOK_DIR_MAIN
|
||||
|
||||
def get_carda_ebook_dir(self, for_upload=False):
|
||||
return self.EBOOK_DIR_CARD_A
|
||||
|
||||
def _sanity_check(self, on_card, files):
|
||||
if on_card == 'carda' and not self._card_a_prefix:
|
||||
raise ValueError(_('The reader has no storage card in this slot.'))
|
||||
@ -847,7 +850,7 @@ class Device(DeviceConfig, DevicePlugin):
|
||||
|
||||
if on_card == 'carda':
|
||||
path = os.path.join(self._card_a_prefix,
|
||||
*(self.EBOOK_DIR_CARD_A.split('/')))
|
||||
*(self.get_carda_ebook_dir(for_upload=True).split('/')))
|
||||
elif on_card == 'cardb':
|
||||
path = os.path.join(self._card_b_prefix,
|
||||
*(self.EBOOK_DIR_CARD_B.split('/')))
|
||||
|
@ -132,7 +132,7 @@ class USBMS(CLI, Device):
|
||||
self._card_b_prefix if oncard == 'cardb' \
|
||||
else self._main_prefix
|
||||
|
||||
ebook_dirs = self.EBOOK_DIR_CARD_A if oncard == 'carda' else \
|
||||
ebook_dirs = self.get_carda_ebook_dir() if oncard == 'carda' else \
|
||||
self.EBOOK_DIR_CARD_B if oncard == 'cardb' else \
|
||||
self.get_main_ebook_dir()
|
||||
|
||||
|
@ -439,7 +439,8 @@ class EditMetadataAction(InterfaceAction):
|
||||
view.reset()
|
||||
|
||||
# Apply bulk metadata changes {{{
|
||||
def apply_metadata_changes(self, id_map, title=None, msg='', callback=None):
|
||||
def apply_metadata_changes(self, id_map, title=None, msg='', callback=None,
|
||||
merge_tags=True):
|
||||
'''
|
||||
Apply the metadata changes in id_map to the database synchronously
|
||||
id_map must be a mapping of ids to Metadata objects. Set any fields you
|
||||
@ -466,9 +467,9 @@ class EditMetadataAction(InterfaceAction):
|
||||
cancelable=False)
|
||||
self.apply_pd.setModal(True)
|
||||
self.apply_pd.show()
|
||||
self._am_merge_tags = True
|
||||
self.do_one_apply()
|
||||
|
||||
|
||||
def do_one_apply(self):
|
||||
if self.apply_current_idx >= len(self.apply_id_map):
|
||||
return self.finalize_apply()
|
||||
@ -484,6 +485,12 @@ class EditMetadataAction(InterfaceAction):
|
||||
mi.identifiers = idents
|
||||
if mi.is_null('series'):
|
||||
mi.series_index = None
|
||||
if self._am_merge_tags:
|
||||
old_tags = db.tags(i, index_is_id=True)
|
||||
if old_tags:
|
||||
tags = [x.strip() for x in old_tags.split(',')] + (
|
||||
mi.tags if mi.tags else [])
|
||||
mi.tags = list(set(tags))
|
||||
db.set_metadata(i, mi, commit=False, set_title=set_title,
|
||||
set_authors=set_authors, notify=False)
|
||||
self.applied_ids.append(i)
|
||||
|
@ -315,7 +315,7 @@ class MetadataSingleDialogBase(ResizableDialog):
|
||||
show=True)
|
||||
return
|
||||
|
||||
def update_from_mi(self, mi, update_sorts=True):
|
||||
def update_from_mi(self, mi, update_sorts=True, merge_tags=True):
|
||||
if not mi.is_null('title'):
|
||||
self.title.current_val = mi.title
|
||||
if update_sorts:
|
||||
@ -334,7 +334,11 @@ class MetadataSingleDialogBase(ResizableDialog):
|
||||
if not mi.is_null('publisher'):
|
||||
self.publisher.current_val = mi.publisher
|
||||
if not mi.is_null('tags'):
|
||||
self.tags.current_val = mi.tags
|
||||
old_tags = self.tags.current_val
|
||||
tags = mi.tags if mi.tags else []
|
||||
if old_tags and merge_tags:
|
||||
tags += old_tags
|
||||
self.tags.current_val = tags
|
||||
if not mi.is_null('identifiers'):
|
||||
current = self.identifiers.current_val
|
||||
current.update(mi.identifiers)
|
||||
|
@ -8,10 +8,11 @@ import os, math, re, glob, sys
|
||||
from base64 import b64encode
|
||||
from functools import partial
|
||||
|
||||
from PyQt4.Qt import QSize, QSizePolicy, QUrl, SIGNAL, Qt, QTimer, \
|
||||
QPainter, QPalette, QBrush, QFontDatabase, QDialog, \
|
||||
QColor, QPoint, QImage, QRegion, QVariant, QIcon, \
|
||||
QFont, pyqtSignature, QAction, QByteArray, QMenu
|
||||
from PyQt4.Qt import (QSize, QSizePolicy, QUrl, SIGNAL, Qt, QTimer,
|
||||
QPainter, QPalette, QBrush, QFontDatabase, QDialog,
|
||||
QColor, QPoint, QImage, QRegion, QVariant, QIcon,
|
||||
QFont, pyqtSignature, QAction, QByteArray, QMenu,
|
||||
pyqtSignal)
|
||||
from PyQt4.QtWebKit import QWebPage, QWebView, QWebSettings
|
||||
|
||||
from calibre.utils.config import Config, StringConfig
|
||||
@ -496,6 +497,7 @@ class EntityDeclarationProcessor(object): # {{{
|
||||
|
||||
class DocumentView(QWebView): # {{{
|
||||
|
||||
magnification_changed = pyqtSignal(object)
|
||||
DISABLED_BRUSH = QBrush(Qt.lightGray, Qt.Dense5Pattern)
|
||||
|
||||
def __init__(self, *args):
|
||||
@ -908,15 +910,22 @@ class DocumentView(QWebView): # {{{
|
||||
if notify and self.manager is not None and self.document.ypos != old_pos:
|
||||
self.manager.scrolled(self.scroll_fraction)
|
||||
|
||||
@dynamic_property
|
||||
def multiplier(self):
|
||||
return self.document.mainFrame().textSizeMultiplier()
|
||||
def fget(self):
|
||||
return self.document.mainFrame().textSizeMultiplier()
|
||||
def fset(self, val):
|
||||
self.document.mainFrame().setTextSizeMultiplier(val)
|
||||
self.magnification_changed.emit(val)
|
||||
return property(fget=fget, fset=fset)
|
||||
|
||||
def magnify_fonts(self):
|
||||
self.document.mainFrame().setTextSizeMultiplier(self.multiplier()+0.2)
|
||||
self.multiplier += 0.2
|
||||
return self.document.scroll_fraction
|
||||
|
||||
def shrink_fonts(self):
|
||||
self.document.mainFrame().setTextSizeMultiplier(max(self.multiplier()-0.2, 0))
|
||||
if self.multiplier >= 0.2:
|
||||
self.multiplier -= 0.2
|
||||
return self.document.scroll_fraction
|
||||
|
||||
def changeEvent(self, event):
|
||||
|
@ -175,6 +175,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
||||
def __init__(self, pathtoebook=None, debug_javascript=False):
|
||||
MainWindow.__init__(self, None)
|
||||
self.setupUi(self)
|
||||
self.view.magnification_changed.connect(self.magnification_changed)
|
||||
self.show_toc_on_open = False
|
||||
self.current_book_has_toc = False
|
||||
self.base_window_title = unicode(self.windowTitle())
|
||||
@ -345,6 +346,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
||||
if self.toc.isVisible():
|
||||
vprefs.set('viewer_splitter_state',
|
||||
bytearray(self.splitter.saveState()))
|
||||
vprefs['multiplier'] = self.view.multiplier
|
||||
|
||||
def restore_state(self):
|
||||
state = vprefs.get('viewer_toolbar_state', None)
|
||||
@ -354,6 +356,9 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
||||
self.restoreState(state, self.STATE_VERSION)
|
||||
except:
|
||||
pass
|
||||
mult = vprefs.get('multiplier', None)
|
||||
if mult:
|
||||
self.view.multiplier = mult
|
||||
|
||||
|
||||
def lookup(self, word):
|
||||
@ -476,16 +481,22 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
||||
|
||||
def font_size_larger(self, checked):
|
||||
frac = self.view.magnify_fonts()
|
||||
self.action_font_size_larger.setEnabled(self.view.multiplier() < 3)
|
||||
self.action_font_size_smaller.setEnabled(self.view.multiplier() > 0.2)
|
||||
self.action_font_size_larger.setEnabled(self.view.multiplier < 3)
|
||||
self.action_font_size_smaller.setEnabled(self.view.multiplier > 0.2)
|
||||
self.set_page_number(frac)
|
||||
|
||||
def font_size_smaller(self, checked):
|
||||
frac = self.view.shrink_fonts()
|
||||
self.action_font_size_larger.setEnabled(self.view.multiplier() < 3)
|
||||
self.action_font_size_smaller.setEnabled(self.view.multiplier() > 0.2)
|
||||
self.action_font_size_larger.setEnabled(self.view.multiplier < 3)
|
||||
self.action_font_size_smaller.setEnabled(self.view.multiplier > 0.2)
|
||||
self.set_page_number(frac)
|
||||
|
||||
def magnification_changed(self, val):
|
||||
tt = _('Make font size %s\nCurrent magnification: %.1f')
|
||||
self.action_font_size_larger.setToolTip(
|
||||
tt %(_('larger'), val))
|
||||
self.action_font_size_smaller.setToolTip(
|
||||
tt %(_('smaller'), val))
|
||||
|
||||
def find(self, text, repeat=False, backwards=False):
|
||||
if not text:
|
||||
|
@ -319,7 +319,7 @@ but it requires that the Kindle be rebooted *every time* it is disconnected from
|
||||
changes to the collections to be recognized. As such, it is unlikely that
|
||||
any |app| developers will ever feel motivated enough to support it. There is however, a |app| plugin
|
||||
that allows you to create collections on your Kindle from the |app| metadata. It is available
|
||||
`here <http://www.mobileread.com/forums/showthread.php?t=118635>`_.
|
||||
`from here <http://www.mobileread.com/forums/showthread.php?t=118635>`_.
|
||||
|
||||
Library Management
|
||||
------------------
|
||||
@ -570,7 +570,7 @@ For many reasons:
|
||||
* *There is no need to update every week*. If you are happy with how |app| works turn off the update notification and be on your merry way. Check back to see if you want to update once a year or so.
|
||||
* Pre downloading the updates for all users in the background would mean require about 80TB of bandwidth *every week*. That costs thousands of dollars a month. And |app| is currently growing at 300,000 new users every month.
|
||||
* If I implement a dialog that downloads the update and launches it, instead of going to the website as it does now, that would save the most ardent |app| updater, *at most five clicks a week*. There are far higher priority things to do in |app| development.
|
||||
* If you really, really hate downloading |app| every week but still want to be upto the latest, I encourage you to run from source, which makes updating trivial. Instructions are :ref:`here <develop>`.
|
||||
* If you really, really hate downloading |app| every week but still want to be up to the latest, I encourage you to run from source, which makes updating trivial. Instructions are :ref:`available here <develop>`.
|
||||
|
||||
How is |app| licensed?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -99,12 +99,12 @@ def test():
|
||||
test_lxml()
|
||||
test_fontconfig()
|
||||
test_sqlite()
|
||||
if iswindows:
|
||||
test_winutil()
|
||||
test_win32()
|
||||
test_qt()
|
||||
test_imaging()
|
||||
test_unrar()
|
||||
if iswindows:
|
||||
test_win32()
|
||||
test_winutil()
|
||||
|
||||
if __name__ == '__main__':
|
||||
test()
|
||||
|
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
17024
src/calibre/translations/az.po
Normal file
17024
src/calibre/translations/az.po
Normal file
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
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
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
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
@ -202,6 +202,10 @@ def main(args=sys.argv):
|
||||
|
||||
|
||||
if len(args) > 1:
|
||||
if len(args) < 4:
|
||||
print ('You must specify the from address, to address and body text'
|
||||
' on the command line')
|
||||
return 1
|
||||
msg = compose_mail(args[1], args[2], args[3], subject=opts.subject,
|
||||
attachment=opts.attachment)
|
||||
from_, to = args[1:3]
|
||||
|
Loading…
x
Reference in New Issue
Block a user