KG updates

This commit is contained in:
GRiker 2010-09-21 19:55:08 -07:00
commit 0474230567
12 changed files with 202 additions and 88 deletions

View File

@ -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 = [

View File

@ -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

View File

@ -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],

View File

@ -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'

View File

@ -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()

View File

@ -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

View File

@ -1,7 +1,7 @@
__license__ = 'GPL v3' __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>' __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
""" The GUI """ """ The GUI """
import os, sys import os, sys, Queue
from threading import RLock from threading import RLock
from PyQt4.Qt import QVariant, QFileInfo, QObject, SIGNAL, QBuffer, Qt, \ from PyQt4.Qt import QVariant, QFileInfo, QObject, SIGNAL, QBuffer, Qt, \
@ -296,6 +296,34 @@ class Dispatcher(QObject):
def dispatch(self, args, kwargs): def dispatch(self, args, kwargs):
self.func(*args, **kwargs) self.func(*args, **kwargs)
class FunctionDispatcher(QObject):
'''
Convenience class to use Qt signals with arbitrary python functions.
By default, ensures that a function call always happens in the
thread this Dispatcher was created in.
'''
dispatch_signal = pyqtSignal(object, object, object)
def __init__(self, func, queued=True, parent=None):
QObject.__init__(self, parent)
self.func = func
typ = Qt.QueuedConnection
if not queued:
typ = Qt.AutoConnection if queued is None else Qt.DirectConnection
self.dispatch_signal.connect(self.dispatch, type=typ)
def __call__(self, *args, **kwargs):
q = Queue.Queue()
self.dispatch_signal.emit(q, args, kwargs)
return q.get()
def dispatch(self, q, args, kwargs):
try:
res = self.func(*args, **kwargs)
except:
res = None
q.put(res)
class GetMetadata(QObject): class GetMetadata(QObject):
''' '''
Convenience class to ensure that metadata readers are used only in the Convenience class to ensure that metadata readers are used only in the

View File

@ -12,7 +12,7 @@ from operator import attrgetter
from PyQt4.Qt import QAbstractTableModel, Qt, pyqtSignal, QIcon, QImage, \ from PyQt4.Qt import QAbstractTableModel, Qt, pyqtSignal, QIcon, QImage, \
QModelIndex, QVariant, QDate QModelIndex, QVariant, QDate
from calibre.gui2 import NONE, config, UNDEFINED_QDATE from calibre.gui2 import NONE, config, UNDEFINED_QDATE, FunctionDispatcher
from calibre.utils.pyparsing import ParseException from calibre.utils.pyparsing import ParseException
from calibre.ebooks.metadata import fmt_sidx, authors_to_string, string_to_authors from calibre.ebooks.metadata import fmt_sidx, authors_to_string, string_to_authors
from calibre.ptempfile import PersistentTemporaryFile from calibre.ptempfile import PersistentTemporaryFile
@ -151,7 +151,7 @@ class BooksModel(QAbstractTableModel): # {{{
self.database_changed.emit(db) self.database_changed.emit(db)
if self.cover_cache is not None: if self.cover_cache is not None:
self.cover_cache.stop() self.cover_cache.stop()
self.cover_cache = CoverCache(db) self.cover_cache = CoverCache(db, FunctionDispatcher(self.db.cover))
self.cover_cache.start() self.cover_cache.start()
def refresh_cover(event, ids): def refresh_cover(event, ids):
if event == 'cover' and self.cover_cache is not None: if event == 'cover' and self.cover_cache is not None:

View File

@ -6,7 +6,7 @@ __license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>' __copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
import re, itertools import re, itertools, time
from itertools import repeat from itertools import repeat
from datetime import timedelta from datetime import timedelta
from threading import Thread, RLock from threading import Thread, RLock
@ -23,10 +23,11 @@ from calibre import fit_image
class CoverCache(Thread): class CoverCache(Thread):
def __init__(self, db): def __init__(self, db, cover_func):
Thread.__init__(self) Thread.__init__(self)
self.daemon = True self.daemon = True
self.db = db self.db = db
self.cover_func = cover_func
self.load_queue = Queue() self.load_queue = Queue()
self.keep_running = True self.keep_running = True
self.cache = {} self.cache = {}
@ -37,7 +38,8 @@ class CoverCache(Thread):
self.keep_running = False self.keep_running = False
def _image_for_id(self, id_): def _image_for_id(self, id_):
img = self.db.cover(id_, index_is_id=True, as_image=True) time.sleep(0.050) # Limit 20/second to not overwhelm the GUI
img = self.cover_func(id_, index_is_id=True, as_image=True)
if img is None: if img is None:
img = QImage() img = QImage()
if not img.isNull(): if not img.isNull():

View File

@ -402,7 +402,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
path = path.lower() path = path.lower()
return path return path
def set_path(self, index, index_is_id=False, commit=True): def set_path(self, index, index_is_id=False):
''' '''
Set the path to the directory containing this books files based on its Set the path to the directory containing this books files based on its
current title and author. If there was a previous directory, its contents current title and author. If there was a previous directory, its contents
@ -432,7 +432,8 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
if current_path and os.path.exists(spath): # Migrate existing files if current_path and os.path.exists(spath): # Migrate existing files
cdata = self.cover(id, index_is_id=True) cdata = self.cover(id, index_is_id=True)
if cdata is not None: if cdata is not None:
open(os.path.join(tpath, 'cover.jpg'), 'wb').write(cdata) with open(os.path.join(tpath, 'cover.jpg'), 'wb') as f:
f.write(cdata)
for format in formats: for format in formats:
# Get data as string (can't use file as source and target files may be the same) # Get data as string (can't use file as source and target files may be the same)
f = self.format(id, format, index_is_id=True, as_file=False) f = self.format(id, format, index_is_id=True, as_file=False)
@ -442,8 +443,6 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
self.add_format(id, format, stream, index_is_id=True, self.add_format(id, format, stream, index_is_id=True,
path=tpath, notify=False) path=tpath, notify=False)
self.conn.execute('UPDATE books SET path=? WHERE id=?', (path, id)) self.conn.execute('UPDATE books SET path=? WHERE id=?', (path, id))
if commit:
self.conn.commit()
self.data.set(id, self.FIELD_MAP['path'], path, row_is_id=True) self.data.set(id, self.FIELD_MAP['path'], path, row_is_id=True)
# Delete not needed directories # Delete not needed directories
if current_path and os.path.exists(spath): if current_path and os.path.exists(spath):
@ -1131,7 +1130,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')]
@ -1163,11 +1165,15 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
','.join([a.replace(',', '|') for a in authors]), ','.join([a.replace(',', '|') for a in authors]),
row_is_id=True) row_is_id=True)
self.data.set(id, self.FIELD_MAP['author_sort'], ss, row_is_id=True) self.data.set(id, self.FIELD_MAP['author_sort'], ss, row_is_id=True)
self.set_path(id, index_is_id=True, commit=commit) self.set_path(id, index_is_id=True)
if notify: if notify:
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):
@ -1178,7 +1184,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
self.data.set(id, self.FIELD_MAP['sort'], title_sort(title), row_is_id=True) self.data.set(id, self.FIELD_MAP['sort'], title_sort(title), row_is_id=True)
else: else:
self.data.set(id, self.FIELD_MAP['sort'], title, row_is_id=True) self.data.set(id, self.FIELD_MAP['sort'], title, row_is_id=True)
self.set_path(id, index_is_id=True, commit=commit) self.set_path(id, index_is_id=True)
if commit: if commit:
self.conn.commit() self.conn.commit()
if notify: if notify:

View File

@ -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:

View File

@ -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(