mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-08 18:54:09 -04:00
Merge from trunk
This commit is contained in:
commit
2c1debbe79
@ -49,7 +49,11 @@ class Danas(BasicNewsRecipe):
|
|||||||
, 'language' : language
|
, 'language' : language
|
||||||
}
|
}
|
||||||
|
|
||||||
preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')]
|
preprocess_regexps = [
|
||||||
|
(re.compile(u'\u0110'), lambda match: u'\u00D0')
|
||||||
|
,(re.compile(u'\u201c'), lambda match: '"')
|
||||||
|
,(re.compile(u'\u201e'), lambda match: '"')
|
||||||
|
]
|
||||||
|
|
||||||
keep_only_tags = [dict(name='div', attrs={'id':'left'})]
|
keep_only_tags = [dict(name='div', attrs={'id':'left'})]
|
||||||
remove_tags = [
|
remove_tags = [
|
||||||
|
@ -21,8 +21,8 @@ class Novosti(BasicNewsRecipe):
|
|||||||
encoding = 'utf-8'
|
encoding = 'utf-8'
|
||||||
language = 'sr'
|
language = 'sr'
|
||||||
publication_type = 'newspaper'
|
publication_type = 'newspaper'
|
||||||
extra_css = """ @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)}
|
extra_css = """ @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)}
|
||||||
.article_description,body{font-family: Arial,Helvetica,sans1,sans-serif}
|
.article_description,body{font-family: Arial,Helvetica,sans1,sans-serif}
|
||||||
.author{font-size: small}
|
.author{font-size: small}
|
||||||
.articleLead{font-size: large; font-weight: bold}
|
.articleLead{font-size: large; font-weight: bold}
|
||||||
"""
|
"""
|
||||||
@ -47,6 +47,8 @@ class Novosti(BasicNewsRecipe):
|
|||||||
item.name='p'
|
item.name='p'
|
||||||
for item in soup.findAll('img'):
|
for item in soup.findAll('img'):
|
||||||
if not item.has_key('alt'):
|
if not item.has_key('alt'):
|
||||||
item['alt'] = 'image'
|
item['alt'] = 'image'
|
||||||
return soup
|
return soup
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
79
resources/recipes/superesportes.recipe
Normal file
79
resources/recipes/superesportes.recipe
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2010, Luciano Furtado <lrfurtado at yahoo.com.br>'
|
||||||
|
'''
|
||||||
|
www.superesportes.com.br
|
||||||
|
'''
|
||||||
|
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class SuperEsportesRecipe(BasicNewsRecipe):
|
||||||
|
|
||||||
|
title = u'www.superesportes.com.br'
|
||||||
|
description = u'Superesportes - Notícias do esporte no Brasil e no mundo'
|
||||||
|
__author__ = 'Luciano Furtado'
|
||||||
|
language = 'pt'
|
||||||
|
category = 'esportes, Brasil'
|
||||||
|
no_stylesheets = True
|
||||||
|
oldest_article = 7
|
||||||
|
|
||||||
|
use_embedded_content=0
|
||||||
|
max_articles_per_feed = 10
|
||||||
|
cover_url = 'http://imgs.mg.superesportes.com.br/superesportes_logo.png'
|
||||||
|
|
||||||
|
extra_css = 'div.info_noticias h1 { font-size: 100% }'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
remove_tags = [
|
||||||
|
dict(name='div',attrs={'class':'topo'}),
|
||||||
|
dict(name='div',attrs={'class':'rodape'}),
|
||||||
|
dict(name='div',attrs={'class':'navegacao'}),
|
||||||
|
dict(name='div',attrs={'class':'lateral2'}),
|
||||||
|
dict(name='div',attrs={'class':'leia_mais'}),
|
||||||
|
dict(name='div',attrs={'id':'comentar'}),
|
||||||
|
dict(name='div',attrs={'id':'vrumelc_noticia'}),
|
||||||
|
dict(name='div',attrs={'class':'compartilhe'}),
|
||||||
|
dict(name='div',attrs={'class':'linha_noticias'}),
|
||||||
|
dict(name='div',attrs={'class':'botoes_noticias'}),
|
||||||
|
dict(name='div',attrs={'class':'barra_time bg_time'}),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def parse_index(self):
|
||||||
|
feeds = []
|
||||||
|
sections = [
|
||||||
|
(u'Atletico', 'http://www.df.superesportes.com.br/futebol/atletico-mg/capa_atletico_mg/index.shtml'),
|
||||||
|
(u'Botafogo', 'http://www.df.superesportes.com.br/futebol/botafogo/capa_botafogo/index.shtml'),
|
||||||
|
(u'Corinthinas', 'http://www.df.superesportes.com.br/futebol/corinthians/capa_corinthians/index.shtml'),
|
||||||
|
(u'Cruzeiro', 'http://www.df.superesportes.com.br/futebol/cruzeiro/capa_cruzeiro/index.shtml'),
|
||||||
|
(u'Flamengo', 'http://www.df.superesportes.com.br/futebol/flamengo/capa_flamengo/index.shtml'),
|
||||||
|
(u'Fluminense', 'http://www.df.superesportes.com.br/futebol/fluminense/capa_fluminense/index.shtml'),
|
||||||
|
(u'Palmeiras', 'http://www.df.superesportes.com.br/futebol/palmeiras/capa_palmeiras/index.shtml'),
|
||||||
|
(u'Santos', 'http://www.df.superesportes.com.br/futebol/santos/capa_santos/index.shtml'),
|
||||||
|
(u'S√£o Paulo', 'http://www.df.superesportes.com.br/futebol/sao-paulo/capa_sao_paulo/index.shtml'),
|
||||||
|
(u'Vasco', 'http://www.df.superesportes.com.br/futebol/vasco/capa_vasco/index.shtml'),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
for section, url in sections:
|
||||||
|
current_articles = []
|
||||||
|
|
||||||
|
soup = self.index_to_soup(url)
|
||||||
|
latestNews = soup.find(name='ul',attrs={'class': 'lista_ultimas_noticias'})
|
||||||
|
|
||||||
|
for li_tag in latestNews.findAll(name='li'):
|
||||||
|
a_tag = li_tag.find('a', href= True)
|
||||||
|
if a_tag is None:
|
||||||
|
continue
|
||||||
|
title = self.tag_to_string(a_tag)
|
||||||
|
url = a_tag.get('href', False)
|
||||||
|
self.log("\n\nFound title: " + title + "\nUrl: " + url + "\nSection: " + section)
|
||||||
|
current_articles.append({'title': title, 'url': url, 'description': title, 'date':''})
|
||||||
|
|
||||||
|
if current_articles:
|
||||||
|
feeds.append((section, current_articles))
|
||||||
|
|
||||||
|
|
||||||
|
return feeds
|
||||||
|
|
@ -2,7 +2,7 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
__appname__ = 'calibre'
|
__appname__ = 'calibre'
|
||||||
__version__ = '0.7.900'
|
__version__ = '0.7.901'
|
||||||
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
@ -36,13 +36,17 @@ Run an embedded python interpreter.
|
|||||||
'plugin code.')
|
'plugin code.')
|
||||||
parser.add_option('--reinitialize-db', default=None,
|
parser.add_option('--reinitialize-db', default=None,
|
||||||
help='Re-initialize the sqlite calibre database at the '
|
help='Re-initialize the sqlite calibre database at the '
|
||||||
'specified path. Useful to recover from db corruption.')
|
'specified path. Useful to recover from db corruption.'
|
||||||
|
' You can also specify the path to an SQL dump which '
|
||||||
|
'will be used instead of trying to dump the database.'
|
||||||
|
' This can be useful when dumping fails, but dumping '
|
||||||
|
'with sqlite3 works.')
|
||||||
parser.add_option('-p', '--py-console', help='Run python console',
|
parser.add_option('-p', '--py-console', help='Run python console',
|
||||||
default=False, action='store_true')
|
default=False, action='store_true')
|
||||||
|
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def reinit_db(dbpath, callback=None):
|
def reinit_db(dbpath, callback=None, sql_dump=None):
|
||||||
if not os.path.exists(dbpath):
|
if not os.path.exists(dbpath):
|
||||||
raise ValueError(dbpath + ' does not exist')
|
raise ValueError(dbpath + ' does not exist')
|
||||||
from calibre.library.sqlite import connect
|
from calibre.library.sqlite import connect
|
||||||
@ -52,26 +56,32 @@ def reinit_db(dbpath, callback=None):
|
|||||||
uv = conn.get('PRAGMA user_version;', all=False)
|
uv = conn.get('PRAGMA user_version;', all=False)
|
||||||
conn.execute('PRAGMA writable_schema=ON')
|
conn.execute('PRAGMA writable_schema=ON')
|
||||||
conn.commit()
|
conn.commit()
|
||||||
sql_lines = conn.dump()
|
if sql_dump is None:
|
||||||
|
sql_lines = conn.dump()
|
||||||
|
else:
|
||||||
|
sql_lines = open(sql_dump, 'rb').read()
|
||||||
conn.close()
|
conn.close()
|
||||||
dest = dbpath + '.tmp'
|
dest = dbpath + '.tmp'
|
||||||
try:
|
try:
|
||||||
with closing(connect(dest, False)) as nconn:
|
with closing(connect(dest, False)) as nconn:
|
||||||
nconn.execute('create temporary table temp_sequence(id INTEGER PRIMARY KEY AUTOINCREMENT)')
|
nconn.execute('create temporary table temp_sequence(id INTEGER PRIMARY KEY AUTOINCREMENT)')
|
||||||
nconn.commit()
|
nconn.commit()
|
||||||
if callable(callback):
|
if sql_dump is None:
|
||||||
callback(len(sql_lines), True)
|
if callable(callback):
|
||||||
for i, line in enumerate(sql_lines):
|
callback(len(sql_lines), True)
|
||||||
try:
|
for i, line in enumerate(sql_lines):
|
||||||
nconn.execute(line)
|
try:
|
||||||
except:
|
nconn.execute(line)
|
||||||
import traceback
|
except:
|
||||||
prints('SQL line %r failed with error:'%line)
|
import traceback
|
||||||
prints(traceback.format_exc())
|
prints('SQL line %r failed with error:'%line)
|
||||||
continue
|
prints(traceback.format_exc())
|
||||||
finally:
|
continue
|
||||||
if callable(callback):
|
finally:
|
||||||
callback(i, False)
|
if callable(callback):
|
||||||
|
callback(i, False)
|
||||||
|
else:
|
||||||
|
nconn.executescript(sql_lines)
|
||||||
nconn.execute('pragma user_version=%d'%int(uv))
|
nconn.execute('pragma user_version=%d'%int(uv))
|
||||||
nconn.commit()
|
nconn.commit()
|
||||||
os.remove(dbpath)
|
os.remove(dbpath)
|
||||||
@ -170,7 +180,10 @@ def main(args=sys.argv):
|
|||||||
prints('CALIBRE_EXTENSIONS_PATH='+sys.extensions_location)
|
prints('CALIBRE_EXTENSIONS_PATH='+sys.extensions_location)
|
||||||
prints('CALIBRE_PYTHON_PATH='+os.pathsep.join(sys.path))
|
prints('CALIBRE_PYTHON_PATH='+os.pathsep.join(sys.path))
|
||||||
elif opts.reinitialize_db is not None:
|
elif opts.reinitialize_db is not None:
|
||||||
reinit_db(opts.reinitialize_db)
|
sql_dump = None
|
||||||
|
if len(args) > 1 and os.access(args[-1], os.R_OK):
|
||||||
|
sql_dump = args[-1]
|
||||||
|
reinit_db(opts.reinitialize_db, sql_dump=sql_dump)
|
||||||
else:
|
else:
|
||||||
from calibre import ipython
|
from calibre import ipython
|
||||||
ipython()
|
ipython()
|
||||||
|
@ -29,7 +29,9 @@ class ANDROID(USBMS):
|
|||||||
# Sony Ericsson
|
# Sony Ericsson
|
||||||
0xfce : { 0xd12e : [0x0100]},
|
0xfce : { 0xd12e : [0x0100]},
|
||||||
|
|
||||||
0x18d1 : { 0x4e11 : [0x0100, 0x226], 0x4e12: [0x0100, 0x226]},
|
# Google
|
||||||
|
0x18d1 : { 0x4e11 : [0x0100, 0x226, 0x227], 0x4e12: [0x0100, 0x226,
|
||||||
|
0x227]},
|
||||||
|
|
||||||
# Samsung
|
# Samsung
|
||||||
0x04e8 : { 0x681d : [0x0222, 0x0400],
|
0x04e8 : { 0x681d : [0x0222, 0x0400],
|
||||||
|
@ -11,6 +11,10 @@ import re
|
|||||||
|
|
||||||
from calibre.devices.usbms.driver import USBMS
|
from calibre.devices.usbms.driver import USBMS
|
||||||
|
|
||||||
|
def is_alex(device_info):
|
||||||
|
return device_info[3] == u'Linux 2.6.28 with pxa3xx_u2d' and \
|
||||||
|
device_info[4] == u'Seleucia Disk'
|
||||||
|
|
||||||
class N516(USBMS):
|
class N516(USBMS):
|
||||||
|
|
||||||
name = 'N516 driver'
|
name = 'N516 driver'
|
||||||
@ -34,6 +38,9 @@ class N516(USBMS):
|
|||||||
EBOOK_DIR_MAIN = 'e_book'
|
EBOOK_DIR_MAIN = 'e_book'
|
||||||
SUPPORTS_SUB_DIRS = True
|
SUPPORTS_SUB_DIRS = True
|
||||||
|
|
||||||
|
def can_handle(self, device_info, debug=False):
|
||||||
|
return not is_alex(device_info)
|
||||||
|
|
||||||
class THEBOOK(N516):
|
class THEBOOK(N516):
|
||||||
name = 'The Book driver'
|
name = 'The Book driver'
|
||||||
gui_name = 'The Book'
|
gui_name = 'The Book'
|
||||||
@ -61,6 +68,9 @@ class ALEX(N516):
|
|||||||
EBOOK_DIR_MAIN = 'eBooks'
|
EBOOK_DIR_MAIN = 'eBooks'
|
||||||
SUPPORTS_SUB_DIRS = True
|
SUPPORTS_SUB_DIRS = True
|
||||||
|
|
||||||
|
def can_handle(self, device_info, debug=False):
|
||||||
|
return is_alex(device_info)
|
||||||
|
|
||||||
class AZBOOKA(ALEX):
|
class AZBOOKA(ALEX):
|
||||||
|
|
||||||
name = 'Azbooka driver'
|
name = 'Azbooka driver'
|
||||||
@ -74,6 +84,9 @@ class AZBOOKA(ALEX):
|
|||||||
|
|
||||||
EBOOK_DIR_MAIN = ''
|
EBOOK_DIR_MAIN = ''
|
||||||
|
|
||||||
|
def can_handle(self, device_info, debug=False):
|
||||||
|
return not is_alex(device_info)
|
||||||
|
|
||||||
|
|
||||||
class EB511(USBMS):
|
class EB511(USBMS):
|
||||||
name = 'Elonex EB 511 driver'
|
name = 'Elonex EB 511 driver'
|
||||||
|
@ -98,6 +98,8 @@ class KOBO(USBMS):
|
|||||||
|
|
||||||
if readstatus == 1:
|
if readstatus == 1:
|
||||||
playlist_map[lpath]= "Im_Reading"
|
playlist_map[lpath]= "Im_Reading"
|
||||||
|
elif readstatus == 2:
|
||||||
|
playlist_map[lpath]= "Read"
|
||||||
|
|
||||||
path = self.normalize_path(path)
|
path = self.normalize_path(path)
|
||||||
# print "Normalized FileName: " + path
|
# print "Normalized FileName: " + path
|
||||||
@ -441,43 +443,99 @@ class KOBO(USBMS):
|
|||||||
connection = sqlite.connect(self._main_prefix + '.kobo/KoboReader.sqlite')
|
connection = sqlite.connect(self._main_prefix + '.kobo/KoboReader.sqlite')
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
|
|
||||||
# Reset Im_Reading list in the database
|
|
||||||
if oncard == 'carda':
|
if collections:
|
||||||
query= 'update content set ReadStatus=0, FirstTimeReading = \'true\' where BookID is Null and ReadStatus = 1 and ContentID like \'file:///mnt/sd/%\''
|
# Process any collections that exist
|
||||||
elif oncard != 'carda' and oncard != 'cardb':
|
for category, books in collections.items():
|
||||||
query= 'update content set ReadStatus=0, FirstTimeReading = \'true\' where BookID is Null and ReadStatus = 1 and ContentID not like \'file:///mnt/sd/%\''
|
if category == 'Im_Reading':
|
||||||
|
# Reset Im_Reading list in the database
|
||||||
|
if oncard == 'carda':
|
||||||
|
query= 'update content set ReadStatus=0, FirstTimeReading = \'true\' where BookID is Null and ReadStatus = 1 and ContentID like \'file:///mnt/sd/%\''
|
||||||
|
elif oncard != 'carda' and oncard != 'cardb':
|
||||||
|
query= 'update content set ReadStatus=0, FirstTimeReading = \'true\' where BookID is Null and ReadStatus = 1 and ContentID not like \'file:///mnt/sd/%\''
|
||||||
|
|
||||||
try:
|
|
||||||
cursor.execute (query)
|
|
||||||
except:
|
|
||||||
debug_print('Database Exception: Unable to reset Im_Reading list')
|
|
||||||
raise
|
|
||||||
else:
|
|
||||||
# debug_print('Commit: Reset Im_Reading list')
|
|
||||||
connection.commit()
|
|
||||||
|
|
||||||
for category, books in collections.items():
|
|
||||||
if category == 'Im_Reading':
|
|
||||||
for book in books:
|
|
||||||
# debug_print('Title:', book.title, 'lpath:', book.path)
|
|
||||||
book.device_collections = ['Im_Reading']
|
|
||||||
|
|
||||||
extension = os.path.splitext(book.path)[1]
|
|
||||||
ContentType = self.get_content_type_from_extension(extension)
|
|
||||||
|
|
||||||
ContentID = self.contentid_from_path(book.path, ContentType)
|
|
||||||
datelastread = time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime())
|
|
||||||
|
|
||||||
t = (datelastread,ContentID,)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cursor.execute('update content set ReadStatus=1,FirstTimeReading=\'false\',DateLastRead=? where BookID is Null and ContentID = ?', t)
|
cursor.execute (query)
|
||||||
except:
|
except:
|
||||||
debug_print('Database Exception: Unable create Im_Reading list')
|
debug_print('Database Exception: Unable to reset Im_Reading list')
|
||||||
raise
|
raise
|
||||||
else:
|
else:
|
||||||
|
# debug_print('Commit: Reset Im_Reading list')
|
||||||
connection.commit()
|
connection.commit()
|
||||||
# debug_print('Database: Commit create Im_Reading list')
|
|
||||||
|
for book in books:
|
||||||
|
# debug_print('Title:', book.title, 'lpath:', book.path)
|
||||||
|
book.device_collections = ['Im_Reading']
|
||||||
|
|
||||||
|
extension = os.path.splitext(book.path)[1]
|
||||||
|
ContentType = self.get_content_type_from_extension(extension)
|
||||||
|
|
||||||
|
ContentID = self.contentid_from_path(book.path, ContentType)
|
||||||
|
datelastread = time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime())
|
||||||
|
|
||||||
|
t = (datelastread,ContentID,)
|
||||||
|
|
||||||
|
try:
|
||||||
|
cursor.execute('update content set ReadStatus=1,FirstTimeReading=\'false\',DateLastRead=? where BookID is Null and ContentID = ?', t)
|
||||||
|
except:
|
||||||
|
debug_print('Database Exception: Unable create Im_Reading list')
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
connection.commit()
|
||||||
|
# debug_print('Database: Commit create Im_Reading list')
|
||||||
|
if category == 'Read':
|
||||||
|
# Reset Im_Reading list in the database
|
||||||
|
if oncard == 'carda':
|
||||||
|
query= 'update content set ReadStatus=0, FirstTimeReading = \'true\' where BookID is Null and ReadStatus = 2 and ContentID like \'file:///mnt/sd/%\''
|
||||||
|
elif oncard != 'carda' and oncard != 'cardb':
|
||||||
|
query= 'update content set ReadStatus=0, FirstTimeReading = \'true\' where BookID is Null and ReadStatus = 2 and ContentID not like \'file:///mnt/sd/%\''
|
||||||
|
|
||||||
|
try:
|
||||||
|
cursor.execute (query)
|
||||||
|
except:
|
||||||
|
debug_print('Database Exception: Unable to reset Im_Reading list')
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
# debug_print('Commit: Reset Im_Reading list')
|
||||||
|
connection.commit()
|
||||||
|
|
||||||
|
for book in books:
|
||||||
|
# debug_print('Title:', book.title, 'lpath:', book.path)
|
||||||
|
book.device_collections = ['Read']
|
||||||
|
|
||||||
|
extension = os.path.splitext(book.path)[1]
|
||||||
|
ContentType = self.get_content_type_from_extension(extension)
|
||||||
|
|
||||||
|
ContentID = self.contentid_from_path(book.path, ContentType)
|
||||||
|
# datelastread = time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime())
|
||||||
|
|
||||||
|
t = (ContentID,)
|
||||||
|
|
||||||
|
try:
|
||||||
|
cursor.execute('update content set ReadStatus=2,FirstTimeReading=\'true\' where BookID is Null and ContentID = ?', t)
|
||||||
|
except:
|
||||||
|
debug_print('Database Exception: Unable set book as Rinished')
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
connection.commit()
|
||||||
|
# debug_print('Database: Commit set ReadStatus as Finished')
|
||||||
|
else: # No collections
|
||||||
|
# Since no collections exist the ReadStatus needs to be reset to 0 (Unread)
|
||||||
|
print "Reseting ReadStatus to 0"
|
||||||
|
# Reset Im_Reading list in the database
|
||||||
|
if oncard == 'carda':
|
||||||
|
query= 'update content set ReadStatus=0, FirstTimeReading = \'true\' where BookID is Null and ContentID like \'file:///mnt/sd/%\''
|
||||||
|
elif oncard != 'carda' and oncard != 'cardb':
|
||||||
|
query= 'update content set ReadStatus=0, FirstTimeReading = \'true\' where BookID is Null and ContentID not like \'file:///mnt/sd/%\''
|
||||||
|
|
||||||
|
try:
|
||||||
|
cursor.execute (query)
|
||||||
|
except:
|
||||||
|
debug_print('Database Exception: Unable to reset Im_Reading list')
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
# debug_print('Commit: Reset Im_Reading list')
|
||||||
|
connection.commit()
|
||||||
|
|
||||||
cursor.close()
|
cursor.close()
|
||||||
connection.close()
|
connection.close()
|
||||||
|
@ -1574,14 +1574,15 @@ class MobiWriter(object):
|
|||||||
id = unicode(oeb.metadata.cover[0])
|
id = unicode(oeb.metadata.cover[0])
|
||||||
item = oeb.manifest.ids[id]
|
item = oeb.manifest.ids[id]
|
||||||
href = item.href
|
href = item.href
|
||||||
index = self._images[href] - 1
|
if href in self._images:
|
||||||
exth.write(pack('>III', 0xc9, 0x0c, index))
|
index = self._images[href] - 1
|
||||||
exth.write(pack('>III', 0xcb, 0x0c, 0))
|
exth.write(pack('>III', 0xc9, 0x0c, index))
|
||||||
nrecs += 2
|
exth.write(pack('>III', 0xcb, 0x0c, 0))
|
||||||
index = self._add_thumbnail(item)
|
nrecs += 2
|
||||||
if index is not None:
|
index = self._add_thumbnail(item)
|
||||||
exth.write(pack('>III', 0xca, 0x0c, index - 1))
|
if index is not None:
|
||||||
nrecs += 1
|
exth.write(pack('>III', 0xca, 0x0c, index - 1))
|
||||||
|
nrecs += 1
|
||||||
|
|
||||||
exth = exth.getvalue()
|
exth = exth.getvalue()
|
||||||
trail = len(exth) % 4
|
trail = len(exth) % 4
|
||||||
|
@ -590,7 +590,11 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
mi.pubdate = self.pubdate(idx, index_is_id=index_is_id)
|
mi.pubdate = self.pubdate(idx, index_is_id=index_is_id)
|
||||||
mi.uuid = self.uuid(idx, index_is_id=index_is_id)
|
mi.uuid = self.uuid(idx, index_is_id=index_is_id)
|
||||||
mi.title_sort = self.title_sort(idx, index_is_id=index_is_id)
|
mi.title_sort = self.title_sort(idx, index_is_id=index_is_id)
|
||||||
mi.formats = self.formats(idx, index_is_id=index_is_id).split(',')
|
mi.formats = self.formats(idx, index_is_id=index_is_id)
|
||||||
|
if hasattr(mi.formats, 'split'):
|
||||||
|
mi.formats = mi.formats.split(',')
|
||||||
|
else:
|
||||||
|
mi.formats = None
|
||||||
tags = self.tags(idx, index_is_id=index_is_id)
|
tags = self.tags(idx, index_is_id=index_is_id)
|
||||||
if tags:
|
if tags:
|
||||||
mi.tags = [i.strip() for i in tags.split(',')]
|
mi.tags = [i.strip() for i in tags.split(',')]
|
||||||
@ -1213,7 +1217,10 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
|
|
||||||
def set_authors(self, id, authors, notify=True, commit=True):
|
def set_authors(self, id, authors, notify=True, commit=True):
|
||||||
'''
|
'''
|
||||||
`authors`: A list of authors.
|
Note that even if commit is False, the db will still be committed to
|
||||||
|
because this causes the location of files to change
|
||||||
|
|
||||||
|
:param authors: A list of authors.
|
||||||
'''
|
'''
|
||||||
if not authors:
|
if not authors:
|
||||||
authors = [_('Unknown')]
|
authors = [_('Unknown')]
|
||||||
@ -1250,6 +1257,10 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
self.notify('metadata', [id])
|
self.notify('metadata', [id])
|
||||||
|
|
||||||
def set_title(self, id, title, notify=True, commit=True):
|
def set_title(self, id, title, notify=True, commit=True):
|
||||||
|
'''
|
||||||
|
Note that even if commit is False, the db will still be committed to
|
||||||
|
because this causes the location of files to change
|
||||||
|
'''
|
||||||
if not title:
|
if not title:
|
||||||
return
|
return
|
||||||
if not isinstance(title, unicode):
|
if not isinstance(title, unicode):
|
||||||
|
@ -60,15 +60,15 @@ def identify(path):
|
|||||||
data = open(path, 'rb').read()
|
data = open(path, 'rb').read()
|
||||||
return identify_data(data)
|
return identify_data(data)
|
||||||
|
|
||||||
def add_borders_to_image(path_to_image, left=0, top=0, right=0, bottom=0,
|
def add_borders_to_image(img_data, left=0, top=0, right=0, bottom=0,
|
||||||
border_color='#ffffff'):
|
border_color='#ffffff', fmt='jpg'):
|
||||||
img = Image()
|
img = Image()
|
||||||
img.open(path_to_image)
|
img.load(img_data)
|
||||||
lwidth, lheight = img.size
|
lwidth, lheight = img.size
|
||||||
canvas = create_canvas(lwidth+left+right, lheight+top+bottom,
|
canvas = create_canvas(lwidth+left+right, lheight+top+bottom,
|
||||||
border_color)
|
border_color)
|
||||||
canvas.compose(img, left, top)
|
canvas.compose(img, left, top)
|
||||||
canvas.save(path_to_image)
|
return canvas.export(fmt)
|
||||||
|
|
||||||
def create_text_wand(font_size, font_path=None):
|
def create_text_wand(font_size, font_path=None):
|
||||||
if font_path is None:
|
if font_path is None:
|
||||||
|
@ -8,14 +8,18 @@ __docformat__ = 'restructuredtext en'
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
from calibre import prints as prints_
|
from calibre import prints as prints_
|
||||||
from calibre.utils.config import Config, StringConfig
|
from calibre.utils.config import Config, ConfigProxy
|
||||||
|
|
||||||
|
|
||||||
def console_config(defaults=None):
|
def console_config():
|
||||||
desc=_('Settings to control the calibre content server')
|
desc='Settings to control the calibre console'
|
||||||
c = Config('console', desc) if defaults is None else StringConfig(defaults, desc)
|
c = Config('console', desc)
|
||||||
|
|
||||||
c.add_opt('--theme', default='default', help='The color theme')
|
c.add_opt('theme', default='native', help='The color theme')
|
||||||
|
|
||||||
|
return c
|
||||||
|
|
||||||
|
prefs = ConfigProxy(console_config())
|
||||||
|
|
||||||
|
|
||||||
def prints(*args, **kwargs):
|
def prints(*args, **kwargs):
|
||||||
|
@ -6,16 +6,18 @@ __copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
|||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import sys, textwrap, traceback, StringIO
|
import sys, textwrap, traceback, StringIO
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
from PyQt4.Qt import QTextEdit, Qt, QTextFrameFormat, pyqtSignal, \
|
from PyQt4.Qt import QTextEdit, Qt, QTextFrameFormat, pyqtSignal, \
|
||||||
QCoreApplication
|
QApplication, QColor, QPalette, QMenu, QActionGroup
|
||||||
|
|
||||||
from pygments.lexers import PythonLexer, PythonTracebackLexer
|
from pygments.lexers import PythonLexer, PythonTracebackLexer
|
||||||
|
from pygments.styles import get_all_styles
|
||||||
|
|
||||||
from calibre.constants import __appname__, __version__
|
from calibre.constants import __appname__, __version__
|
||||||
from calibre.utils.pyconsole.formatter import Formatter
|
from calibre.utils.pyconsole.formatter import Formatter
|
||||||
from calibre.utils.pyconsole.repl import Interpreter, DummyFile
|
from calibre.utils.pyconsole.repl import Interpreter, DummyFile
|
||||||
from calibre.utils.pyconsole import prints
|
from calibre.utils.pyconsole import prints, prefs
|
||||||
from calibre.gui2 import error_dialog
|
from calibre.gui2 import error_dialog
|
||||||
|
|
||||||
class EditBlock(object): # {{{
|
class EditBlock(object): # {{{
|
||||||
@ -47,6 +49,30 @@ class Prepender(object): # {{{
|
|||||||
self.console.cursor_pos = self.opos
|
self.console.cursor_pos = self.opos
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
class ThemeMenu(QMenu): # {{{
|
||||||
|
|
||||||
|
def __init__(self, parent):
|
||||||
|
QMenu.__init__(self, _('Choose theme (needs restart)'))
|
||||||
|
parent.addMenu(self)
|
||||||
|
self.group = QActionGroup(self)
|
||||||
|
current = prefs['theme']
|
||||||
|
alls = list(sorted(get_all_styles()))
|
||||||
|
if current not in alls:
|
||||||
|
current = prefs['theme'] = 'default'
|
||||||
|
self.actions = []
|
||||||
|
for style in alls:
|
||||||
|
ac = self.group.addAction(style)
|
||||||
|
ac.setCheckable(True)
|
||||||
|
if current == style:
|
||||||
|
ac.setChecked(True)
|
||||||
|
self.actions.append(ac)
|
||||||
|
ac.triggered.connect(partial(self.set_theme, style))
|
||||||
|
self.addAction(ac)
|
||||||
|
|
||||||
|
def set_theme(self, style, *args):
|
||||||
|
prefs['theme'] = style
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
class Console(QTextEdit):
|
class Console(QTextEdit):
|
||||||
|
|
||||||
@ -99,8 +125,16 @@ class Console(QTextEdit):
|
|||||||
self.doc.setMaximumBlockCount(10000)
|
self.doc.setMaximumBlockCount(10000)
|
||||||
self.lexer = PythonLexer(ensurenl=False)
|
self.lexer = PythonLexer(ensurenl=False)
|
||||||
self.tb_lexer = PythonTracebackLexer()
|
self.tb_lexer = PythonTracebackLexer()
|
||||||
self.formatter = Formatter(prompt, continuation, style='default')
|
|
||||||
self.setStyleSheet(self.formatter.stylesheet)
|
self.context_menu = cm = QMenu(self) # {{{
|
||||||
|
cm.theme = ThemeMenu(cm)
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
self.formatter = Formatter(prompt, continuation, style=prefs['theme'])
|
||||||
|
p = QPalette()
|
||||||
|
p.setColor(p.Base, QColor(self.formatter.background_color))
|
||||||
|
p.setColor(p.Text, QColor(self.formatter.color))
|
||||||
|
self.setPalette(p)
|
||||||
|
|
||||||
self.key_dispatcher = { # {{{
|
self.key_dispatcher = { # {{{
|
||||||
Qt.Key_Enter : self.enter_pressed,
|
Qt.Key_Enter : self.enter_pressed,
|
||||||
@ -109,6 +143,8 @@ class Console(QTextEdit):
|
|||||||
Qt.Key_End : self.end_pressed,
|
Qt.Key_End : self.end_pressed,
|
||||||
Qt.Key_Left : self.left_pressed,
|
Qt.Key_Left : self.left_pressed,
|
||||||
Qt.Key_Right : self.right_pressed,
|
Qt.Key_Right : self.right_pressed,
|
||||||
|
Qt.Key_Backspace : self.backspace_pressed,
|
||||||
|
Qt.Key_Delete : self.delete_pressed,
|
||||||
} # }}}
|
} # }}}
|
||||||
|
|
||||||
motd = textwrap.dedent('''\
|
motd = textwrap.dedent('''\
|
||||||
@ -127,6 +163,9 @@ class Console(QTextEdit):
|
|||||||
|
|
||||||
sys.excepthook = self.unhandled_exception
|
sys.excepthook = self.unhandled_exception
|
||||||
|
|
||||||
|
def contextMenuEvent(self, event):
|
||||||
|
self.context_menu.popup(event.globalPos())
|
||||||
|
event.accept()
|
||||||
|
|
||||||
# Prompt management {{{
|
# Prompt management {{{
|
||||||
|
|
||||||
@ -239,7 +278,7 @@ class Console(QTextEdit):
|
|||||||
except:
|
except:
|
||||||
prints(tb, end='')
|
prints(tb, end='')
|
||||||
self.ensureCursorVisible()
|
self.ensureCursorVisible()
|
||||||
QCoreApplication.processEvents()
|
QApplication.processEvents()
|
||||||
|
|
||||||
def show_output(self, raw):
|
def show_output(self, raw):
|
||||||
def do_show():
|
def do_show():
|
||||||
@ -257,7 +296,7 @@ class Console(QTextEdit):
|
|||||||
else:
|
else:
|
||||||
do_show()
|
do_show()
|
||||||
self.ensureCursorVisible()
|
self.ensureCursorVisible()
|
||||||
QCoreApplication.processEvents()
|
QApplication.processEvents()
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
@ -290,6 +329,22 @@ class Console(QTextEdit):
|
|||||||
self.setTextCursor(c)
|
self.setTextCursor(c)
|
||||||
self.ensureCursorVisible()
|
self.ensureCursorVisible()
|
||||||
|
|
||||||
|
def backspace_pressed(self):
|
||||||
|
lineno, pos = self.cursor_pos
|
||||||
|
if lineno < 0: return
|
||||||
|
if pos > self.prompt_len:
|
||||||
|
self.cursor.deletePreviousChar()
|
||||||
|
elif lineno > 0:
|
||||||
|
c = self.cursor
|
||||||
|
c.movePosition(c.Up)
|
||||||
|
c.movePosition(c.EndOfLine)
|
||||||
|
self.setTextCursor(c)
|
||||||
|
self.ensureCursorVisible()
|
||||||
|
|
||||||
|
def delete_pressed(self):
|
||||||
|
self.cursor.deleteChar()
|
||||||
|
self.ensureCursorVisible()
|
||||||
|
|
||||||
def right_pressed(self):
|
def right_pressed(self):
|
||||||
lineno, pos = self.cursor_pos
|
lineno, pos = self.cursor_pos
|
||||||
if lineno < 0: return
|
if lineno < 0: return
|
||||||
@ -305,14 +360,23 @@ class Console(QTextEdit):
|
|||||||
|
|
||||||
def home_pressed(self):
|
def home_pressed(self):
|
||||||
if self.prompt_frame is not None:
|
if self.prompt_frame is not None:
|
||||||
c = self.cursor
|
mods = QApplication.keyboardModifiers()
|
||||||
c.movePosition(c.StartOfLine)
|
ctrl = bool(int(mods & Qt.CTRL))
|
||||||
c.movePosition(c.NextCharacter, n=self.prompt_len)
|
if ctrl:
|
||||||
self.setTextCursor(c)
|
self.cursor_pos = (0, self.prompt_len)
|
||||||
|
else:
|
||||||
|
c = self.cursor
|
||||||
|
c.movePosition(c.StartOfLine)
|
||||||
|
c.movePosition(c.NextCharacter, n=self.prompt_len)
|
||||||
|
self.setTextCursor(c)
|
||||||
self.ensureCursorVisible()
|
self.ensureCursorVisible()
|
||||||
|
|
||||||
def end_pressed(self):
|
def end_pressed(self):
|
||||||
if self.prompt_frame is not None:
|
if self.prompt_frame is not None:
|
||||||
|
mods = QApplication.keyboardModifiers()
|
||||||
|
ctrl = bool(int(mods & Qt.CTRL))
|
||||||
|
if ctrl:
|
||||||
|
self.cursor_pos = (len(list(self.prompt()))-1, self.prompt_len)
|
||||||
c = self.cursor
|
c = self.cursor
|
||||||
c.movePosition(c.EndOfLine)
|
c.movePosition(c.EndOfLine)
|
||||||
self.setTextCursor(c)
|
self.setTextCursor(c)
|
||||||
|
@ -8,18 +8,20 @@ __docformat__ = 'restructuredtext en'
|
|||||||
from PyQt4.Qt import QTextCharFormat, QFont, QBrush, QColor
|
from PyQt4.Qt import QTextCharFormat, QFont, QBrush, QColor
|
||||||
|
|
||||||
from pygments.formatter import Formatter as PF
|
from pygments.formatter import Formatter as PF
|
||||||
from pygments.token import Token, Generic
|
from pygments.token import Token, Generic, string_to_tokentype
|
||||||
|
|
||||||
class Formatter(object):
|
class Formatter(object):
|
||||||
|
|
||||||
def __init__(self, prompt, continuation, **options):
|
def __init__(self, prompt, continuation, style='default'):
|
||||||
if len(prompt) != len(continuation):
|
if len(prompt) != len(continuation):
|
||||||
raise ValueError('%r does not have the same length as %r' %
|
raise ValueError('%r does not have the same length as %r' %
|
||||||
(prompt, continuation))
|
(prompt, continuation))
|
||||||
|
|
||||||
self.prompt, self.continuation = prompt, continuation
|
self.prompt, self.continuation = prompt, continuation
|
||||||
|
self.set_style(style)
|
||||||
|
|
||||||
pf = PF(**options)
|
def set_style(self, style):
|
||||||
|
pf = PF(style=style)
|
||||||
self.styles = {}
|
self.styles = {}
|
||||||
self.normal = self.base_fmt()
|
self.normal = self.base_fmt()
|
||||||
self.background_color = pf.style.background_color
|
self.background_color = pf.style.background_color
|
||||||
@ -27,6 +29,7 @@ class Formatter(object):
|
|||||||
|
|
||||||
for ttype, ndef in pf.style:
|
for ttype, ndef in pf.style:
|
||||||
fmt = self.base_fmt()
|
fmt = self.base_fmt()
|
||||||
|
fmt.setProperty(fmt.UserProperty, str(ttype))
|
||||||
if ndef['color']:
|
if ndef['color']:
|
||||||
fmt.setForeground(QBrush(QColor('#%s'%ndef['color'])))
|
fmt.setForeground(QBrush(QColor('#%s'%ndef['color'])))
|
||||||
fmt.setUnderlineColor(QColor('#%s'%ndef['color']))
|
fmt.setUnderlineColor(QColor('#%s'%ndef['color']))
|
||||||
@ -45,10 +48,14 @@ class Formatter(object):
|
|||||||
|
|
||||||
self.styles[ttype] = fmt
|
self.styles[ttype] = fmt
|
||||||
|
|
||||||
self.stylesheet = '''
|
def get_fmt(self, token):
|
||||||
QTextEdit { color: %s; background-color: %s }
|
if type(token) != type(Token.Generic):
|
||||||
'''%(self.color, self.background_color)
|
token = string_to_tokentype(token)
|
||||||
|
fmt = self.styles.get(token, None)
|
||||||
|
if fmt is None:
|
||||||
|
fmt = self.base_fmt()
|
||||||
|
fmt.setProperty(fmt.UserProperty, str(token))
|
||||||
|
return fmt
|
||||||
|
|
||||||
def base_fmt(self):
|
def base_fmt(self):
|
||||||
fmt = QTextCharFormat()
|
fmt = QTextCharFormat()
|
||||||
@ -59,7 +66,7 @@ class Formatter(object):
|
|||||||
cursor.insertText(raw, self.normal)
|
cursor.insertText(raw, self.normal)
|
||||||
|
|
||||||
def render_syntax_error(self, tb, cursor):
|
def render_syntax_error(self, tb, cursor):
|
||||||
fmt = self.styles[Token.Error]
|
fmt = self.get_fmt(Token.Error)
|
||||||
cursor.insertText(tb, fmt)
|
cursor.insertText(tb, fmt)
|
||||||
|
|
||||||
def render(self, tokens, cursor):
|
def render(self, tokens, cursor):
|
||||||
@ -84,7 +91,9 @@ class Formatter(object):
|
|||||||
|
|
||||||
def render_prompt(self, is_continuation, cursor):
|
def render_prompt(self, is_continuation, cursor):
|
||||||
pr = self.continuation if is_continuation else self.prompt
|
pr = self.continuation if is_continuation else self.prompt
|
||||||
fmt = self.styles[Generic.Prompt]
|
fmt = self.get_fmt(Generic.Prompt)
|
||||||
|
if fmt is None:
|
||||||
|
fmt = self.base_fmt()
|
||||||
cursor.insertText(pr, fmt)
|
cursor.insertText(pr, fmt)
|
||||||
|
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ __version__ = '0.1.0'
|
|||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
from PyQt4.Qt import QDialog, QToolBar, QStatusBar, QLabel, QFont, Qt, \
|
from PyQt4.Qt import QDialog, QToolBar, QStatusBar, QLabel, QFont, Qt, \
|
||||||
QApplication, QIcon, QVBoxLayout
|
QApplication, QIcon, QVBoxLayout, QAction
|
||||||
|
|
||||||
from calibre.constants import __appname__, __version__
|
from calibre.constants import __appname__, __version__
|
||||||
from calibre.utils.pyconsole.console import Console
|
from calibre.utils.pyconsole.console import Console
|
||||||
@ -19,8 +19,9 @@ class MainWindow(QDialog):
|
|||||||
def __init__(self,
|
def __init__(self,
|
||||||
default_status_msg=_('Welcome to') + ' ' + __appname__+' console',
|
default_status_msg=_('Welcome to') + ' ' + __appname__+' console',
|
||||||
parent=None):
|
parent=None):
|
||||||
|
|
||||||
QDialog.__init__(self, parent)
|
QDialog.__init__(self, parent)
|
||||||
|
|
||||||
|
self.restart_requested = False
|
||||||
self.l = QVBoxLayout()
|
self.l = QVBoxLayout()
|
||||||
self.setLayout(self.l)
|
self.setLayout(self.l)
|
||||||
|
|
||||||
@ -51,14 +52,26 @@ class MainWindow(QDialog):
|
|||||||
self.setWindowTitle(__appname__ + ' console')
|
self.setWindowTitle(__appname__ + ' console')
|
||||||
self.setWindowIcon(QIcon(I('console.png')))
|
self.setWindowIcon(QIcon(I('console.png')))
|
||||||
|
|
||||||
|
self.restart_action = QAction(_('Restart console'), self)
|
||||||
|
self.restart_action.setShortcut(_('Ctrl+R'))
|
||||||
|
self.addAction(self.restart_action)
|
||||||
|
self.restart_action.triggered.connect(self.restart)
|
||||||
|
self.console.context_menu.addAction(self.restart_action)
|
||||||
|
|
||||||
|
def restart(self):
|
||||||
|
self.restart_requested = True
|
||||||
|
self.reject()
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
QApplication.setApplicationName(__appname__+' console')
|
QApplication.setApplicationName(__appname__+' console')
|
||||||
QApplication.setOrganizationName('Kovid Goyal')
|
QApplication.setOrganizationName('Kovid Goyal')
|
||||||
app = QApplication([])
|
app = QApplication([])
|
||||||
m = MainWindow()
|
app
|
||||||
m.show()
|
while True:
|
||||||
app.exec_()
|
m = MainWindow()
|
||||||
|
m.exec_()
|
||||||
|
if not m.restart_requested:
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@ -7,7 +7,7 @@ Defines various abstract base classes that can be subclassed to create powerful
|
|||||||
__docformat__ = "restructuredtext en"
|
__docformat__ = "restructuredtext en"
|
||||||
|
|
||||||
|
|
||||||
import os, time, traceback, re, urlparse, sys
|
import os, time, traceback, re, urlparse, sys, cStringIO
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from contextlib import nested, closing
|
from contextlib import nested, closing
|
||||||
@ -27,6 +27,7 @@ from calibre.web.fetch.simple import RecursiveFetcher
|
|||||||
from calibre.utils.threadpool import WorkRequest, ThreadPool, NoResultsPending
|
from calibre.utils.threadpool import WorkRequest, ThreadPool, NoResultsPending
|
||||||
from calibre.ptempfile import PersistentTemporaryFile
|
from calibre.ptempfile import PersistentTemporaryFile
|
||||||
from calibre.utils.date import now as nowf
|
from calibre.utils.date import now as nowf
|
||||||
|
from calibre.utils.magick.draw import save_cover_data_to, add_borders_to_image
|
||||||
|
|
||||||
class LoginFailed(ValueError):
|
class LoginFailed(ValueError):
|
||||||
pass
|
pass
|
||||||
@ -948,38 +949,36 @@ class BasicNewsRecipe(Recipe):
|
|||||||
try:
|
try:
|
||||||
cu = self.get_cover_url()
|
cu = self.get_cover_url()
|
||||||
except Exception, err:
|
except Exception, err:
|
||||||
cu = None
|
|
||||||
self.log.error(_('Could not download cover: %s')%str(err))
|
self.log.error(_('Could not download cover: %s')%str(err))
|
||||||
self.log.debug(traceback.format_exc())
|
self.log.debug(traceback.format_exc())
|
||||||
if cu is not None:
|
else:
|
||||||
ext = cu.split('/')[-1].rpartition('.')[-1]
|
cdata = None
|
||||||
if '?' in ext:
|
|
||||||
ext = ''
|
|
||||||
ext = ext.lower() if ext and '/' not in ext else 'jpg'
|
|
||||||
cpath = os.path.join(self.output_dir, 'cover.'+ext)
|
|
||||||
if os.access(cu, os.R_OK):
|
if os.access(cu, os.R_OK):
|
||||||
with open(cpath, 'wb') as cfile:
|
cdata = open(cu, 'rb').read()
|
||||||
cfile.write(open(cu, 'rb').read())
|
|
||||||
else:
|
else:
|
||||||
self.report_progress(1, _('Downloading cover from %s')%cu)
|
self.report_progress(1, _('Downloading cover from %s')%cu)
|
||||||
with nested(open(cpath, 'wb'), closing(self.browser.open(cu))) as (cfile, r):
|
with closing(self.browser.open(cu)) as r:
|
||||||
cfile.write(r.read())
|
cdata = r.read()
|
||||||
if self.cover_margins[0] or self.cover_margins[1]:
|
if not cdata:
|
||||||
from calibre.utils.magick.draw import add_borders_to_image
|
return
|
||||||
add_borders_to_image(cpath,
|
ext = cu.split('/')[-1].rpartition('.')[-1].lower().strip()
|
||||||
left=self.cover_margins[0],right=self.cover_margins[0],
|
if ext == 'pdf':
|
||||||
top=self.cover_margins[1],bottom=self.cover_margins[1],
|
|
||||||
border_color=self.cover_margins[2])
|
|
||||||
if ext.lower() == 'pdf':
|
|
||||||
from calibre.ebooks.metadata.pdf import get_metadata
|
from calibre.ebooks.metadata.pdf import get_metadata
|
||||||
stream = open(cpath, 'rb')
|
stream = cStringIO.StringIO(cdata)
|
||||||
|
cdata = None
|
||||||
mi = get_metadata(stream)
|
mi = get_metadata(stream)
|
||||||
cpath = None
|
|
||||||
if mi.cover_data and mi.cover_data[1]:
|
if mi.cover_data and mi.cover_data[1]:
|
||||||
cpath = os.path.join(self.output_dir,
|
cdata = mi.cover_data[1]
|
||||||
'cover.'+mi.cover_data[0])
|
if not cdata:
|
||||||
with open(cpath, 'wb') as f:
|
return
|
||||||
f.write(mi.cover_data[1])
|
if self.cover_margins[0] or self.cover_margins[1]:
|
||||||
|
cdata = add_borders_to_image(cdata,
|
||||||
|
left=self.cover_margins[0],right=self.cover_margins[0],
|
||||||
|
top=self.cover_margins[1],bottom=self.cover_margins[1],
|
||||||
|
border_color=self.cover_margins[2])
|
||||||
|
|
||||||
|
cpath = os.path.join(self.output_dir, 'cover.jpg')
|
||||||
|
save_cover_data_to(cdata, cpath)
|
||||||
self.cover_path = cpath
|
self.cover_path = cpath
|
||||||
|
|
||||||
def download_cover(self):
|
def download_cover(self):
|
||||||
@ -1422,7 +1421,6 @@ class CalibrePeriodical(BasicNewsRecipe):
|
|||||||
return br
|
return br
|
||||||
|
|
||||||
def download(self):
|
def download(self):
|
||||||
import cStringIO
|
|
||||||
self.log('Fetching downloaded recipe')
|
self.log('Fetching downloaded recipe')
|
||||||
try:
|
try:
|
||||||
raw = self.browser.open_novisit(
|
raw = self.browser.open_novisit(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user