mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Synchronized with upstream.
This commit is contained in:
commit
946b91f767
@ -64,9 +64,11 @@ class PRS505(Device):
|
||||
<match key="info.category" string="volume">
|
||||
<match key="@info.parent:@info.parent:@info.parent:@info.parent:usb.vendor_id" int="%(vendor_id)s">
|
||||
<match key="@info.parent:@info.parent:@info.parent:@info.parent:usb.product_id" int="%(product_id)s">
|
||||
<match key="volume.is_partition" bool="true">
|
||||
<merge key="volume.label" type="string">%(storage_card)s</merge>
|
||||
<merge key="%(app)s.cardvolume" type="string">%(deviceclass)s</merge>
|
||||
<match key="@info.parent:@info.parent:@info.parent:@info.parent:usb.device_revision_bcd" int="%(bcd)s">
|
||||
<match key="volume.is_partition" bool="true">
|
||||
<merge key="volume.label" type="string">%(storage_card)s</merge>
|
||||
<merge key="%(app)s.cardvolume" type="string">%(deviceclass)s</merge>
|
||||
</match>
|
||||
</match>
|
||||
</match>
|
||||
</match>
|
||||
|
@ -43,7 +43,8 @@ class DeviceScanner(object):
|
||||
if iswindows:
|
||||
for device_id in self.devices:
|
||||
vid, pid = 'vid_%4.4x'%device.VENDOR_ID, 'pid_%4.4x'%device.PRODUCT_ID
|
||||
if vid in device_id and pid in device_id:
|
||||
rev = ('rev_%4.4x'%device.BCD).replace('a', ':') # Bug in winutil.get_usb_devices converts a to :
|
||||
if vid in device_id and pid in device_id and rev in device_id:
|
||||
return True
|
||||
return False
|
||||
else:
|
||||
|
@ -757,7 +757,10 @@ class LibraryDatabase2(LibraryDatabase):
|
||||
newspapers = self.conn.get('SELECT name FROM tags WHERE id IN (SELECT DISTINCT tag FROM books_tags_link WHERE book IN (select book from books_tags_link where tag IN (SELECT id FROM tags WHERE name=?)))', (_('News'),))
|
||||
if newspapers:
|
||||
newspapers = [f[0] for f in newspapers]
|
||||
newspapers.remove(_('News'))
|
||||
try:
|
||||
newspapers.remove(_('News'))
|
||||
except ValueError:
|
||||
pass
|
||||
categories['news'] = list(map(Tag, newspapers))
|
||||
for tag in categories['news']:
|
||||
tag.count = self.conn.get('SELECT COUNT(id) FROM books_tags_link WHERE tag IN (SELECT DISTINCT id FROM tags WHERE name=?)', (tag,), all=False)
|
||||
|
@ -256,7 +256,9 @@ class LibraryServer(object):
|
||||
' Feeds to read calibre books on a ipod with stanza.'
|
||||
books = []
|
||||
for record in iter(self.db):
|
||||
if 'EPUB' in record[FIELD_MAP['formats']].upper():
|
||||
r = record[FIELD_MAP['formats']]
|
||||
r = r.upper() if r else ''
|
||||
if 'EPUB' in r:
|
||||
authors = ' & '.join([i.replace('|', ',') for i in record[FIELD_MAP['authors']].split(',')])
|
||||
extra = []
|
||||
rating = record[FIELD_MAP['rating']]
|
||||
|
@ -153,8 +153,9 @@ Content From The Web
|
||||
My downloaded news content causes the reader to reset.
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
This is a bug in the SONY firmware. The problem can be mitigated by switching the output format to EPUB
|
||||
in the configuration dialog. EPUB files typically only cause the reader to reset when clicking on certain
|
||||
links. Alternatively, you can use the LRF output format and use the SONY software to transfer the files to the reader. The SONY software pre-paginates the LRF file, thereby reducing the number of resets.
|
||||
in the configuration dialog. Alternatively, you can use the LRF output format and use the SONY software
|
||||
to transfer the files to the reader. The SONY software pre-paginates the LRF file,
|
||||
thereby reducing the number of resets.
|
||||
|
||||
I obtained a recipe for a news site as a .py file from somewhere, how do I use it?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -20,6 +20,7 @@ class Article(object):
|
||||
self.id = id
|
||||
self.title = title.strip() if title else title
|
||||
self.url = url
|
||||
self.summary = summary
|
||||
if summary and not isinstance(summary, unicode):
|
||||
summary = summary.decode('utf-8', 'replace')
|
||||
if summary and '<' in summary:
|
||||
@ -31,7 +32,7 @@ class Article(object):
|
||||
print summary.encode('utf-8')
|
||||
traceback.print_exc()
|
||||
summary = u''
|
||||
self.summary = summary
|
||||
self.text_summary = summary
|
||||
self.content = content
|
||||
self.date = published
|
||||
self.utctime = datetime(*self.date[:6])
|
||||
|
@ -7,12 +7,17 @@ Defines various abstract base classes that can be subclassed to create powerful
|
||||
__docformat__ = "restructuredtext en"
|
||||
|
||||
|
||||
import logging, os, cStringIO, time, traceback, re, urlparse, sys
|
||||
import logging, os, cStringIO, time, traceback, re, urlparse, sys, tempfile, functools
|
||||
from collections import defaultdict
|
||||
from functools import partial
|
||||
from contextlib import nested, closing
|
||||
|
||||
from calibre import browser, __appname__, iswindows, LoggingInterface, strftime
|
||||
from PyQt4.Qt import QApplication, QFile, Qt, QPalette, QSize, QImage, QPainter, \
|
||||
QBuffer, QByteArray, SIGNAL, QUrl, QEventLoop, QIODevice
|
||||
from PyQt4.QtWebKit import QWebPage
|
||||
|
||||
|
||||
from calibre import browser, __appname__, iswindows, LoggingInterface, strftime, __version__
|
||||
from calibre.ebooks.BeautifulSoup import BeautifulSoup, NavigableString, CData, Tag
|
||||
from calibre.ebooks.metadata.opf import OPFCreator
|
||||
from calibre.ebooks.lrf import entity_to_unicode
|
||||
@ -23,6 +28,7 @@ from calibre.web.fetch.simple import option_parser as web2disk_option_parser
|
||||
from calibre.web.fetch.simple import RecursiveFetcher
|
||||
from calibre.utils.threadpool import WorkRequest, ThreadPool, NoResultsPending
|
||||
from calibre.ptempfile import PersistentTemporaryFile
|
||||
from calibre.gui2 import images_rc # Needed for default cover
|
||||
|
||||
|
||||
class BasicNewsRecipe(object, LoggingInterface):
|
||||
@ -766,6 +772,89 @@ class BasicNewsRecipe(object, LoggingInterface):
|
||||
self.cover_path = cpath
|
||||
|
||||
|
||||
def default_cover(self, cover_file):
|
||||
'''
|
||||
Create a generic cover for recipes that dont have a cover
|
||||
'''
|
||||
if QApplication.instance() is None: QApplication([])
|
||||
f = QFile(':/library')
|
||||
f.open(QIODevice.ReadOnly)
|
||||
img = str(f.readAll())
|
||||
f.close()
|
||||
f = tempfile.NamedTemporaryFile(suffix='library.png')
|
||||
f.write(img)
|
||||
f.flush()
|
||||
img = f.name
|
||||
html= u'''\
|
||||
<html>
|
||||
<head>
|
||||
<style type="text/css">
|
||||
body {
|
||||
background: white no-repeat fixed center center;
|
||||
text-align: center;
|
||||
vertical-align: center;
|
||||
overflow: hidden;
|
||||
font-size: 18px;
|
||||
}
|
||||
h1 { font-family: serif; }
|
||||
h2, h4 { font-family: monospace; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>%(title)s</h1>
|
||||
<br/><br/>
|
||||
<div style="position:relative">
|
||||
<div style="position: absolute; left: 0; top: 0; width:100%%; height:100%%; vertical-align:center">
|
||||
<img src="%(img)s" alt="calibre" style="opacity:0.3"/>
|
||||
</div>
|
||||
<div style="position: absolute; left: 0; top: 0; width:100%%; height:100%%; vertical-align:center">
|
||||
<h2>%(date)s</h2>
|
||||
<br/><br/><br/><br/><br/>
|
||||
<h3>%(author)s</h3>
|
||||
<br/><br/></br/><br/><br/><br/><br/><br/><br/>
|
||||
<h4>Produced by %(app)s</h4>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
'''%dict(title=self.title, author=self.__author__,
|
||||
date=time.strftime(self.timefmt),
|
||||
app=__appname__ +' '+__version__,
|
||||
img=img)
|
||||
f2 = tempfile.NamedTemporaryFile(suffix='cover.html')
|
||||
f2.write(html)
|
||||
f2.flush()
|
||||
page = QWebPage()
|
||||
pal = page.palette()
|
||||
pal.setBrush(QPalette.Background, Qt.white)
|
||||
page.setPalette(pal)
|
||||
page.setViewportSize(QSize(590, 750))
|
||||
page.mainFrame().setScrollBarPolicy(Qt.Vertical, Qt.ScrollBarAlwaysOff)
|
||||
page.mainFrame().setScrollBarPolicy(Qt.Horizontal, Qt.ScrollBarAlwaysOff)
|
||||
loop = QEventLoop()
|
||||
def render_html(page, loop, ok):
|
||||
try:
|
||||
image = QImage(page.viewportSize(), QImage.Format_ARGB32)
|
||||
image.setDotsPerMeterX(96*(100/2.54))
|
||||
image.setDotsPerMeterY(96*(100/2.54))
|
||||
painter = QPainter(image)
|
||||
page.mainFrame().render(painter)
|
||||
painter.end()
|
||||
ba = QByteArray()
|
||||
buf = QBuffer(ba)
|
||||
buf.open(QBuffer.WriteOnly)
|
||||
image.save(buf, 'JPEG')
|
||||
image_data = str(ba.data())
|
||||
cover_file.write(image_data)
|
||||
cover_file.flush()
|
||||
finally:
|
||||
loop.exit(0)
|
||||
|
||||
page.connect(page, SIGNAL('loadFinished(bool)'), functools.partial(render_html, page, loop))
|
||||
page.mainFrame().load(QUrl.fromLocalFile(f2.name))
|
||||
loop.exec_()
|
||||
|
||||
|
||||
def create_opf(self, feeds, dir=None):
|
||||
if dir is None:
|
||||
dir = self.output_dir
|
||||
@ -778,7 +867,11 @@ class BasicNewsRecipe(object, LoggingInterface):
|
||||
|
||||
manifest = [os.path.join(dir, 'feed_%d'%i) for i in range(len(feeds))]
|
||||
manifest.append(os.path.join(dir, 'index.html'))
|
||||
cpath = getattr(self, 'cover_path', None)
|
||||
cpath = getattr(self, 'cover_path', None)
|
||||
if cpath is None:
|
||||
pf = PersistentTemporaryFile('_recipe_cover.jpg')
|
||||
self.default_cover(pf)
|
||||
cpath = pf.name
|
||||
if cpath is not None and os.access(cpath, os.R_OK):
|
||||
opf.cover = cpath
|
||||
manifest.append(cpath)
|
||||
|
@ -145,7 +145,7 @@ class FeedTemplate(Template):
|
||||
<a class="article" href="${article.url}">${article.title}</a>
|
||||
<span class="article_date">${article.localtime.strftime(" [%a, %d %b %H:%M]")}</span>
|
||||
<div class="article_decription" py:if="article.summary">
|
||||
${Markup(cutoff(article.summary))}
|
||||
${Markup(cutoff(article.text_summary))}
|
||||
</div>
|
||||
</li>
|
||||
</py:for>
|
||||
|
Loading…
x
Reference in New Issue
Block a user