mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-06-23 15:30:45 -04:00
Merge from trunk
This commit is contained in:
commit
7bf0c77dff
@ -11,11 +11,11 @@ http://www.libero-news.it/
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class LiberoNews(BasicNewsRecipe):
|
||||
__author__ = 'Marini Gabriele'
|
||||
description = 'Italian daily newspaper'
|
||||
__author__ = 'Marini Gabriele'
|
||||
description = 'Italian daily newspaper'
|
||||
|
||||
cover_url = 'http://www.ilgiornale.it/img_v1/logo.gif'
|
||||
title = u'Libero'
|
||||
cover_url = 'http://www.libero-news.it/images/logo.png'
|
||||
title = u'Libero '
|
||||
publisher = 'EDITORIALE LIBERO s.r.l 2006'
|
||||
category = 'News, politics, culture, economy, general interest'
|
||||
|
||||
|
@ -16,7 +16,7 @@ class DailyTelegraph(BasicNewsRecipe):
|
||||
language = 'en_AU'
|
||||
|
||||
oldest_article = 2
|
||||
max_articles_per_feed = 10
|
||||
max_articles_per_feed = 20
|
||||
remove_javascript = True
|
||||
no_stylesheets = True
|
||||
encoding = 'utf8'
|
||||
|
@ -30,7 +30,7 @@ class ANDROID(USBMS):
|
||||
0x18d1 : { 0x4e11 : [0x0100, 0x226], 0x4e12: [0x0100, 0x226]},
|
||||
|
||||
# Samsung
|
||||
0x04e8 : { 0x681d : [0x0222], 0x681c : [0x0222, 0x0224]},
|
||||
0x04e8 : { 0x681d : [0x0222, 0x0400], 0x681c : [0x0222, 0x0224]},
|
||||
|
||||
# Acer
|
||||
0x502 : { 0x3203 : [0x0100]},
|
||||
@ -41,10 +41,12 @@ class ANDROID(USBMS):
|
||||
'be used')
|
||||
EXTRA_CUSTOMIZATION_DEFAULT = ', '.join(EBOOK_DIR_MAIN)
|
||||
|
||||
VENDOR_NAME = ['HTC', 'MOTOROLA', 'GOOGLE_', 'ANDROID', 'ACER', 'GT-I5700']
|
||||
VENDOR_NAME = ['HTC', 'MOTOROLA', 'GOOGLE_', 'ANDROID', 'ACER',
|
||||
'GT-I5700', 'SAMSUNG']
|
||||
WINDOWS_MAIN_MEM = ['ANDROID_PHONE', 'A855', 'A853', 'INC.NEXUS_ONE',
|
||||
'__UMS_COMPOSITE', '_MB200', 'MASS_STORAGE', '_-_CARD']
|
||||
WINDOWS_CARD_A_MEM = ['ANDROID_PHONE']
|
||||
'__UMS_COMPOSITE', '_MB200', 'MASS_STORAGE', '_-_CARD',
|
||||
'PROD_GT-I9000']
|
||||
WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'PROD_GT-I9000_CARD']
|
||||
|
||||
OSX_MAIN_MEM = 'HTC Android Phone Media'
|
||||
|
||||
|
@ -76,7 +76,7 @@ class ITUNES(DevicePlugin):
|
||||
supported_platforms = ['osx','windows']
|
||||
author = 'GRiker'
|
||||
#: The version of this plugin as a 3-tuple (major, minor, revision)
|
||||
version = (0, 5, 0)
|
||||
version = (0,6,0)
|
||||
|
||||
OPEN_FEEDBACK_MESSAGE = _(
|
||||
'Apple device detected, launching iTunes, please wait ...')
|
||||
@ -280,7 +280,7 @@ class ITUNES(DevicePlugin):
|
||||
|
||||
if self.report_progress is not None:
|
||||
self.report_progress(i+1/book_count, _('%d of %d') % (i+1, book_count))
|
||||
self._purge_orphans(cached_books)
|
||||
self._purge_orphans(library_books, cached_books)
|
||||
|
||||
elif iswindows:
|
||||
try:
|
||||
@ -316,7 +316,7 @@ class ITUNES(DevicePlugin):
|
||||
if self.report_progress is not None:
|
||||
self.report_progress(i+1/book_count,
|
||||
_('%d of %d') % (i+1, book_count))
|
||||
self._purge_orphans(cached_books)
|
||||
self._purge_orphans(library_books, cached_books)
|
||||
|
||||
finally:
|
||||
pythoncom.CoUninitialize()
|
||||
@ -324,9 +324,9 @@ class ITUNES(DevicePlugin):
|
||||
if self.report_progress is not None:
|
||||
self.report_progress(1.0, _('finished'))
|
||||
self.cached_books = cached_books
|
||||
if DEBUG:
|
||||
self._dump_booklist(booklist, 'returning from books():')
|
||||
self._dump_cached_books('returning from books():')
|
||||
# if DEBUG:
|
||||
# self._dump_booklist(booklist, 'returning from books():')
|
||||
# self._dump_cached_books('returning from books():')
|
||||
return booklist
|
||||
else:
|
||||
return []
|
||||
@ -463,7 +463,7 @@ class ITUNES(DevicePlugin):
|
||||
else:
|
||||
# iTunes running, but not connected iPad
|
||||
if DEBUG:
|
||||
self.log.info(' self.ejected = True')
|
||||
self.log.info(' iDevice has been ejected')
|
||||
self.ejected = True
|
||||
return False
|
||||
|
||||
@ -782,121 +782,6 @@ class ITUNES(DevicePlugin):
|
||||
# self._dump_cached_books('upload_books()')
|
||||
self._dump_update_list('upload_books()')
|
||||
|
||||
'''
|
||||
if isosx:
|
||||
|
||||
for (i,file) in enumerate(files):
|
||||
path = self.path_template % (metadata[i].title, metadata[i].author[0])
|
||||
|
||||
if self.manual_sync_mode:
|
||||
# Delete existing from Device|Books, add to self.update_list
|
||||
# for deletion from booklist[0] during add_books_to_metadata
|
||||
if path in self.cached_books:
|
||||
self.update_list.append(self.cached_books[path])
|
||||
if DEBUG:
|
||||
self.log.info(" adding '%s' by %s to self.update_list" %
|
||||
(self.cached_books[path]['title'],self.cached_books[path]['author']))
|
||||
|
||||
if DEBUG:
|
||||
self.log.info( " deleting existing '%s'" % (path))
|
||||
self._remove_from_iTunes(self.cached_books[path])
|
||||
if self.manual_sync_mode:
|
||||
dev_book_added = self._remove_from_device(self.cached_books[path])
|
||||
|
||||
|
||||
# Add to iTunes Library|Books
|
||||
fpath = file
|
||||
if getattr(file, 'orig_file_path', None) is not None:
|
||||
fpath = file.orig_file_path
|
||||
elif getattr(file, 'name', None) is not None:
|
||||
fpath = file.name
|
||||
|
||||
if isinstance(file,PersistentTemporaryFile) and self.manual_sync_mode:
|
||||
if DEBUG:
|
||||
self.log.info(" PTF not added to Library|Books")
|
||||
else:
|
||||
added = self.iTunes.add(appscript.mactypes.File(fpath))
|
||||
if DEBUG:
|
||||
self.log.info(" file added to Library|Books")
|
||||
|
||||
dev_book_added = None
|
||||
if self.manual_sync_mode:
|
||||
dev_book_added = self._add_device_book(fpath)
|
||||
|
||||
thumb = None
|
||||
if metadata[i].cover:
|
||||
try:
|
||||
# Use cover data as artwork
|
||||
cover_data = open(metadata[i].cover,'rb')
|
||||
added.artworks[1].data_.set(cover_data.read())
|
||||
|
||||
# Resize for thumb
|
||||
width = metadata[i].thumbnail[0]
|
||||
height = metadata[i].thumbnail[1]
|
||||
im = PILImage.open(metadata[i].cover)
|
||||
im = im.resize((width, height), PILImage.ANTIALIAS)
|
||||
of = cStringIO.StringIO()
|
||||
im.convert('RGB').save(of, 'JPEG')
|
||||
thumb = of.getvalue()
|
||||
|
||||
# Refresh the thumbnail cache
|
||||
if DEBUG:
|
||||
self.log.info( " refreshing cached thumb for '%s'" % metadata[i].title)
|
||||
archive_path = os.path.join(self.cache_dir, "thumbs.zip")
|
||||
zfw = zipfile.ZipFile(archive_path, mode='a')
|
||||
thumb_path = path.rpartition('.')[0] + '.jpg'
|
||||
zfw.writestr(thumb_path, thumb)
|
||||
zfw.close()
|
||||
except:
|
||||
self.problem_titles.append("'%s' by %s" % (metadata[i].title, metadata[i].author[0]))
|
||||
self.log.error("ITUNES.upload_books(): error converting '%s' to thumb for '%s'" % (metadata[i].cover,metadata[i].title))
|
||||
|
||||
# Create a new Book
|
||||
this_book = Book(metadata[i].title, metadata[i].author[0])
|
||||
try:
|
||||
this_book.datetime = parse_date(str(added.date_added())).timetuple()
|
||||
except:
|
||||
pass
|
||||
this_book.db_id = None
|
||||
this_book.device_collections = []
|
||||
this_book.library_id = added
|
||||
this_book.path = path
|
||||
this_book.size = self._get_device_book_size(fpath, added.size())
|
||||
this_book.thumbnail = thumb
|
||||
this_book.iTunes_id = added
|
||||
|
||||
new_booklist.append(this_book)
|
||||
|
||||
# Populate the iTunes metadata
|
||||
if metadata[i].comments:
|
||||
added.comment.set(strip_tags.sub('',metadata[i].comments))
|
||||
added.description.set("added by calibre %s" % strftime('%Y-%m-%d %H:%M:%S'))
|
||||
added.enabled.set(True)
|
||||
if metadata[i].rating:
|
||||
added.rating.set(metadata[i].rating*10)
|
||||
added.sort_artist.set(metadata[i].author_sort.title())
|
||||
added.sort_name.set(this_book.title_sorter)
|
||||
|
||||
# Set genre from metadata
|
||||
# iTunes grabs the first dc:subject from the opf metadata,
|
||||
# But we can manually override with first tag starting with alpha
|
||||
for tag in metadata[i].tags:
|
||||
if self._is_alpha(tag[0]):
|
||||
added.genre.set(tag)
|
||||
break
|
||||
|
||||
# Add new_book to self.cached_paths
|
||||
self.cached_books[this_book.path] = {
|
||||
'title': this_book.title,
|
||||
'author': this_book.author,
|
||||
'lib_book': added,
|
||||
'dev_book': dev_book_added
|
||||
}
|
||||
|
||||
# Report progress
|
||||
if self.report_progress is not None:
|
||||
self.report_progress(i+1/file_count, _('%d of %d') % (i+1, file_count))
|
||||
'''
|
||||
if isosx:
|
||||
for (i,file) in enumerate(files):
|
||||
path = self.path_template % (metadata[i].title, metadata[i].author[0])
|
||||
@ -1378,6 +1263,15 @@ class ITUNES(DevicePlugin):
|
||||
self.log.info(" %s" % file.name)
|
||||
self.log.info()
|
||||
|
||||
def _dump_library_books(self, library_books):
|
||||
'''
|
||||
'''
|
||||
if DEBUG:
|
||||
self.log.info("\n library_books:")
|
||||
for book in library_books:
|
||||
self.log.info(" %s" % book)
|
||||
self.log.info()
|
||||
|
||||
def _dump_update_list(self,header=None):
|
||||
if header:
|
||||
msg = '\nself.update_list called from %s' % header
|
||||
@ -1590,7 +1484,7 @@ class ITUNES(DevicePlugin):
|
||||
self.log.info(" ignoring '%s' of type '%s'" % (book.name(), book.kind()))
|
||||
else:
|
||||
if DEBUG:
|
||||
self.log.info(" adding %-30.30s [%s]" % (book.name(), book.kind()))
|
||||
self.log.info(" adding %-30.30s %-30.30s [%s]" % (book.name(), book.artist(), book.kind()))
|
||||
device_books.append(book)
|
||||
|
||||
elif iswindows:
|
||||
@ -1619,7 +1513,7 @@ class ITUNES(DevicePlugin):
|
||||
self.log.info(" ignoring '%s' of type '%s'" % (book.Name, book.KindAsString))
|
||||
else:
|
||||
if DEBUG:
|
||||
self.log.info(" adding %-30.30s [%s]" % (book.Name, book.KindAsString))
|
||||
self.log.info(" adding %-30.30s %-30.30s [%s]" % (book.Name, book.Artist, book.KindAsString))
|
||||
device_books.append(book)
|
||||
|
||||
finally:
|
||||
@ -1716,11 +1610,11 @@ class ITUNES(DevicePlugin):
|
||||
if book.location() == appscript.k.missing_value:
|
||||
library_orphans[path] = book
|
||||
if DEBUG:
|
||||
self.log.info(" found calibre orphan '%s' in Library|Books" % book.name())
|
||||
self.log.info(" found iTunes PTF '%s' in Library|Books" % book.name())
|
||||
|
||||
library_books[path] = book
|
||||
if DEBUG:
|
||||
self.log.info(" adding %-30.30s [%s]" % (book.name(), book.kind()))
|
||||
self.log.info(" adding %-30.30s %-30.30s [%s]" % (book.name(), book.artist(), book.kind()))
|
||||
else:
|
||||
if DEBUG:
|
||||
self.log.info(' no Library playlists')
|
||||
@ -1730,9 +1624,6 @@ class ITUNES(DevicePlugin):
|
||||
|
||||
elif iswindows:
|
||||
lib = None
|
||||
# try:
|
||||
# pythoncom.CoInitialize()
|
||||
# self.iTunes = win32com.client.Dispatch("iTunes.Application")
|
||||
for source in self.iTunes.sources:
|
||||
if source.Kind == self.Sources.index('Library'):
|
||||
lib = source
|
||||
@ -1772,16 +1663,14 @@ class ITUNES(DevicePlugin):
|
||||
if not book.Location:
|
||||
library_orphans[path] = book
|
||||
if DEBUG:
|
||||
self.log.info(" found calibre orphan '%s' in Library|Books" % book.Name)
|
||||
self.log.info(" found iTunes PTF '%s' in Library|Books" % book.Name)
|
||||
|
||||
library_books[path] = book
|
||||
if DEBUG:
|
||||
self.log.info(" adding %-30.30s [%s]" % (book.Name, book.KindAsString))
|
||||
self.log.info(" adding %-30.30s %-30.30s [%s]" % (book.Name, book.Artist, book.KindAsString))
|
||||
except:
|
||||
if DEBUG:
|
||||
self.log.info(" no books in library")
|
||||
# finally:
|
||||
# pythoncom.CoUninitialize()
|
||||
self.library_orphans = library_orphans
|
||||
return library_books
|
||||
|
||||
@ -1905,44 +1794,36 @@ class ITUNES(DevicePlugin):
|
||||
self.version[0],self.version[1],self.version[2]))
|
||||
self.log.info(" iTunes_media: %s" % self.iTunes_media)
|
||||
|
||||
def _purge_orphans(self,cached_books):
|
||||
def _purge_orphans(self,library_books, cached_books):
|
||||
'''
|
||||
Scan self.library_orphans for any paths not on device
|
||||
Remove any true orphans from iTunes
|
||||
This occurs when recipes are uploaded in a previous session
|
||||
and the book has since been deleted on the device
|
||||
Scan library_books for any paths not on device
|
||||
Remove any iTunes orphans originally added by calibre
|
||||
This occurs when the user deletes a book in iBooks while disconnected
|
||||
'''
|
||||
if DEBUG:
|
||||
self.log.info(" ITUNES._purge_orphans")
|
||||
self.log.info("\n ITUNES._purge_orphans")
|
||||
#self._dump_library_books(library_books)
|
||||
#self.log.info(" cached_books:\n %s" % "\n ".join(cached_books.keys()))
|
||||
|
||||
orphan_paths = {}
|
||||
|
||||
if isosx:
|
||||
for orphan in self.library_orphans:
|
||||
path = self.path_template % (self.library_orphans[orphan].name(),
|
||||
self.library_orphans[orphan].artist())
|
||||
orphan_paths[path] = self.library_orphans[orphan]
|
||||
|
||||
# Scan orphan_paths for paths not found in cached_books
|
||||
for orphan in orphan_paths.keys():
|
||||
if orphan not in cached_books:
|
||||
for book in library_books:
|
||||
if isosx:
|
||||
if book not in cached_books and \
|
||||
str(library_books[book].description()).startswith(self.description_prefix):
|
||||
if DEBUG:
|
||||
self.log.info(" '%s' not found on device, removing from iTunes" % orphan)
|
||||
self.iTunes.delete(orphan_paths[orphan])
|
||||
|
||||
elif iswindows:
|
||||
for orphan in self.library_orphans:
|
||||
path = self.path_template % (self.library_orphans[orphan].Name,
|
||||
self.library_orphans[orphan].Artist)
|
||||
orphan_paths[path] = self.library_orphans[orphan]
|
||||
|
||||
# Scan orphan_paths for paths not found in cached_books
|
||||
for orphan in orphan_paths.keys():
|
||||
if orphan not in cached_books:
|
||||
self.log.info(" '%s' not found on iDevice, removing from iTunes" % book)
|
||||
btr = { 'title':library_books[book].name(),
|
||||
'author':library_books[book].artist(),
|
||||
'lib_book':library_books[book]}
|
||||
self._remove_from_iTunes(btr)
|
||||
elif iswindows:
|
||||
if book not in cached_books and \
|
||||
library_books[book].Description.startswith(self.description_prefix):
|
||||
if DEBUG:
|
||||
self.log.info(" '%s' not found on device, removing from iTunes" % orphan)
|
||||
orphan_paths[orphan].Delete()
|
||||
self.log.info(" '%s' not found on iDevice, removing from iTunes" % book)
|
||||
btr = { 'title':library_books[book].Name,
|
||||
'author':library_books[book].Artist,
|
||||
'lib_book':library_books[book]}
|
||||
self._remove_from_iTunes(btr)
|
||||
|
||||
def _remove_existing_copies(self,path,file,metadata):
|
||||
'''
|
||||
@ -2040,7 +1921,7 @@ class ITUNES(DevicePlugin):
|
||||
|
||||
except:
|
||||
# We get here if there was an error with .location().path
|
||||
self.log.info(" removing orphan '%s' from iTunes" % cached_book['title'])
|
||||
self.log.info(" removing orphan '%s' from iTunes" % cached_book['title'])
|
||||
|
||||
self.iTunes.delete(cached_book['lib_book'])
|
||||
|
||||
@ -2049,33 +1930,33 @@ class ITUNES(DevicePlugin):
|
||||
Assume we're wrapped in a pythoncom
|
||||
Windows stores the book under a common author directory, so we just delete the .epub
|
||||
'''
|
||||
|
||||
book = self._find_library_book(cached_book)
|
||||
if book:
|
||||
try:
|
||||
book = cached_book['lib_book']
|
||||
path = book.Location
|
||||
except:
|
||||
book = self._find_library_book(cached_book)
|
||||
path = book.Location
|
||||
storage_path = os.path.split(book.Location)
|
||||
if book.Location.startswith(self.iTunes_media):
|
||||
if DEBUG:
|
||||
self.log.info(" removing '%s' at %s" %
|
||||
(cached_book['title'], path))
|
||||
try:
|
||||
os.remove(path)
|
||||
except:
|
||||
self.log.warning(" could not find '%s' in iTunes storage" % path)
|
||||
try:
|
||||
os.rmdir(storage_path[0])
|
||||
self.log.info(" removed folder '%s'" % storage_path[0])
|
||||
except:
|
||||
self.log.info(" folder '%s' not found or not empty" % storage_path[0])
|
||||
|
||||
# Delete from iTunes database
|
||||
else:
|
||||
self.log.info(" '%s' stored external to iTunes, no files deleted" % cached_book['title'])
|
||||
|
||||
book.Delete()
|
||||
storage_path = os.path.split(book.Location)
|
||||
if book.Location.startswith(self.iTunes_media):
|
||||
if DEBUG:
|
||||
self.log.info(" removing '%s' at %s" %
|
||||
(cached_book['title'], path))
|
||||
try:
|
||||
os.remove(path)
|
||||
except:
|
||||
self.log.warning(" could not find '%s' in iTunes storage" % path)
|
||||
try:
|
||||
os.rmdir(storage_path[0])
|
||||
self.log.info(" removed folder '%s'" % storage_path[0])
|
||||
except:
|
||||
self.log.info(" folder '%s' not found or not empty" % storage_path[0])
|
||||
|
||||
# Delete from iTunes database
|
||||
else:
|
||||
self.log.warning(" could not find '%s' in iTunes database" % cached_book['title'])
|
||||
self.log.info(" '%s' stored external to iTunes, no files deleted" % cached_book['title'])
|
||||
|
||||
book.Delete()
|
||||
|
||||
def _update_device(self, msg='', wait=True):
|
||||
'''
|
||||
|
@ -103,8 +103,8 @@ class CoverManager(object):
|
||||
32)]
|
||||
img_data = create_cover_page(lines, I('library.png'))
|
||||
id, href = self.oeb.manifest.generate('cover_image',
|
||||
'cover_image.png')
|
||||
item = self.oeb.manifest.add(id, href, guess_type('t.png')[0],
|
||||
'cover_image.jpg')
|
||||
item = self.oeb.manifest.add(id, href, guess_type('t.jpg')[0],
|
||||
data=img_data)
|
||||
m.clear('cover')
|
||||
m.add('cover', item.id)
|
||||
|
1200
src/calibre/gui2/actions.py
Normal file
1200
src/calibre/gui2/actions.py
Normal file
File diff suppressed because it is too large
Load Diff
@ -83,7 +83,6 @@ if pictureflow is not None:
|
||||
self.setFocusPolicy(Qt.WheelFocus)
|
||||
self.setSizePolicy(QSizePolicy(QSizePolicy.Expanding,
|
||||
QSizePolicy.Expanding))
|
||||
self.setZoomFactor(150)
|
||||
|
||||
def sizeHint(self):
|
||||
return self.minimumSize()
|
||||
|
@ -3,24 +3,26 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
# Imports {{{
|
||||
import os, traceback, Queue, time, socket, cStringIO, re
|
||||
import os, traceback, Queue, time, socket, cStringIO, re, sys
|
||||
from threading import Thread, RLock
|
||||
from itertools import repeat
|
||||
from functools import partial
|
||||
from binascii import unhexlify
|
||||
|
||||
from PyQt4.Qt import QMenu, QAction, QActionGroup, QIcon, SIGNAL, QPixmap, \
|
||||
Qt, pyqtSignal
|
||||
Qt, pyqtSignal, QColor, QPainter
|
||||
from PyQt4.QtSvg import QSvgRenderer
|
||||
|
||||
from calibre.customize.ui import available_input_formats, available_output_formats, \
|
||||
device_plugins
|
||||
from calibre.devices.interface import DevicePlugin
|
||||
from calibre.devices.errors import UserFeedback
|
||||
from calibre.gui2.dialogs.choose_format import ChooseFormatDialog
|
||||
from calibre.utils.ipc.job import BaseJob
|
||||
from calibre.devices.scanner import DeviceScanner
|
||||
from calibre.gui2 import config, error_dialog, Dispatcher, dynamic, \
|
||||
pixmap_to_data, warning_dialog, \
|
||||
question_dialog
|
||||
pixmap_to_data, warning_dialog, \
|
||||
question_dialog, info_dialog, choose_dir
|
||||
from calibre.ebooks.metadata import authors_to_string, authors_to_sort_string
|
||||
from calibre import preferred_encoding, prints
|
||||
from calibre.utils.filenames import ascii_filename
|
||||
@ -597,10 +599,204 @@ class Emailer(Thread): # {{{
|
||||
|
||||
# }}}
|
||||
|
||||
class DeviceMixin(object):
|
||||
class DeviceMixin(object): # {{{
|
||||
|
||||
def __init__(self):
|
||||
self.db_book_uuid_cache = set()
|
||||
self.device_error_dialog = error_dialog(self, _('Error'),
|
||||
_('Error communicating with device'), ' ')
|
||||
self.device_error_dialog.setModal(Qt.NonModal)
|
||||
self.device_connected = None
|
||||
self.emailer = Emailer()
|
||||
self.emailer.start()
|
||||
self.device_manager = DeviceManager(Dispatcher(self.device_detected),
|
||||
self.job_manager, Dispatcher(self.status_bar.show_message))
|
||||
self.device_manager.start()
|
||||
|
||||
def set_default_thumbnail(self, height):
|
||||
r = QSvgRenderer(I('book.svg'))
|
||||
pixmap = QPixmap(height, height)
|
||||
pixmap.fill(QColor(255,255,255))
|
||||
p = QPainter(pixmap)
|
||||
r.render(p)
|
||||
p.end()
|
||||
self.default_thumbnail = (pixmap.width(), pixmap.height(),
|
||||
pixmap_to_data(pixmap))
|
||||
|
||||
def connect_to_folder(self):
|
||||
dir = choose_dir(self, 'Select Device Folder',
|
||||
_('Select folder to open as device'))
|
||||
if dir is not None:
|
||||
self.device_manager.connect_to_folder(dir)
|
||||
|
||||
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._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)
|
||||
if self.device_connected:
|
||||
self._sync_menu.connect_to_folder_action.setEnabled(False)
|
||||
if self.device_connected == 'folder':
|
||||
self._sync_menu.disconnect_from_folder_action.setEnabled(True)
|
||||
else:
|
||||
self._sync_menu.disconnect_from_folder_action.setEnabled(False)
|
||||
else:
|
||||
self._sync_menu.connect_to_folder_action.setEnabled(True)
|
||||
self._sync_menu.disconnect_from_folder_action.setEnabled(False)
|
||||
|
||||
|
||||
|
||||
def device_job_exception(self, job):
|
||||
'''
|
||||
Handle exceptions in threaded device jobs.
|
||||
'''
|
||||
if isinstance(getattr(job, 'exception', None), UserFeedback):
|
||||
ex = job.exception
|
||||
func = {UserFeedback.ERROR:error_dialog,
|
||||
UserFeedback.WARNING:warning_dialog,
|
||||
UserFeedback.INFO:info_dialog}[ex.level]
|
||||
return func(self, _('Failed'), ex.msg, det_msg=ex.details if
|
||||
ex.details else '', show=True)
|
||||
|
||||
try:
|
||||
if 'Could not read 32 bytes on the control bus.' in \
|
||||
unicode(job.details):
|
||||
error_dialog(self, _('Error talking to device'),
|
||||
_('There was a temporary error talking to the '
|
||||
'device. Please unplug and reconnect the device '
|
||||
'and or reboot.')).show()
|
||||
return
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
prints(job.details, file=sys.stderr)
|
||||
except:
|
||||
pass
|
||||
if not self.device_error_dialog.isVisible():
|
||||
self.device_error_dialog.setDetailedText(job.details)
|
||||
self.device_error_dialog.show()
|
||||
|
||||
# Device connected {{{
|
||||
def device_detected(self, connected, is_folder_device):
|
||||
'''
|
||||
Called when a device is connected to the computer.
|
||||
'''
|
||||
if connected:
|
||||
self._sync_menu.connect_to_folder_action.setEnabled(False)
|
||||
if is_folder_device:
|
||||
self._sync_menu.disconnect_from_folder_action.setEnabled(True)
|
||||
self.device_manager.get_device_information(\
|
||||
Dispatcher(self.info_read))
|
||||
self.set_default_thumbnail(\
|
||||
self.device_manager.device.THUMBNAIL_HEIGHT)
|
||||
self.status_bar.show_message(_('Device: ')+\
|
||||
self.device_manager.device.__class__.get_gui_name()+\
|
||||
_(' detected.'), 3000)
|
||||
self.device_connected = 'device' if not is_folder_device else 'folder'
|
||||
self._sync_menu.enable_device_actions(True,
|
||||
self.device_manager.device.card_prefix(),
|
||||
self.device_manager.device)
|
||||
self.location_view.model().device_connected(self.device_manager.device)
|
||||
self.eject_action.setEnabled(True)
|
||||
self.refresh_ondevice_info (device_connected = True, reset_only = True)
|
||||
else:
|
||||
self._sync_menu.connect_to_folder_action.setEnabled(True)
|
||||
self._sync_menu.disconnect_from_folder_action.setEnabled(False)
|
||||
self.device_connected = None
|
||||
self._sync_menu.enable_device_actions(False)
|
||||
self.location_view.model().update_devices()
|
||||
self.vanity.setText(self.vanity_template%\
|
||||
dict(version=self.latest_version, device=' '))
|
||||
self.device_info = ' '
|
||||
if self.current_view() != self.library_view:
|
||||
self.book_details.reset_info()
|
||||
self.location_view.setCurrentIndex(self.location_view.model().index(0))
|
||||
self.eject_action.setEnabled(False)
|
||||
self.refresh_ondevice_info (device_connected = False)
|
||||
|
||||
def info_read(self, job):
|
||||
'''
|
||||
Called once device information has been read.
|
||||
'''
|
||||
if job.failed:
|
||||
return self.device_job_exception(job)
|
||||
info, cp, fs = job.result
|
||||
self.location_view.model().update_devices(cp, fs)
|
||||
self.device_info = _('Connected ')+info[0]
|
||||
self.vanity.setText(self.vanity_template%\
|
||||
dict(version=self.latest_version, device=self.device_info))
|
||||
|
||||
self.device_manager.books(Dispatcher(self.metadata_downloaded))
|
||||
|
||||
def metadata_downloaded(self, job):
|
||||
'''
|
||||
Called once metadata has been read for all books on the device.
|
||||
'''
|
||||
if job.failed:
|
||||
self.device_job_exception(job)
|
||||
return
|
||||
self.set_books_in_library(job.result, reset=True)
|
||||
mainlist, cardalist, cardblist = job.result
|
||||
self.memory_view.set_database(mainlist)
|
||||
self.memory_view.set_editable(self.device_manager.device.CAN_SET_METADATA)
|
||||
self.card_a_view.set_database(cardalist)
|
||||
self.card_a_view.set_editable(self.device_manager.device.CAN_SET_METADATA)
|
||||
self.card_b_view.set_database(cardblist)
|
||||
self.card_b_view.set_editable(self.device_manager.device.CAN_SET_METADATA)
|
||||
self.sync_news()
|
||||
self.sync_catalogs()
|
||||
self.refresh_ondevice_info(device_connected = True)
|
||||
|
||||
def refresh_ondevice_info(self, device_connected, reset_only = False):
|
||||
'''
|
||||
Force the library view to refresh, taking into consideration
|
||||
books information
|
||||
'''
|
||||
self.book_on_device(None, reset=True)
|
||||
if reset_only:
|
||||
return
|
||||
self.library_view.set_device_connected(device_connected)
|
||||
|
||||
# }}}
|
||||
|
||||
def remove_paths(self, paths):
|
||||
return self.device_manager.delete_books(
|
||||
Dispatcher(self.books_deleted), paths)
|
||||
|
||||
def books_deleted(self, job):
|
||||
'''
|
||||
Called once deletion is done on the device
|
||||
'''
|
||||
for view in (self.memory_view, self.card_a_view, self.card_b_view):
|
||||
view.model().deletion_done(job, job.failed)
|
||||
if job.failed:
|
||||
self.device_job_exception(job)
|
||||
return
|
||||
|
||||
if self.delete_memory.has_key(job):
|
||||
paths, model = self.delete_memory.pop(job)
|
||||
self.device_manager.remove_books_from_metadata(paths,
|
||||
self.booklists())
|
||||
model.paths_deleted(paths)
|
||||
self.upload_booklists()
|
||||
# Clear the ondevice info so it will be recomputed
|
||||
self.book_on_device(None, None, reset=True)
|
||||
# We want to reset all the ondevice flags in the library. Use a big
|
||||
# hammer, so we don't need to worry about whether some succeeded or not
|
||||
self.library_view.model().refresh()
|
||||
|
||||
|
||||
def dispatch_sync_event(self, dest, delete, specific):
|
||||
rows = self.library_view.selectionModel().selectedRows()
|
||||
@ -1220,3 +1416,6 @@ class DeviceMixin(object):
|
||||
# Correct the metadata cache on device.
|
||||
if self.device_manager.is_device_connected:
|
||||
self.device_manager.sync_booklists(None, booklists)
|
||||
|
||||
# }}}
|
||||
|
||||
|
@ -47,7 +47,7 @@ class ToolbarMixin(object): # {{{
|
||||
def __init__(self):
|
||||
md = QMenu()
|
||||
md.addAction(_('Edit metadata individually'),
|
||||
partial(self.edit_metadata, False))
|
||||
partial(self.edit_metadata, False, bulk=False))
|
||||
md.addSeparator()
|
||||
md.addAction(_('Edit metadata in bulk'),
|
||||
partial(self.edit_metadata, False, bulk=True))
|
||||
|
@ -85,7 +85,9 @@ typedef long PFreal;
|
||||
|
||||
typedef unsigned short QRgb565;
|
||||
|
||||
#define FONT_SIZE 18
|
||||
#define REFLECTION_FACTOR 1.5
|
||||
|
||||
#define MAX(x, y) ((x > y) ? x : y)
|
||||
|
||||
#define RGB565_RED_MASK 0xF800
|
||||
#define RGB565_GREEN_MASK 0x07E0
|
||||
@ -124,6 +126,7 @@ inline PFreal floatToFixed(float val)
|
||||
return (PFreal)(val*PFREAL_ONE);
|
||||
}
|
||||
|
||||
// sinTable {{{
|
||||
#define IANGLE_MAX 1024
|
||||
#define IANGLE_MASK 1023
|
||||
|
||||
@ -293,6 +296,7 @@ int main(int, char**)
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
// }}}
|
||||
|
||||
inline PFreal fsin(int iangle)
|
||||
{
|
||||
@ -315,6 +319,8 @@ struct SlideInfo
|
||||
PFreal cy;
|
||||
};
|
||||
|
||||
// PicturePlowPrivate {{{
|
||||
|
||||
class PictureFlowPrivate
|
||||
{
|
||||
public:
|
||||
@ -369,6 +375,7 @@ private:
|
||||
|
||||
int slideWidth;
|
||||
int slideHeight;
|
||||
int fontSize;
|
||||
int zoom;
|
||||
int queueLength;
|
||||
|
||||
@ -406,6 +413,7 @@ PictureFlowPrivate::PictureFlowPrivate(PictureFlow* w, int queueLength_)
|
||||
|
||||
slideWidth = 200;
|
||||
slideHeight = 200;
|
||||
fontSize = 10;
|
||||
zoom = 100;
|
||||
|
||||
centerIndex = 0;
|
||||
@ -542,8 +550,11 @@ void PictureFlowPrivate::showSlide(int index)
|
||||
|
||||
void PictureFlowPrivate::resize(int w, int h)
|
||||
{
|
||||
slideHeight = int(float(h)/2.);
|
||||
if (w < 10) w = 10;
|
||||
if (h < 10) h = 10;
|
||||
slideHeight = int(float(h)/REFLECTION_FACTOR);
|
||||
slideWidth = int(float(slideHeight) * 2/3.);
|
||||
fontSize = MAX(int(h/20.), 12);
|
||||
recalc(w, h);
|
||||
resetSlides();
|
||||
triggerRender();
|
||||
@ -592,8 +603,8 @@ static QImage prepareSurface(QImage img, int w, int h)
|
||||
img = img.scaled(w, h, Qt::IgnoreAspectRatio, mode);
|
||||
|
||||
// slightly larger, to accomodate for the reflection
|
||||
int hs = h * 2;
|
||||
int hofs = h / 3;
|
||||
int hs = int(h * REFLECTION_FACTOR);
|
||||
int hofs = 0;
|
||||
|
||||
// offscreen buffer: black is sweet
|
||||
QImage result(hs, w, QImage::Format_RGB16);
|
||||
@ -715,13 +726,13 @@ void PictureFlowPrivate::render()
|
||||
|
||||
QFont font = QFont();
|
||||
font.setBold(true);
|
||||
font.setPointSize(FONT_SIZE);
|
||||
font.setPixelSize(fontSize);
|
||||
painter.setFont(font);
|
||||
painter.setPen(Qt::white);
|
||||
//painter.setPen(QColor(255,255,255,127));
|
||||
|
||||
if (centerIndex < slideCount() && centerIndex > -1)
|
||||
painter.drawText( QRect(0,0, buffer.width(), buffer.height()*2-FONT_SIZE*3),
|
||||
painter.drawText( QRect(0,0, buffer.width(), buffer.height()*2-fontSize*3),
|
||||
Qt::AlignCenter, slideImages->caption(centerIndex));
|
||||
|
||||
painter.end();
|
||||
@ -766,7 +777,7 @@ void PictureFlowPrivate::render()
|
||||
|
||||
QFont font = QFont();
|
||||
font.setBold(true);
|
||||
font.setPointSize(FONT_SIZE);
|
||||
font.setPixelSize(fontSize);
|
||||
painter.setFont(font);
|
||||
|
||||
int leftTextIndex = (step>0) ? centerIndex : centerIndex-1;
|
||||
@ -774,12 +785,12 @@ void PictureFlowPrivate::render()
|
||||
|
||||
painter.setPen(QColor(255,255,255, (255-fade) ));
|
||||
if (leftTextIndex < sc && leftTextIndex > -1)
|
||||
painter.drawText( QRect(0,0, buffer.width(), buffer.height()*2 - FONT_SIZE*3),
|
||||
painter.drawText( QRect(0,0, buffer.width(), buffer.height()*2 - fontSize*3),
|
||||
Qt::AlignCenter, slideImages->caption(leftTextIndex));
|
||||
|
||||
painter.setPen(QColor(255,255,255, fade));
|
||||
if (leftTextIndex+1 < sc && leftTextIndex > -2)
|
||||
painter.drawText( QRect(0,0, buffer.width(), buffer.height()*2 - FONT_SIZE*3),
|
||||
painter.drawText( QRect(0,0, buffer.width(), buffer.height()*2 - fontSize*3),
|
||||
Qt::AlignCenter, slideImages->caption(leftTextIndex+1));
|
||||
|
||||
|
||||
@ -893,7 +904,7 @@ int col1, int col2)
|
||||
int center = (sh*BILINEAR_STRETCH_VER/2);
|
||||
int dy = dist*BILINEAR_STRETCH_VER / h;
|
||||
#else
|
||||
int center = (sh/2);
|
||||
int center = sh/2;
|
||||
int dy = dist / h;
|
||||
#endif
|
||||
int p1 = center*PFREAL_ONE - dy/2;
|
||||
@ -1110,8 +1121,9 @@ void PictureFlowPrivate::clearSurfaceCache()
|
||||
surfaceCache.clear();
|
||||
}
|
||||
|
||||
// -----------------------------------------
|
||||
// }}}
|
||||
|
||||
// PictureFlow {{{
|
||||
PictureFlow::PictureFlow(QWidget* parent, int queueLength): QWidget(parent)
|
||||
{
|
||||
d = new PictureFlowPrivate(this, queueLength);
|
||||
@ -1387,3 +1399,5 @@ void PictureFlow::emitcurrentChanged(int index) { emit currentChanged(index); }
|
||||
int FlowImages::count() { return 0; }
|
||||
QImage FlowImages::image(int index) { index=0; return QImage(); }
|
||||
QString FlowImages::caption(int index) {index=0; return QString(); }
|
||||
|
||||
// }}}
|
||||
|
@ -2,9 +2,10 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
import os, collections
|
||||
|
||||
from PyQt4.QtGui import QStatusBar, QLabel, QWidget, QHBoxLayout, QPixmap, \
|
||||
QSizePolicy, QScrollArea
|
||||
from PyQt4.QtCore import Qt, QSize, pyqtSignal
|
||||
from PyQt4.Qt import QStatusBar, QLabel, QWidget, QHBoxLayout, QPixmap, \
|
||||
QSizePolicy, QScrollArea, Qt, QSize, pyqtSignal, \
|
||||
QPropertyAnimation, QEasingCurve
|
||||
|
||||
|
||||
from calibre import fit_image, preferred_encoding, isosx
|
||||
from calibre.gui2 import config
|
||||
@ -50,6 +51,10 @@ class BookInfoDisplay(QWidget):
|
||||
|
||||
def __init__(self, coverpath=I('book.svg')):
|
||||
QLabel.__init__(self)
|
||||
self.animation = QPropertyAnimation(self, 'size', self)
|
||||
self.animation.setEasingCurve(QEasingCurve(QEasingCurve.OutExpo))
|
||||
self.animation.setDuration(1000)
|
||||
self.animation.setStartValue(QSize(0, 0))
|
||||
self.setMaximumWidth(81)
|
||||
self.setMaximumHeight(108)
|
||||
self.default_pixmap = QPixmap(coverpath)
|
||||
@ -58,6 +63,7 @@ class BookInfoDisplay(QWidget):
|
||||
self.setPixmap(self.default_pixmap)
|
||||
|
||||
def do_layout(self):
|
||||
self.animation.stop()
|
||||
pixmap = self.pixmap()
|
||||
pwidth, pheight = pixmap.width(), pixmap.height()
|
||||
width, height = fit_image(pwidth, pheight,
|
||||
@ -68,11 +74,12 @@ class BookInfoDisplay(QWidget):
|
||||
except ZeroDivisionError:
|
||||
aspect_ratio = 1
|
||||
self.setMaximumWidth(int(aspect_ratio*self.maximumHeight()))
|
||||
self.animation.setEndValue(self.maximumSize())
|
||||
|
||||
def setPixmap(self, pixmap):
|
||||
QLabel.setPixmap(self, pixmap)
|
||||
self.do_layout()
|
||||
|
||||
self.animation.start()
|
||||
|
||||
def sizeHint(self):
|
||||
return QSize(self.maximumWidth(), self.maximumHeight())
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -3,12 +3,13 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
import traceback
|
||||
|
||||
from PyQt4.QtCore import QThread, pyqtSignal
|
||||
from PyQt4.Qt import QThread, pyqtSignal, QDesktopServices, QUrl, Qt
|
||||
import mechanize
|
||||
|
||||
from calibre.constants import __version__, iswindows, isosx
|
||||
from calibre.constants import __appname__, __version__, iswindows, isosx
|
||||
from calibre import browser
|
||||
from calibre.utils.config import prefs
|
||||
from calibre.gui2 import config, dynamic, question_dialog
|
||||
|
||||
URL = 'http://status.calibre-ebook.com/latest'
|
||||
|
||||
@ -36,3 +37,35 @@ class CheckForUpdates(QThread):
|
||||
traceback.print_exc()
|
||||
self.sleep(self.INTERVAL)
|
||||
|
||||
class UpdateMixin(object):
|
||||
|
||||
def __init__(self, opts):
|
||||
if not opts.no_update_check:
|
||||
self.update_checker = CheckForUpdates(self)
|
||||
self.update_checker.update_found.connect(self.update_found,
|
||||
type=Qt.QueuedConnection)
|
||||
self.update_checker.start()
|
||||
|
||||
def update_found(self, version):
|
||||
os = 'windows' if iswindows else 'osx' if isosx else 'linux'
|
||||
url = 'http://calibre-ebook.com/download_%s'%os
|
||||
self.latest_version = '<br>' + _('<span style="color:red; font-weight:bold">'
|
||||
'Latest version: <a href="%s">%s</a></span>')%(url, version)
|
||||
self.vanity.setText(self.vanity_template%\
|
||||
(dict(version=self.latest_version,
|
||||
device=self.device_info)))
|
||||
self.vanity.update()
|
||||
if config.get('new_version_notification') and \
|
||||
dynamic.get('update to version %s'%version, True):
|
||||
if question_dialog(self, _('Update available'),
|
||||
_('%s has been updated to version %s. '
|
||||
'See the <a href="http://calibre-ebook.com/whats-new'
|
||||
'">new features</a>. Visit the download pa'
|
||||
'ge?')%(__appname__, version)):
|
||||
url = 'http://calibre-ebook.com/download_'+\
|
||||
('windows' if iswindows else 'osx' if isosx else 'linux')
|
||||
QDesktopServices.openUrl(QUrl(url))
|
||||
dynamic.set('update to version %s'%version, False)
|
||||
|
||||
|
||||
|
||||
|
@ -10,14 +10,14 @@ import collections, glob, os, re, itertools, functools
|
||||
from itertools import repeat
|
||||
from datetime import timedelta
|
||||
|
||||
from PyQt4.QtCore import QThread, QReadWriteLock
|
||||
from PyQt4.QtGui import QImage
|
||||
from PyQt4.Qt import QThread, QReadWriteLock, QImage, Qt
|
||||
|
||||
from calibre.utils.config import tweaks
|
||||
from calibre.utils.date import parse_date, now, UNDEFINED_DATE
|
||||
from calibre.utils.search_query_parser import SearchQueryParser
|
||||
from calibre.utils.pyparsing import ParseException
|
||||
from calibre.ebooks.metadata import title_sort
|
||||
from calibre import fit_image
|
||||
|
||||
class CoverCache(QThread):
|
||||
|
||||
@ -96,6 +96,11 @@ class CoverCache(QThread):
|
||||
img.loadFromData(data)
|
||||
if img.isNull():
|
||||
continue
|
||||
scaled, nwidth, nheight = fit_image(img.width(),
|
||||
img.height(), 600, 800)
|
||||
if scaled:
|
||||
img = img.scaled(nwidth, nheight, Qt.KeepAspectRatio,
|
||||
Qt.SmoothTransformation)
|
||||
except:
|
||||
continue
|
||||
self.cache_lock.lockForWrite()
|
||||
|
@ -111,7 +111,7 @@ Pre/post processing of downloaded HTML
|
||||
|
||||
.. automember:: BasicNewsRecipe.remove_javascript
|
||||
|
||||
.. automethod:: BasicNewsRecipe.prepreprocess_html
|
||||
.. automethod:: BasicNewsRecipe.skip_ad_pages
|
||||
|
||||
.. automethod:: BasicNewsRecipe.preprocess_html
|
||||
|
||||
|
@ -175,7 +175,7 @@ def add_borders_to_image(path_to_image, left=0, top=0, right=0, bottom=0,
|
||||
p.DestroyMagickWand(canvas)
|
||||
|
||||
def create_cover_page(top_lines, logo_path, width=590, height=750,
|
||||
bgcolor='white', output_format='png'):
|
||||
bgcolor='white', output_format='jpg'):
|
||||
ans = None
|
||||
with p.ImageMagick():
|
||||
canvas = create_canvas(width, height, bgcolor)
|
||||
|
@ -413,18 +413,19 @@ class BasicNewsRecipe(Recipe):
|
||||
return url
|
||||
return article.get('link', None)
|
||||
|
||||
def prepreprocess_html(self, soup):
|
||||
def skip_ad_pages(self, soup):
|
||||
'''
|
||||
This method is called with the source of each downloaded :term:`HTML` file, before
|
||||
any of the cleanup attributes like remove_tags, keep_only_tags are
|
||||
applied. Note that preprocess_regexps will have already been applied.
|
||||
It can be used to do arbitrarily powerful pre-processing on the :term:`HTML`.
|
||||
It should return `soup` after processing it.
|
||||
It is meant to allow the recipe to skip ad pages. If the soup represents
|
||||
an ad page, return the HTML of the real page. Otherwise return
|
||||
None.
|
||||
|
||||
`soup`: A `BeautifulSoup <http://www.crummy.com/software/BeautifulSoup/documentation.html>`_
|
||||
instance containing the downloaded :term:`HTML`.
|
||||
'''
|
||||
return soup
|
||||
return None
|
||||
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
@ -628,7 +629,7 @@ class BasicNewsRecipe(Recipe):
|
||||
|
||||
self.web2disk_options = web2disk_option_parser().parse_args(web2disk_cmdline)[0]
|
||||
for extra in ('keep_only_tags', 'remove_tags', 'preprocess_regexps',
|
||||
'prepreprocess_html', 'preprocess_html', 'remove_tags_after',
|
||||
'skip_ad_pages', 'preprocess_html', 'remove_tags_after',
|
||||
'remove_tags_before', 'is_link_wanted'):
|
||||
setattr(self.web2disk_options, extra, getattr(self, extra))
|
||||
self.web2disk_options.postprocess_html = self._postprocess_html
|
||||
@ -801,11 +802,6 @@ class BasicNewsRecipe(Recipe):
|
||||
.calibre_navbar {
|
||||
font-family:monospace;
|
||||
}
|
||||
hr {
|
||||
border-color:gray;
|
||||
border-style:solid;
|
||||
border-width:thin;
|
||||
}
|
||||
|
||||
'''
|
||||
|
||||
|
@ -108,7 +108,7 @@ class TouchscreenNavBarTemplate(Template):
|
||||
navbar = DIV(CLASS('calibre_navbar', 'calibre_rescale_100',
|
||||
style='text-align:'+align))
|
||||
if bottom:
|
||||
navbar.append(HR())
|
||||
navbar.append(DIV(style="border-top:1px solid gray;border-bottom:1em solid white"))
|
||||
text = 'This article was downloaded by '
|
||||
p = PT(text, STRONG(__appname__), A(url, href=url), style='text-align:left')
|
||||
p[0].tail = ' from '
|
||||
@ -136,7 +136,7 @@ class TouchscreenNavBarTemplate(Template):
|
||||
|
||||
navbar.iterchildren(reversed=True).next().tail = ' | '
|
||||
if not bottom:
|
||||
navbar.append(HR())
|
||||
navbar.append(DIV(style="border-top:1px solid gray;border-bottom:1em solid white"))
|
||||
|
||||
self.root = HTML(head, BODY(navbar))
|
||||
|
||||
@ -193,6 +193,8 @@ class TouchscreenIndexTemplate(Template):
|
||||
div = DIV(
|
||||
masthead_p,
|
||||
PT(date, style='text-align:center'),
|
||||
#DIV(style="border-color:gray;border-top-style:solid;border-width:thin"),
|
||||
DIV(style="border-top:1px solid gray;border-bottom:1em solid white"),
|
||||
toc)
|
||||
self.root = HTML(head, BODY(div))
|
||||
|
||||
@ -256,10 +258,9 @@ class TouchscreenFeedTemplate(Template):
|
||||
head.append(STYLE(extra_css, type='text/css'))
|
||||
body = BODY(style='page-break-before:always')
|
||||
div = DIV(
|
||||
H2(feed.title,
|
||||
CLASS('calibre_feed_title', 'calibre_rescale_160')),
|
||||
CLASS('calibre_rescale_100')
|
||||
)
|
||||
H2(feed.title, CLASS('calibre_feed_title', 'calibre_rescale_160')),
|
||||
DIV(style="border-top:1px solid gray;border-bottom:1em solid white")
|
||||
)
|
||||
body.append(div)
|
||||
if getattr(feed, 'image', None):
|
||||
div.append(DIV(IMG(
|
||||
@ -278,17 +279,33 @@ class TouchscreenFeedTemplate(Template):
|
||||
if not getattr(article, 'downloaded', False):
|
||||
continue
|
||||
tr = TR()
|
||||
td = TD(
|
||||
A(article.title, CLASS('summary_headline','calibre_rescale_120',
|
||||
href=article.url))
|
||||
)
|
||||
if article.author:
|
||||
td.append(DIV(article.author,
|
||||
CLASS('summary_byline', 'calibre_rescale_100')))
|
||||
if article.summary:
|
||||
td.append(DIV(cutoff(article.text_summary),
|
||||
CLASS('summary_text', 'calibre_rescale_100')))
|
||||
tr.append(td)
|
||||
|
||||
if True:
|
||||
div_td = DIV(
|
||||
A(article.title, CLASS('summary_headline','calibre_rescale_120',
|
||||
href=article.url)),
|
||||
style="display:inline-block")
|
||||
if article.author:
|
||||
div_td.append(DIV(article.author,
|
||||
CLASS('summary_byline', 'calibre_rescale_100')))
|
||||
if article.summary:
|
||||
div_td.append(DIV(cutoff(article.text_summary),
|
||||
CLASS('summary_text', 'calibre_rescale_100')))
|
||||
tr.append(TD(div_td))
|
||||
else:
|
||||
td = TD(
|
||||
A(article.title, CLASS('summary_headline','calibre_rescale_120',
|
||||
href=article.url))
|
||||
)
|
||||
if article.author:
|
||||
td.append(DIV(article.author,
|
||||
CLASS('summary_byline', 'calibre_rescale_100')))
|
||||
if article.summary:
|
||||
td.append(DIV(cutoff(article.text_summary),
|
||||
CLASS('summary_text', 'calibre_rescale_100')))
|
||||
|
||||
tr.append(td)
|
||||
|
||||
toc.append(tr)
|
||||
div.append(toc)
|
||||
|
||||
|
@ -136,7 +136,7 @@ class RecursiveFetcher(object):
|
||||
self.remove_tags_before = getattr(options, 'remove_tags_before', None)
|
||||
self.keep_only_tags = getattr(options, 'keep_only_tags', [])
|
||||
self.preprocess_html_ext = getattr(options, 'preprocess_html', lambda soup: soup)
|
||||
self.prepreprocess_html_ext = getattr(options, 'prepreprocess_html', lambda soup: soup)
|
||||
self.prepreprocess_html_ext = getattr(options, 'skip_ad_pages', lambda soup: None)
|
||||
self.postprocess_html_ext= getattr(options, 'postprocess_html', None)
|
||||
self._is_link_wanted = getattr(options, 'is_link_wanted',
|
||||
default_is_link_wanted)
|
||||
@ -154,7 +154,9 @@ class RecursiveFetcher(object):
|
||||
nmassage.append((re.compile(r'<!--.*?-->', re.DOTALL), lambda m: ''))
|
||||
soup = BeautifulSoup(xml_to_unicode(src, self.verbose, strip_encoding_pats=True)[0], markupMassage=nmassage)
|
||||
|
||||
soup = self.prepreprocess_html_ext(soup)
|
||||
replace = self.prepreprocess_html_ext(soup)
|
||||
if replace is not None:
|
||||
soup = BeautifulSoup(xml_to_unicode(src, self.verbose, strip_encoding_pats=True)[0], markupMassage=nmassage)
|
||||
|
||||
if self.keep_only_tags:
|
||||
body = Tag(soup, 'body')
|
||||
|
Loading…
x
Reference in New Issue
Block a user