mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Pull from custcol trunk
This commit is contained in:
commit
64965e4a49
@ -4,6 +4,87 @@
|
||||
# for important features/bug fixes.
|
||||
# Also, each release can have new and improved recipes.
|
||||
|
||||
- version: 0.6.53
|
||||
date: 2010-05-15
|
||||
|
||||
new features:
|
||||
- title: "Clean up GUI initialization and add support for restoring corrupted databases automatically"
|
||||
|
||||
- title: "Make proxy detection more robust on windows and OS X. calibre now queries OS X Network Settigns if no environment variables are set. Also handle proxies with a trailing slash correctly"
|
||||
|
||||
- title: "Add EPUB advanced formatting demo to User Manual"
|
||||
|
||||
- title: "Support for the Booq Avant, Azbooka and the Samsung GT-I5700"
|
||||
|
||||
- title: "Backwards search in the E-book viewer"
|
||||
|
||||
- title: "calibredb: Add ability to create empty books in the database."
|
||||
tickets: [5504]
|
||||
|
||||
- title: "Conversion pipeline: Support for the :first-letter pseudo selector"
|
||||
|
||||
- title: "Interpret a Keyboard interrupt (Ctrl+C) as a request to quit the main GUI"
|
||||
|
||||
- title: "CBC Input: Handle comics.txt encoded in UTF-16 with a BOM"
|
||||
|
||||
bug fixes:
|
||||
- title: "HTML Input: Fix silly bug in case sensitivity detection"
|
||||
|
||||
- title: "Kobo driver: Show all sideloaded content on the device."
|
||||
tickets: [5492]
|
||||
|
||||
- title: "EPUB metadata: Fix bug with relative apths in encryption detection when reading cover"
|
||||
tickets: [5471]
|
||||
|
||||
- title: "E-book viewer: Fix next page scrolling when current document is just a little more than a screenfull. Also use a more robust method to insert blank space at the end of the document when the last screenfull is partially empty."
|
||||
|
||||
- title: "EPUB metadata: Allow deletion of series/tags/isbn from EPUB files when Saving to Disk"
|
||||
tickets: [5518]
|
||||
|
||||
- title: "Fix regression that caused temporary blank line at the bottom of the books list when adding duplicates"
|
||||
tickets: [5500]
|
||||
|
||||
- title: "Add icon for RTF"
|
||||
tickets: [5503]
|
||||
|
||||
- title: "Amazon metadata: If ISBN is not found, don't report an error message."
|
||||
tickets: [5501]
|
||||
|
||||
- title: "EPUB Input: Fix typo that caused incorrect processing of EPUB files with more than one identifier element and encrypted fonts"
|
||||
|
||||
- title: "Fix bug that caused send to device to send multiple copies to the device if you had previously used Prefrences"
|
||||
|
||||
- title: "Linux prs 500 udev rule: Use SUBSYSTEMS instead of the deprecated BUS"
|
||||
|
||||
- title: "PML2PMLZ plugin: Actually compress the PML file stored in the PMLZ archive"
|
||||
tickets: [5511]
|
||||
|
||||
- title: "SONY drivers: Fix regression that broke collection ordering by series when sending to device. And fix another rare error condition."
|
||||
tickets: [5487]
|
||||
|
||||
- title: "CHM Input: Regression that broke CHM conversion on OS X."
|
||||
tickets: [5483]
|
||||
|
||||
- title: "Fix PDB created in Dropbook not convertable by Calibre"
|
||||
tickets: [5441]
|
||||
|
||||
|
||||
new recipes:
|
||||
- title: APCOM, Leggo (it), Ansa and Punto Informatico
|
||||
author: Gabriele Marini
|
||||
|
||||
- title: Scinexx.de
|
||||
author: JSuer
|
||||
|
||||
- title: Various Russian news sources
|
||||
author: Darko Miletic
|
||||
|
||||
improved recipes:
|
||||
- Christian Science Monitor
|
||||
- The Nation
|
||||
- Physics World
|
||||
- Discover Magazine
|
||||
|
||||
- version: 0.6.52
|
||||
date: 2010-05-07
|
||||
|
||||
|
72
resources/recipes/Ansa.recipe
Normal file
72
resources/recipes/Ansa.recipe
Normal file
@ -0,0 +1,72 @@
|
||||
#!/usr/bin/env python
|
||||
__license__ = 'GPL v3'
|
||||
__author__ = 'Gabriele Marini, based on Darko Miletic'
|
||||
__copyright__ = '2010, Darko Miletic <darko.miletic at gmail.com>'
|
||||
description = 'Italian daily newspaper - 01-05-2010'
|
||||
'''
|
||||
http://www.ansa.it/
|
||||
'''
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class Ansa(BasicNewsRecipe):
|
||||
__author__ = 'Gabriele Marini'
|
||||
description = 'Italian News Agency'
|
||||
|
||||
cover_url = 'http://www.ansa.it/web/images/logo_ansa_interna.gif'
|
||||
title = u'Ansa'
|
||||
publisher = 'Ansa'
|
||||
category = 'News, politics, culture, economy, general interest'
|
||||
|
||||
language = 'it'
|
||||
timefmt = '[%a, %d %b, %Y]'
|
||||
|
||||
oldest_article = 1
|
||||
max_articles_per_feed = 10
|
||||
use_embedded_content = False
|
||||
recursion = 10
|
||||
|
||||
remove_javascript = True
|
||||
no_stylesheets = True
|
||||
conversion_options = {'linearize_tables':True}
|
||||
remove_attributes = ['colspan']
|
||||
|
||||
keep_only_tags = [dict(name='div', attrs={'class':['path','header-content','corpo']}),
|
||||
]
|
||||
|
||||
|
||||
remove_tags = [
|
||||
dict(name='div', attrs={'class':'tools-bar'}),
|
||||
dict(name='div', attrs={'id':['rssdiv','blocco']})
|
||||
]
|
||||
|
||||
|
||||
feeds = [
|
||||
(u'HomePage', u'http://www.ansa.it/web/ansait_web_rss_homepage.xml'),
|
||||
(u'Top New', u'http://www.ansa.it/web/notizie/rubriche/topnews/topnews_rss.xml'),
|
||||
(u'Cronaca', u'http://www.ansa.it/web/notizie/rubriche/cronaca/cronaca_rss.xml'),
|
||||
(u'Mondo', u'http://www.ansa.it/web/notizie/rubriche/mondo/mondo_rss.xml'),
|
||||
(u'Economia', u'http://www.ansa.it/web/notizie/rubriche/economia/economia_rss.xml'),
|
||||
(u'Politica', u'http://www.ansa.it/web/notizie/rubriche/politica/politica_rss.xml'),
|
||||
(u'Scienze', u'http://www.ansa.it/web/notizie/rubriche/scienza/scienza_rss.xml'),
|
||||
(u'Cinema', u'http://www.ansa.it/web/notizie/rubriche/cinema/cinema_rss.xml'),
|
||||
(u'Tecnologia e Internet', u'http://www.ansa.it/web/notizie/rubriche/tecnologia/tecnologia_rss.xml'),
|
||||
(u'Spettacolo', u'http://www.ansa.it/web/notizie/rubriche/spettacolo/spettacolo_rss.xml'),
|
||||
(u'Cultura e Tendenze', u'http://www.ansa.it/web/notizie/rubriche/cultura/cultura_rss.xml'),
|
||||
(u'Sport', u'http://www.ansa.it/web/notizie/rubriche/altrisport/altrisport_rss.xml'),
|
||||
(u'Calcio', u'http://www.ansa.it/web/notizie/rubriche/calcio/calcio_rss.xml'),
|
||||
(u'Lazio', u'http://www.ansa.it/web/notizie/regioni/lazio/lazio_rss.xml'),
|
||||
(u'Lombardia', u'http://www.ansa.it/web/notizie/regioni/lombardia/lombardia.shtml'),
|
||||
(u'Veneto', u'http://www.ansa.it/web/notizie/regioni/veneto/veneto.shtml'),
|
||||
(u'Campanioa', u'http://www.ansa.it/web/notizie/regioni/campania/campania.shtml'),
|
||||
(u'Sicilia', u'http://www.ansa.it/web/notizie/regioni/sicilia/sicilia.shtml'),
|
||||
(u'Toscana', u'http://www.ansa.it/web/notizie/regioni/toscana/toscana.shtml'),
|
||||
(u'Trentino', u'http://www.ansa.it/web/notizie/regioni/trentino/trentino.shtml')
|
||||
]
|
||||
|
||||
extra_css = '''
|
||||
.path{font-style: italic; font-size: small}
|
||||
.header-content h1{font-weight: bold; font-size: xx-large}
|
||||
.header-content h2{font-weight: bold; font-size: x-large; font-syle: italic}
|
||||
.content-corpo{font-size: 14px;font-family: Times New Roman;}
|
||||
'''
|
48
resources/recipes/apcom.recipe
Normal file
48
resources/recipes/apcom.recipe
Normal file
@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env python
|
||||
__license__ = 'GPL v3'
|
||||
__author__ = 'Gabriele Marini, based on Darko Miletic'
|
||||
__copyright__ = '2009-2010, Darko Miletic <darko.miletic at gmail.com>'
|
||||
description = 'Italian daily newspaper - 14-05-2010'
|
||||
|
||||
'''
|
||||
http://www.apcom.NET/
|
||||
'''
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class Apcom(BasicNewsRecipe):
|
||||
__author__ = 'Marini Gabriele'
|
||||
description = 'Italian daily newspaper'
|
||||
|
||||
cover_url = 'http://www.apcom.net/img/logoAP.gif'
|
||||
title = u'Apcom'
|
||||
publisher = 'TM News S.p.A.'
|
||||
category = 'News, politics, culture, economy, general interest'
|
||||
|
||||
language = 'it'
|
||||
timefmt = '[%a, %d %b, %Y]'
|
||||
|
||||
oldest_article = 7
|
||||
max_articles_per_feed = 50
|
||||
use_embedded_content = False
|
||||
recursion = 100
|
||||
|
||||
no_stylesheets = True
|
||||
conversion_options = {'linearize_tables':True}
|
||||
remove_javascript = True
|
||||
|
||||
keep_only_tags = [
|
||||
dict(name='div', attrs={'id':'ag_center'})
|
||||
]
|
||||
|
||||
|
||||
|
||||
feeds = [
|
||||
(u'Globale', u'http://www.apcom.net/rss/globale.xml '),
|
||||
(u'Politica', u'http://www.apcom.net/rss/politica.xml'),
|
||||
(u'Cronaca', u'http://www.apcom.net/rss/cronaca.xml'),
|
||||
(u'Econimia', u'http://www.apcom.net/rss/economia.xml'),
|
||||
(u'Esteri', u'http://www.apcom.net/rss/esteri.xml'),
|
||||
(u'Cultura', u'http://www.apcom.net/rss/cultura.xml'),
|
||||
(u'Sport', u'http://www.apcom.net/rss/sport.xml')
|
||||
]
|
@ -37,6 +37,7 @@ class ChristianScienceMonitor(BasicNewsRecipe):
|
||||
|
||||
preprocess_regexps = [ (re.compile(i[0], re.IGNORECASE | re.DOTALL), i[1]) for i in
|
||||
[
|
||||
(r'<!--.*?-->', lambda match : ''),
|
||||
(r'<body.*?<div id="story"', lambda match : '<body><div id="story"'),
|
||||
(r'<div class="pubdate">.*?</div>', lambda m: ''),
|
||||
(r'Full HTML version of this story which may include photos, graphics, and related links.*</body>',
|
||||
@ -79,7 +80,10 @@ class ChristianScienceMonitor(BasicNewsRecipe):
|
||||
|
||||
remove_tags = [
|
||||
dict(name='div', attrs={'id':['story-tools','videoPlayer','storyRelatedBottom','enlarge-photo','photo-paginate']}),
|
||||
dict(name='div', attrs={'class':['storyToolbar cfx','podStoryRel','spacer3','divvy spacer7','comment','storyIncludeBottom']}),
|
||||
dict(name=['div','a'], attrs={'class':
|
||||
['storyToolbar cfx','podStoryRel','spacer3',
|
||||
'divvy spacer7','comment','storyIncludeBottom',
|
||||
'hide', 'podBrdr']}),
|
||||
dict(name='ul', attrs={'class':[ 'centerliststories']}) ,
|
||||
dict(name='form', attrs={'id':[ 'commentform']}) ,
|
||||
]
|
||||
|
49
resources/recipes/leggo_it.recipe
Normal file
49
resources/recipes/leggo_it.recipe
Normal file
@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env python
|
||||
__license__ = 'GPL v3'
|
||||
__author__ = 'Gabriele Marini, based on Darko Miletic'
|
||||
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>'
|
||||
description = 'Italian daily newspaper - v1.00 05-05-2010'
|
||||
|
||||
'''
|
||||
http://www.leggo.it
|
||||
'''
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class LeggoIT(BasicNewsRecipe):
|
||||
__author__ = 'Gabriele Marini'
|
||||
description = 'Italian Free daily newspaper'
|
||||
|
||||
cover_url = 'http://www.leggo.it/img/logo-leggo2.gif'
|
||||
title = u'Leggo.it'
|
||||
publisher = 'Ced Caltagirone Editore S.p.A.'
|
||||
category = 'News, politics, culture, economy, general interest'
|
||||
|
||||
language = 'it'
|
||||
timefmt = '[%a, %d %b, %Y]'
|
||||
|
||||
oldest_article = 5
|
||||
max_articles_per_feed = 100
|
||||
use_embedded_content = False
|
||||
recursion = 100
|
||||
|
||||
no_stylesheets = True
|
||||
remove_javascript = True
|
||||
conversion_options = {'linearize_tables':True}
|
||||
|
||||
keep_only_tags = [
|
||||
dict(name='h1',attrs={'class':'nero22'}),
|
||||
dict(name='div',attrs={'id':'testodim'})
|
||||
]
|
||||
feeds = [
|
||||
(u'Home Page', u'http://www.leggo.it/rss/home.xml'),
|
||||
(u'Italia', u'http://www.leggo.it/rss/italia.xml'),
|
||||
(u'Esteri', u'http://www.leggo.it/rss/esteri.xml'),
|
||||
(u'Economia', u'http://www.leggo.it/rss/economia.xml'),
|
||||
(u'Sport', u'http://www.leggo.it/rss/sport.xml'),
|
||||
(u'Gossip', u'http://www.leggo.it/rss/gossip.xml'),
|
||||
(u'Spettacoli', u'http://www.leggo.it/rss/spettacoli.xml'),
|
||||
(u'Salute', u'http://www.leggo.it/rss/salute.xml'),
|
||||
(u'Scienza', u'http://www.leggo.it/rss/scienza.xml')
|
||||
]
|
||||
|
@ -10,6 +10,7 @@ class PhysicsWorld(BasicNewsRecipe):
|
||||
oldest_article = 7
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets = True
|
||||
cover_url = 'http://images.iop.org/cws/icons/themes/phw/header-logo.png'
|
||||
use_embedded_content = False
|
||||
remove_javascript = True
|
||||
needs_subscription = True
|
||||
@ -27,7 +28,7 @@ class PhysicsWorld(BasicNewsRecipe):
|
||||
br = BasicNewsRecipe.get_browser(self)
|
||||
if self.username is not None and self.password is not None:
|
||||
br.open('http://physicsworld.com/cws/sign-in')
|
||||
br.select_form(nr=1)
|
||||
br.select_form(nr=2)
|
||||
br['username'] = self.username
|
||||
br['password'] = self.password
|
||||
br.submit()
|
||||
|
38
resources/recipes/punto_informatico.recipe
Normal file
38
resources/recipes/punto_informatico.recipe
Normal file
@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env python
|
||||
__license__ = 'GPL v3'
|
||||
__author__ = 'Gabriele Marini'
|
||||
__copyright__ = 'Gabriele Marini'
|
||||
__description__ = 'Punto Informatico'
|
||||
|
||||
'''
|
||||
http://www.punto-informatico.it/
|
||||
'''
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
|
||||
class PuntoInformatico(BasicNewsRecipe):
|
||||
__author__ = 'Gabriele Marini'
|
||||
description = 'Punto Informatico: Internet dal 1996'
|
||||
|
||||
cover_url = 'http://punto-informatico.it/images/logo_8bit.png'
|
||||
title = u'Punto Informatico '
|
||||
publisher = 'italiaNews High Tech'
|
||||
category = 'News, Information Tecnology'
|
||||
|
||||
language = 'it'
|
||||
timefmt = '[%a, %d %b, %Y]'
|
||||
|
||||
oldest_article = 15
|
||||
max_articles_per_feed = 50
|
||||
use_embedded_content = False
|
||||
|
||||
remove_javascript = True
|
||||
no_stylesheets = True
|
||||
keep_only_tags = [dict(name='div', attrs={'class':'box'})]
|
||||
remove_tags = [dict(name='div',attrs={'class':'boxadv'})]
|
||||
def get_article_url(self, article):
|
||||
return article.get('id', article.get('guid', None))
|
||||
|
||||
feeds = [(u'Punto Informatico',u'http://feeds.punto-informatico.it/c/32288/f/438866/index.rss')]
|
||||
|
@ -15,7 +15,7 @@ class al(BasicNewsRecipe):
|
||||
description = 'The Escapist Magazine'
|
||||
|
||||
cover_url = 'http://cdn.themis-media.com/themes/escapistmagazine/default/images/logo.png'
|
||||
title = u'the Escapist Magazine'
|
||||
title = u'The Escapist Magazine'
|
||||
publisher = 'Themis media'
|
||||
category = 'Video games news, lifestyle, gaming culture'
|
||||
|
||||
|
@ -1,37 +1,40 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Darko Miletic <darko.miletic at gmail.com>'
|
||||
__copyright__ = '2008 - 2010, Darko Miletic <darko.miletic at gmail.com>'
|
||||
'''
|
||||
thenation.com
|
||||
'''
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class Thenation(BasicNewsRecipe):
|
||||
title = u'The Nation'
|
||||
__author__ = u'Darko Miletic'
|
||||
description = u'Unconventional Wisdom Since 1865'
|
||||
title = 'The Nation'
|
||||
__author__ = 'Darko Miletic'
|
||||
description = 'Unconventional Wisdom Since 1865'
|
||||
publisher = 'The Nation'
|
||||
category = 'news, politics, USA'
|
||||
oldest_article = 120
|
||||
encoding = 'utf-8'
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets = True
|
||||
language = 'en'
|
||||
|
||||
use_embedded_content = False
|
||||
simultaneous_downloads = 1
|
||||
language = 'en'
|
||||
use_embedded_content = False
|
||||
delay = 1
|
||||
timefmt = ' [%A, %d %B, %Y]'
|
||||
masthead_url = 'http://www.thenation.com/sites/default/themes/thenation/images/logo-main.gif'
|
||||
exra_css = ' body{font-family: Arial,Helvetica,sans-serif;} .print-created{font-size: small;} .caption{display: block; font-size: x-small;} '
|
||||
|
||||
conversion_options = {
|
||||
'comment' : description
|
||||
, 'tags' : category
|
||||
, 'publisher' : publisher
|
||||
, 'language' : language
|
||||
}
|
||||
|
||||
keep_only_tags = [ dict(name='div', attrs={'class':'main'}) ]
|
||||
remove_tags = [
|
||||
dict(name='div', attrs={'class':'mod tools'})
|
||||
,dict(name='div', attrs={'class':'inset' })
|
||||
,dict(name='div', attrs={'class':'share' })
|
||||
,dict(name='ol' , attrs={'id' :'comments' })
|
||||
,dict(name='p' , attrs={'class':'info' })
|
||||
,dict(name='a' , attrs={'class':'comments' })
|
||||
,dict(name='ul' , attrs={'class':'important'})
|
||||
,dict(name='object')
|
||||
]
|
||||
keep_only_tags = [ dict(attrs={'class':['print-title','print-created','print-content','print-links']}) ]
|
||||
remove_tags = [dict(name='link')]
|
||||
|
||||
feeds = [(u"Top Stories", u'http://feedproxy.google.com/TheNationEdPicks')]
|
||||
feeds = [(u"Editor's Picks", u'http://www.thenation.com/rss/editors_picks')]
|
||||
|
||||
def print_version(self, url):
|
||||
return url.replace('.thenation.com/','.thenation.com/print/')
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
return self.adeify_images(soup)
|
||||
|
@ -236,6 +236,10 @@ class MetadataWriterPlugin(Plugin):
|
||||
|
||||
type = _('Metadata writer')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
Plugin.__init__(self, *args, **kwargs)
|
||||
self.apply_null = False
|
||||
|
||||
def set_metadata(self, stream, mi, type):
|
||||
'''
|
||||
Set metadata for the file represented by stream (a file like object
|
||||
|
@ -329,7 +329,7 @@ class EPUBMetadataWriter(MetadataWriterPlugin):
|
||||
|
||||
def set_metadata(self, stream, mi, type):
|
||||
from calibre.ebooks.metadata.epub import set_metadata
|
||||
set_metadata(stream, mi)
|
||||
set_metadata(stream, mi, apply_null=self.apply_null)
|
||||
|
||||
class LRFMetadataWriter(MetadataWriterPlugin):
|
||||
|
||||
|
@ -187,6 +187,18 @@ class QuickMetadata(object):
|
||||
|
||||
quick_metadata = QuickMetadata()
|
||||
|
||||
class ApplyNullMetadata(object):
|
||||
|
||||
def __init__(self):
|
||||
self.apply_null = False
|
||||
|
||||
def __enter__(self):
|
||||
self.apply_null = True
|
||||
|
||||
def __exit__(self, *args):
|
||||
self.apply_null = False
|
||||
|
||||
apply_null_metadata = ApplyNullMetadata()
|
||||
|
||||
def get_file_type_metadata(stream, ftype):
|
||||
mi = MetaInformation(None, None)
|
||||
@ -214,6 +226,7 @@ def set_file_type_metadata(stream, mi, ftype):
|
||||
if not is_disabled(plugin):
|
||||
with plugin:
|
||||
try:
|
||||
plugin.apply_null = apply_null_metadata.apply_null
|
||||
plugin.set_metadata(stream, mi, ftype.lower().strip())
|
||||
break
|
||||
except:
|
||||
|
@ -24,7 +24,7 @@ class N516(USBMS):
|
||||
|
||||
VENDOR_ID = [0x0525]
|
||||
PRODUCT_ID = [0xa4a5]
|
||||
BCD = [0x323, 0x326]
|
||||
BCD = [0x323, 0x326, 0x399]
|
||||
|
||||
VENDOR_NAME = 'INGENIC'
|
||||
WINDOWS_MAIN_MEM = '_FILE-STOR_GADGE'
|
||||
|
@ -66,5 +66,6 @@ class AVANT(USBMS):
|
||||
VENDOR_NAME = 'E-BOOK'
|
||||
WINDOWS_MAIN_MEM = 'READER'
|
||||
|
||||
EBOOK_DIR_MAIN = 'E-books'
|
||||
EBOOK_DIR_MAIN = ''
|
||||
SUPPORTS_SUB_DIRS = True
|
||||
|
||||
|
@ -32,9 +32,9 @@ class EPUBInput(InputFormatPlugin):
|
||||
key = None
|
||||
for item in opf.identifier_iter():
|
||||
scheme = None
|
||||
for key in item.attrib.keys():
|
||||
if key.endswith('scheme'):
|
||||
scheme = item.get(key)
|
||||
for xkey in item.attrib.keys():
|
||||
if xkey.endswith('scheme'):
|
||||
scheme = item.get(xkey)
|
||||
if (scheme and scheme.lower() == 'uuid') or \
|
||||
(item.text and item.text.startswith('urn:uuid:')):
|
||||
key = str(item.text).rpartition(':')[-1]
|
||||
|
@ -19,12 +19,18 @@ AWS_NS = 'http://webservices.amazon.com/AWSECommerceService/2005-10-05'
|
||||
def AWS(tag):
|
||||
return '{%s}%s'%(AWS_NS, tag)
|
||||
|
||||
def check_for_errors(root):
|
||||
class ISBNNotFound(ValueError):
|
||||
pass
|
||||
|
||||
def check_for_errors(root, isbn):
|
||||
err = root.find('.//'+AWS('Error'))
|
||||
if err is not None:
|
||||
text = etree.tostring(err, method='text', pretty_print=True,
|
||||
encoding=unicode)
|
||||
if 'AWS.InvalidParameterValue'+isbn in text:
|
||||
raise ISBNNotFound(isbn)
|
||||
raise Exception('Failed to get metadata with error: '\
|
||||
+ etree.tostring(err, method='text', pretty_print=True,
|
||||
encoding=unicode))
|
||||
+ text)
|
||||
|
||||
def get_social_metadata(title, authors, publisher, isbn):
|
||||
mi = MetaInformation(title, authors)
|
||||
@ -32,7 +38,10 @@ def get_social_metadata(title, authors, publisher, isbn):
|
||||
br = browser()
|
||||
response_xml = br.open('http://status.calibre-ebook.com/aws/metadata/'+isbn).read()
|
||||
root = etree.fromstring(response_xml)
|
||||
check_for_errors(root)
|
||||
try:
|
||||
check_for_errors(root, isbn)
|
||||
except ISBNNotFound:
|
||||
return mi
|
||||
mi.title = root.findtext('.//'+AWS('Title'))
|
||||
authors = [x.text for x in root.findall('.//'+AWS('Author'))]
|
||||
if authors:
|
||||
|
@ -182,13 +182,21 @@ def get_metadata(stream, extract_cover=True):
|
||||
def get_quick_metadata(stream):
|
||||
return get_metadata(stream, False)
|
||||
|
||||
def set_metadata(stream, mi):
|
||||
def set_metadata(stream, mi, apply_null=False):
|
||||
stream.seek(0)
|
||||
reader = OCFZipReader(stream, root=os.getcwdu())
|
||||
mi = MetaInformation(mi)
|
||||
for x in ('guide', 'toc', 'manifest', 'spine'):
|
||||
setattr(mi, x, None)
|
||||
reader.opf.smart_update(mi)
|
||||
if apply_null:
|
||||
if not getattr(mi, 'series', None):
|
||||
reader.opf.series = None
|
||||
if not getattr(mi, 'tags', []):
|
||||
reader.opf.tags = []
|
||||
if not getattr(mi, 'isbn', None):
|
||||
reader.opf.isbn = None
|
||||
|
||||
newopf = StringIO(reader.opf.render())
|
||||
safe_replace(stream, reader.container[OPF.MIMETYPE], newopf)
|
||||
|
||||
|
@ -404,6 +404,10 @@ class MetadataField(object):
|
||||
|
||||
def __set__(self, obj, val):
|
||||
elem = obj.get_metadata_element(self.name)
|
||||
if val is None:
|
||||
if elem is not None:
|
||||
elem.getparent().remove(elem)
|
||||
return
|
||||
if elem is None:
|
||||
elem = obj.create_metadata_element(self.name, is_dc=self.is_dc)
|
||||
obj.set_text(elem, unicode(val))
|
||||
@ -722,6 +726,11 @@ class OPF(object):
|
||||
|
||||
def fset(self, val):
|
||||
matches = self.isbn_path(self.metadata)
|
||||
if val is None:
|
||||
if matches:
|
||||
for x in matches:
|
||||
x.getparent().remove(x)
|
||||
return
|
||||
if not matches:
|
||||
attrib = {'{%s}scheme'%self.NAMESPACES['opf']: 'ISBN'}
|
||||
matches = [self.create_metadata_element('identifier',
|
||||
|
@ -235,6 +235,7 @@ def save_book(task, library_path, path, recs, notification=lambda x,y:x):
|
||||
from calibre.library.database2 import LibraryDatabase2
|
||||
db = LibraryDatabase2(library_path)
|
||||
from calibre.library.save_to_disk import config, save_to_disk
|
||||
from calibre.customize.ui import apply_null_metadata
|
||||
opts = config().parse()
|
||||
for name in recs:
|
||||
setattr(opts, name, recs[name])
|
||||
@ -244,5 +245,6 @@ def save_book(task, library_path, path, recs, notification=lambda x,y:x):
|
||||
notification((id, title, not failed, tb))
|
||||
return True
|
||||
|
||||
save_to_disk(db, task, path, opts, callback)
|
||||
with apply_null_metadata:
|
||||
save_to_disk(db, task, path, opts, callback)
|
||||
|
||||
|
@ -92,10 +92,10 @@ class EbookIterator(object):
|
||||
ext = re.sub(r'(x{0,1})htm(l{0,1})', 'html', ext)
|
||||
self.ebook_ext = ext
|
||||
|
||||
def search(self, text, index):
|
||||
def search(self, text, index, backwards=False):
|
||||
text = text.lower()
|
||||
for i, path in enumerate(self.spine):
|
||||
if i > index:
|
||||
if (backwards and i < index) or (not backwards and i > index):
|
||||
if text in open(path, 'rb').read().decode(path.encoding).lower():
|
||||
return i
|
||||
|
||||
|
@ -358,6 +358,7 @@ class FileIconProvider(QFileIconProvider):
|
||||
'tan' : 'zero',
|
||||
'epub' : 'epub',
|
||||
'fb2' : 'fb2',
|
||||
'rtf' : 'rtf',
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
|
@ -199,11 +199,11 @@ class DBAdder(Thread):
|
||||
self.add_formats(id, formats)
|
||||
else:
|
||||
id = self.db.create_book_entry(mi, cover=cover, add_duplicates=False)
|
||||
self.number_of_books_added += 1
|
||||
if id is None:
|
||||
self.duplicates.append((mi, cover, orig_formats))
|
||||
else:
|
||||
self.add_formats(id, formats)
|
||||
self.number_of_books_added += 1
|
||||
else:
|
||||
self.names.append(name)
|
||||
self.paths.append(formats[0])
|
||||
|
@ -141,6 +141,13 @@ class SearchBox2(QComboBox):
|
||||
if event.timerId() == self.timer:
|
||||
self.do_search()
|
||||
|
||||
@property
|
||||
def smart_text(self):
|
||||
text = unicode(self.currentText()).strip()
|
||||
if not text or text == self.help_text:
|
||||
return ''
|
||||
return text
|
||||
|
||||
def do_search(self):
|
||||
text = unicode(self.currentText()).strip()
|
||||
if not text or text == self.help_text:
|
||||
|
@ -396,6 +396,9 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
||||
self.action_sync.setShortcut(Qt.Key_D)
|
||||
self.action_sync.setEnabled(True)
|
||||
self.create_device_menu()
|
||||
self.connect(self.action_sync, SIGNAL('triggered(bool)'),
|
||||
self._sync_action_triggered)
|
||||
|
||||
self.action_edit.setMenu(md)
|
||||
self.action_save.setMenu(self.save_menu)
|
||||
|
||||
@ -675,14 +678,17 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
||||
def disconnect_from_folder(self):
|
||||
self.device_manager.disconnect_folder()
|
||||
|
||||
def _sync_action_triggered(self, *args):
|
||||
m = getattr(self, '_sync_menu', None)
|
||||
if m is not None:
|
||||
m.trigger_default()
|
||||
|
||||
def create_device_menu(self):
|
||||
self._sync_menu = DeviceMenu(self)
|
||||
self.action_sync.setMenu(self._sync_menu)
|
||||
self.connect(self._sync_menu,
|
||||
SIGNAL('sync(PyQt_PyObject, PyQt_PyObject, PyQt_PyObject)'),
|
||||
self.dispatch_sync_event)
|
||||
self.connect(self.action_sync, SIGNAL('triggered(bool)'),
|
||||
self._sync_menu.trigger_default)
|
||||
self._sync_menu.fetch_annotations.connect(self.fetch_annotations)
|
||||
self._sync_menu.connect_to_folder.connect(self.connect_to_folder)
|
||||
self._sync_menu.disconnect_from_folder.connect(self.disconnect_from_folder)
|
||||
|
@ -585,7 +585,9 @@ class DocumentView(QWebView):
|
||||
def fset(self, val): self.document.current_language = val
|
||||
return property(fget=fget, fset=fset)
|
||||
|
||||
def search(self, text):
|
||||
def search(self, text, backwards=False):
|
||||
if backwards:
|
||||
return self.findText(text, self.document.FindBackwards)
|
||||
return self.findText(text)
|
||||
|
||||
def path(self):
|
||||
|
@ -229,7 +229,11 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
||||
self.connect(self.action_previous_page, SIGNAL('triggered(bool)'),
|
||||
lambda x:self.view.previous_page())
|
||||
self.connect(self.action_find_next, SIGNAL('triggered(bool)'),
|
||||
lambda x:self.find(unicode(self.search.text()), True, repeat=True))
|
||||
lambda x:self.find(self.search.smart_text, True, repeat=True))
|
||||
self.connect(self.action_find_previous, SIGNAL('triggered(bool)'),
|
||||
lambda x:self.find(self.search.smart_text, True,
|
||||
repeat=True, backwards=True))
|
||||
|
||||
self.connect(self.action_full_screen, SIGNAL('triggered(bool)'),
|
||||
self.toggle_fullscreen)
|
||||
self.action_full_screen.setShortcuts([Qt.Key_F11, Qt.CTRL+Qt.SHIFT+Qt.Key_F])
|
||||
@ -420,13 +424,15 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
||||
self.set_bookmarks(self.iterator.bookmarks)
|
||||
|
||||
|
||||
def find(self, text, refinement, repeat=False):
|
||||
def find(self, text, refinement, repeat=False, backwards=False):
|
||||
if not text:
|
||||
self.view.search('')
|
||||
return self.search.search_done(False)
|
||||
if self.view.search(text):
|
||||
self.scrolled(self.view.scroll_fraction)
|
||||
return self.search.search_done(True)
|
||||
index = self.iterator.search(text, self.current_index)
|
||||
index = self.iterator.search(text, self.current_index,
|
||||
backwards=backwards)
|
||||
if index is None:
|
||||
if self.current_index > 0:
|
||||
index = self.iterator.search(text, 0)
|
||||
@ -444,10 +450,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
||||
self.scrolled(self.view.scroll_fraction)
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
if event.key() == Qt.Key_F3:
|
||||
text = unicode(self.search.text())
|
||||
self.find(text, True, repeat=True)
|
||||
elif event.key() == Qt.Key_Slash:
|
||||
if event.key() == Qt.Key_Slash:
|
||||
self.search.setFocus(Qt.OtherFocusReason)
|
||||
else:
|
||||
return MainWindow.keyPressEvent(self, event)
|
||||
|
@ -142,6 +142,7 @@
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<addaction name="action_find_next"/>
|
||||
<addaction name="action_find_previous"/>
|
||||
</widget>
|
||||
<action name="action_back">
|
||||
<property name="icon">
|
||||
@ -232,6 +233,12 @@
|
||||
<property name="text">
|
||||
<string>Find next</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Find next occurrence</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>F3</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_copy">
|
||||
<property name="icon">
|
||||
@ -287,6 +294,21 @@
|
||||
<string>Print</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_find_previous">
|
||||
<property name="icon">
|
||||
<iconset resource="../../../../resources/images.qrc">
|
||||
<normaloff>:/images/arrow-up.svg</normaloff>:/images/arrow-up.svg</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Find previous</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Find previous occurrence</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Shift+F3</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
|
@ -97,9 +97,18 @@ class Kobo(Device):
|
||||
manufacturer = 'Kobo'
|
||||
output_profile = 'kobo'
|
||||
output_format = 'EPUB'
|
||||
name = 'Kobo Reader'
|
||||
id = 'kobo'
|
||||
|
||||
class Booq(Device):
|
||||
name = 'Booq Reader'
|
||||
manufacturer = 'Booq'
|
||||
output_profile = 'prs505'
|
||||
output_format = 'EPUB'
|
||||
id = 'booq'
|
||||
|
||||
class Avant(Booq):
|
||||
name = 'Booq Avant'
|
||||
|
||||
class Sony300(Sony505):
|
||||
|
||||
name = 'SONY Reader Pocket Edition'
|
||||
|
@ -376,12 +376,35 @@ the directory related options below.
|
||||
help=_('Process directories recursively'))
|
||||
parser.add_option('-d', '--duplicates', action='store_true', default=False,
|
||||
help=_('Add books to database even if they already exist. Comparison is done based on book titles.'))
|
||||
parser.add_option('-e', '--empty', action='store_true', default=False,
|
||||
help=_('Add an empty book (a book with no formats)'))
|
||||
parser.add_option('-t', '--title', default=None,
|
||||
help=_('Set the title of the added empty book'))
|
||||
parser.add_option('-a', '--authors', default=None,
|
||||
help=_('Set the authors of the added empty book'))
|
||||
parser.add_option('-i', '--isbn', default=None,
|
||||
help=_('Set the ISBN of the added empty book'))
|
||||
|
||||
return parser
|
||||
|
||||
def do_add_empty(db, title, authors, isbn):
|
||||
from calibre.ebooks.metadata import MetaInformation, string_to_authors
|
||||
mi = MetaInformation(None)
|
||||
if title is not None:
|
||||
mi.title = title
|
||||
if authors:
|
||||
mi.authors = string_to_authors(authors)
|
||||
if isbn:
|
||||
mi.isbn = isbn
|
||||
db.import_book(mi, [])
|
||||
send_message()
|
||||
|
||||
def command_add(args, dbpath):
|
||||
parser = add_option_parser()
|
||||
opts, args = parser.parse_args(sys.argv[:1] + args)
|
||||
if opts.empty:
|
||||
do_add_empty(get_db(dbpath, opts), opts.title, opts.authors, opts.isbn)
|
||||
return 0
|
||||
if len(args) < 2:
|
||||
parser.print_help()
|
||||
print
|
||||
|
Loading…
x
Reference in New Issue
Block a user