Synchronized with upstream.

This commit is contained in:
Marshall T. Vandegrift 2008-12-09 09:25:34 -05:00
commit 1b8887894a
14 changed files with 188 additions and 96 deletions

View File

@ -6,13 +6,13 @@ __docformat__ = 'restructuredtext en'
''' '''
Freeze app into executable using py2exe. Freeze app into executable using py2exe.
''' '''
QT_DIR = 'C:\\Qt\\4.4.1' QT_DIR = 'C:\\Qt\\4.4.3'
LIBUSB_DIR = 'C:\\libusb' LIBUSB_DIR = 'C:\\libusb'
LIBUNRAR = 'C:\\Program Files\\UnrarDLL\\unrar.dll' LIBUNRAR = 'C:\\Program Files\\UnrarDLL\\unrar.dll'
PDFTOHTML = 'C:\\pdftohtml\\pdftohtml.exe' PDFTOHTML = 'C:\\pdftohtml\\pdftohtml.exe'
IMAGEMAGICK_DIR = 'C:\\ImageMagick' IMAGEMAGICK_DIR = 'C:\\ImageMagick'
FONTCONFIG_DIR = 'C:\\fontconfig' FONTCONFIG_DIR = 'C:\\fontconfig'
VC90 = r'C:\Program Files\Microsoft Visual Studio 9.0\VC\redist\x86\Microsoft.VC90.CRT'
import sys, os, py2exe, shutil, zipfile, glob, subprocess, re import sys, os, py2exe, shutil, zipfile, glob, subprocess, re
from distutils.core import setup from distutils.core import setup
@ -65,6 +65,8 @@ class BuildEXE(py2exe.build_exe.py2exe):
shutil.copyfile(f, os.path.join(self.dist_dir, os.path.basename(f))) shutil.copyfile(f, os.path.join(self.dist_dir, os.path.basename(f)))
for f in glob.glob(os.path.join(BASE_DIR, 'src', 'calibre', 'plugins', '*.pyd')): for f in glob.glob(os.path.join(BASE_DIR, 'src', 'calibre', 'plugins', '*.pyd')):
shutil.copyfile(f, os.path.join(tgt, os.path.basename(f))) shutil.copyfile(f, os.path.join(tgt, os.path.basename(f)))
for f in glob.glob(os.path.join(BASE_DIR, 'src', 'calibre', 'plugins', '*.manifest')):
shutil.copyfile(f, os.path.join(tgt, os.path.basename(f)))
shutil.copyfile('LICENSE', os.path.join(self.dist_dir, 'LICENSE')) shutil.copyfile('LICENSE', os.path.join(self.dist_dir, 'LICENSE'))
print print
print 'Adding QtXml4.dll' print 'Adding QtXml4.dll'
@ -115,12 +117,17 @@ class BuildEXE(py2exe.build_exe.py2exe):
shutil.copytree(f, tgt) shutil.copytree(f, tgt)
else: else:
shutil.copyfile(f, tgt) shutil.copyfile(f, tgt)
print print
print 'Doing DLL redirection' # See http://msdn.microsoft.com/en-us/library/ms682600(VS.85).aspx print 'Doing DLL redirection' # See http://msdn.microsoft.com/en-us/library/ms682600(VS.85).aspx
for f in glob.glob(os.path.join(PY2EXE_DIR, '*.exe')): for f in glob.glob(os.path.join(PY2EXE_DIR, '*.exe')):
open(f + '.local', 'w').write('\n') open(f + '.local', 'w').write('\n')
print
print 'Adding Windows runtime dependencies...'
for f in glob.glob(os.path.join(VC90, '*')):
shutil.copyfile(f, os.path.join(PY2EXE_DIR, os.path.basename(f)))
@classmethod @classmethod
def manifest(cls, prog): def manifest(cls, prog):
@ -142,17 +149,17 @@ def main(args=sys.argv):
{'script' : scripts['gui'][0], {'script' : scripts['gui'][0],
'dest_base' : APPNAME, 'dest_base' : APPNAME,
'icon_resources' : [(1, ICONS[0])], 'icon_resources' : [(1, ICONS[0])],
'other_resources' : [BuildEXE.manifest(APPNAME)], #'other_resources' : [BuildEXE.manifest(APPNAME)],
}, },
{'script' : scripts['gui'][1], {'script' : scripts['gui'][1],
'dest_base' : 'lrfviewer', 'dest_base' : 'lrfviewer',
'icon_resources' : [(1, ICONS[1])], 'icon_resources' : [(1, ICONS[1])],
'other_resources' : [BuildEXE.manifest('lrfviewer')], #'other_resources' : [BuildEXE.manifest('lrfviewer')],
}, },
{'script' : scripts['gui'][2], {'script' : scripts['gui'][2],
'dest_base' : 'ebook-viewer', 'dest_base' : 'ebook-viewer',
'icon_resources' : [(1, ICONS[1])], 'icon_resources' : [(1, ICONS[1])],
'other_resources' : [BuildEXE.manifest('ebook-viewer')], #'other_resources' : [BuildEXE.manifest('ebook-viewer')],
}, },
], ],
console = console, console = console,
@ -162,12 +169,12 @@ def main(args=sys.argv):
'includes' : [ 'includes' : [
'sip', 'pkg_resources', 'PyQt4.QtSvg', 'sip', 'pkg_resources', 'PyQt4.QtSvg',
'mechanize', 'ClientForm', 'wmi', 'mechanize', 'ClientForm', 'wmi',
'win32file', 'pythoncom', 'rtf2xml', 'win32file', 'pythoncom',
'win32process', 'win32api', 'msvcrt', 'win32process', 'win32api', 'msvcrt',
'win32event', 'calibre.ebooks.lrf.any.*', 'win32event', 'calibre.ebooks.lrf.any.*',
'calibre.ebooks.lrf.feeds.*', 'calibre.ebooks.lrf.feeds.*',
'genshi', 'BeautifulSoup', 'BeautifulSoup', 'pyreadline',
'path', 'pydoc', 'IPython.Extensions.*', 'pydoc', 'IPython.Extensions.*',
'calibre.web.feeds.recipes.*', 'calibre.web.feeds.recipes.*',
'PyQt4.QtWebKit', 'PyQt4.QtNetwork', 'PyQt4.QtWebKit', 'PyQt4.QtNetwork',
], ],

View File

@ -6,7 +6,7 @@ __docformat__ = 'restructuredtext en'
''' '''
Build PyQt extensions. Integrates with distutils (but uses the PyQt build system). Build PyQt extensions. Integrates with distutils (but uses the PyQt build system).
''' '''
from distutils.core import Extension from distutils.core import Extension as _Extension
from distutils.command.build_ext import build_ext as _build_ext from distutils.command.build_ext import build_ext as _build_ext
from distutils.dep_util import newer_group from distutils.dep_util import newer_group
from distutils import log from distutils import log
@ -15,12 +15,23 @@ import sipconfig, os, sys, string, glob, shutil
from PyQt4 import pyqtconfig from PyQt4 import pyqtconfig
iswindows = 'win32' in sys.platform iswindows = 'win32' in sys.platform
QMAKE = os.path.expanduser('~/qt/bin/qmake') if 'darwin' in sys.platform else'qmake' QMAKE = os.path.expanduser('~/qt/bin/qmake') if 'darwin' in sys.platform else'qmake'
WINDOWS_PYTHON = ['C:/Python25/libs'] WINDOWS_PYTHON = ['C:/Python26/libs']
OSX_SDK = '/Developer/SDKs/MacOSX10.4u.sdk' OSX_SDK = '/Developer/SDKs/MacOSX10.4u.sdk'
def replace_suffix(path, new_suffix): def replace_suffix(path, new_suffix):
return os.path.splitext(path)[0] + new_suffix return os.path.splitext(path)[0] + new_suffix
class Extension(_Extension):
pass
if iswindows:
from distutils import msvc9compiler
msvc = msvc9compiler.MSVCCompiler()
msvc.initialize()
nmake = msvc.find_exe('nmake.exe')
rc = msvc.find_exe('rc.exe')
class PyQtExtension(Extension): class PyQtExtension(Extension):
def __init__(self, name, sources, sip_sources, **kw): def __init__(self, name, sources, sip_sources, **kw):
@ -37,9 +48,7 @@ class PyQtExtension(Extension):
class build_ext(_build_ext): class build_ext(_build_ext):
def make(self, makefile): def make(self, makefile):
make = 'make' make = nmake if iswindows else 'make'
if iswindows:
make = 'mingw32-make'
self.spawn([make, '-f', makefile]) self.spawn([make, '-f', makefile])
def build_qt_objects(self, ext, bdir): def build_qt_objects(self, ext, bdir):
@ -65,12 +74,13 @@ CONFIG += x86 ppc
open(name+'.pro', 'wb').write(pro) open(name+'.pro', 'wb').write(pro)
self.spawn([QMAKE, '-o', 'Makefile.qt', name+'.pro']) self.spawn([QMAKE, '-o', 'Makefile.qt', name+'.pro'])
self.make('Makefile.qt') self.make('Makefile.qt')
pat = 'release\\*.o' if iswindows else '*.o' pat = 'release\\*.obj' if iswindows else '*.o'
return map(os.path.abspath, glob.glob(pat)) return map(os.path.abspath, glob.glob(pat))
finally: finally:
os.chdir(cwd) os.chdir(cwd)
def build_sbf(self, sip, sbf, bdir): def build_sbf(self, sip, sbf, bdir):
print '\tBuilding spf...'
sip_bin = self.sipcfg.sip_bin sip_bin = self.sipcfg.sip_bin
self.spawn([sip_bin, self.spawn([sip_bin,
"-c", bdir, "-c", bdir,
@ -100,9 +110,7 @@ CONFIG += x86 ppc
def build_extension(self, ext): def build_extension(self, ext):
self.inplace = True # Causes extensions to be built in the source tree self.inplace = True # Causes extensions to be built in the source tree
if not isinstance(ext, PyQtExtension):
return _build_ext.build_extension(self, ext)
fullname = self.get_ext_fullname(ext.name) fullname = self.get_ext_fullname(ext.name)
if self.inplace: if self.inplace:
# ignore build-lib -- put the compiled extension into # ignore build-lib -- put the compiled extension into
@ -119,7 +127,38 @@ CONFIG += x86 ppc
else: else:
ext_filename = os.path.join(self.build_lib, ext_filename = os.path.join(self.build_lib,
self.get_ext_filename(fullname)) self.get_ext_filename(fullname))
bdir = os.path.abspath(os.path.join(self.build_temp, fullname)) bdir = os.path.abspath(os.path.join(self.build_temp, fullname))
if not os.path.exists(bdir):
os.makedirs(bdir)
if not isinstance(ext, PyQtExtension):
if not iswindows:
return _build_ext.build_extension(self, ext)
c_sources = [f for f in ext.sources if os.path.splitext(f)[1].lower() in ('.c', '.cpp', '.cxx')]
compile_args = '/c /nologo /Ox /MD /W3 /GX /DNDEBUG'.split()
compile_args += ext.extra_compile_args
self.swig_opts = ''
inc_dirs = self.include_dirs + [x.replace('/', '\\') for x in ext.include_dirs]
cc = [msvc.cc] + compile_args + ['-I%s'%x for x in list(set(inc_dirs))]
objects = []
for f in c_sources:
o = os.path.join(bdir, os.path.basename(f)+'.obj')
objects.append(o)
compiler = cc + ['/Tc'+f, '/Fo'+o]
self.spawn(compiler)
out = os.path.join(bdir, base+'.pyd')
linker = [msvc.linker] + '/DLL /nologo /INCREMENTAL:NO'.split()
linker += ['/LIBPATH:'+x for x in self.library_dirs]
linker += [x+'.lib' for x in ext.libraries]
linker += ['/EXPORT:init'+base] + objects + ['/OUT:'+out]
self.spawn(linker)
for src in (out, out+'.manifest'):
shutil.copyfile(src, os.path.join('src', 'calibre', 'plugins', os.path.basename(src)))
return
if not os.path.exists(bdir): if not os.path.exists(bdir):
os.makedirs(bdir) os.makedirs(bdir)
ext.sources2 = map(os.path.abspath, ext.sources) ext.sources2 = map(os.path.abspath, ext.sources)

View File

@ -46,10 +46,10 @@ main_functions = {
} }
if __name__ == '__main__': if __name__ == '__main__':
from setuptools import setup, find_packages, Extension from setuptools import setup, find_packages
from distutils.command.build import build as _build from distutils.command.build import build as _build
from distutils.core import Command as _Command from distutils.core import Command as _Command
from pyqtdistutils import PyQtExtension, build_ext from pyqtdistutils import PyQtExtension, build_ext, Extension
import subprocess, glob import subprocess, glob
def newer(targets, sources): def newer(targets, sources):
@ -394,8 +394,10 @@ if __name__ == '__main__':
ext_modules.append(Extension('calibre.plugins.winutil', ext_modules.append(Extension('calibre.plugins.winutil',
sources=['src/calibre/utils/windows/winutil.c'], sources=['src/calibre/utils/windows/winutil.c'],
libraries=['shell32', 'setupapi'], libraries=['shell32', 'setupapi'],
include_dirs=['C:/WinDDK/6001.18001/inc/api/']) include_dirs=['C:/WinDDK/6001.18001/inc/api/',
) 'C:/WinDDK/6001.18001/inc/crt/'],
extra_compile_args=['/X']
))
if isosx: if isosx:
ext_modules.append(Extension('calibre.plugins.usbobserver', ext_modules.append(Extension('calibre.plugins.usbobserver',
sources=['src/calibre/devices/usbobserver/usbobserver.c']) sources=['src/calibre/devices/usbobserver/usbobserver.c'])

View File

@ -4,17 +4,20 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
This module provides a thin ctypes based wrapper around libusb. This module provides a thin ctypes based wrapper around libusb.
""" """
from ctypes import cdll, POINTER, byref, pointer, Structure, \ from ctypes import cdll, POINTER, byref, pointer, Structure as _Structure, \
c_ubyte, c_ushort, c_int, c_char, c_void_p, c_byte, c_uint c_ubyte, c_ushort, c_int, c_char, c_void_p, c_byte, c_uint
from errno import EBUSY, ENOMEM from errno import EBUSY, ENOMEM
from calibre import iswindows, isosx, load_library, isfrozen from calibre import iswindows, isosx, load_library
_libusb_name = 'libusb' _libusb_name = 'libusb'
PATH_MAX = 511 if iswindows else 1024 if isosx else 4096 PATH_MAX = 511 if iswindows else 1024 if isosx else 4096
if iswindows: if iswindows:
Structure._pack_ = 1 class Structure(_Structure):
_pack_ = 1
_libusb_name = 'libusb0' _libusb_name = 'libusb0'
else:
Structure = _Structure
try: try:
try: try:

View File

@ -46,8 +46,8 @@
</property> </property>
<property name="maximumSize" > <property name="maximumSize" >
<size> <size>
<width>10000</width> <width>16777215</width>
<height>110</height> <height>100</height>
</size> </size>
</property> </property>
<property name="verticalScrollBarPolicy" > <property name="verticalScrollBarPolicy" >
@ -74,14 +74,17 @@
<property name="flow" > <property name="flow" >
<enum>QListView::LeftToRight</enum> <enum>QListView::LeftToRight</enum>
</property> </property>
<property name="isWrapping" stdset="0" > <property name="gridSize" >
<bool>false</bool> <size>
</property> <width>175</width>
<property name="spacing" > <height>90</height>
<number>10</number> </size>
</property> </property>
<property name="viewMode" > <property name="viewMode" >
<enum>QListView::IconMode</enum> <enum>QListView::ListMode</enum>
</property>
<property name="wordWrap" >
<bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>

View File

@ -715,7 +715,7 @@ void PictureFlowPrivate::render()
painter.setPen(Qt::white); painter.setPen(Qt::white);
//painter.setPen(QColor(255,255,255,127)); //painter.setPen(QColor(255,255,255,127));
if (centerIndex < slideCount() and centerIndex > -1) if (centerIndex < slideCount() && centerIndex > -1)
painter.drawText( QRect(0,0, buffer.width(), (buffer.height() - slideSize().height())/2), painter.drawText( QRect(0,0, buffer.width(), (buffer.height() - slideSize().height())/2),
Qt::AlignCenter, slideImages->caption(centerIndex)); Qt::AlignCenter, slideImages->caption(centerIndex));
@ -767,12 +767,12 @@ void PictureFlowPrivate::render()
int sc = slideCount(); int sc = slideCount();
painter.setPen(QColor(255,255,255, (255-fade) )); painter.setPen(QColor(255,255,255, (255-fade) ));
if (leftTextIndex < sc and leftTextIndex > -1) if (leftTextIndex < sc && leftTextIndex > -1)
painter.drawText( QRect(0,0, buffer.width(), (buffer.height() - slideSize().height())/2), painter.drawText( QRect(0,0, buffer.width(), (buffer.height() - slideSize().height())/2),
Qt::AlignCenter, slideImages->caption(leftTextIndex)); Qt::AlignCenter, slideImages->caption(leftTextIndex));
painter.setPen(QColor(255,255,255, fade)); painter.setPen(QColor(255,255,255, fade));
if (leftTextIndex+1 < sc and leftTextIndex > -2) if (leftTextIndex+1 < sc && leftTextIndex > -2)
painter.drawText( QRect(0,0, buffer.width(), (buffer.height() - slideSize().height())/2), painter.drawText( QRect(0,0, buffer.width(), (buffer.height() - slideSize().height())/2),
Qt::AlignCenter, slideImages->caption(leftTextIndex+1)); Qt::AlignCenter, slideImages->caption(leftTextIndex+1));

View File

@ -7,9 +7,9 @@ import re, os, traceback
from PyQt4.QtGui import QListView, QIcon, QFont, QLabel, QListWidget, \ from PyQt4.QtGui import QListView, QIcon, QFont, QLabel, QListWidget, \
QListWidgetItem, QTextCharFormat, QApplication, \ QListWidgetItem, QTextCharFormat, QApplication, \
QSyntaxHighlighter, QCursor, QColor, QWidget, QDialog, \ QSyntaxHighlighter, QCursor, QColor, QWidget, QDialog, \
QAbstractItemDelegate, QPixmap, QStyle, QFontMetrics QPixmap
from PyQt4.QtCore import QAbstractListModel, QVariant, Qt, SIGNAL, \ from PyQt4.QtCore import QAbstractListModel, QVariant, Qt, SIGNAL, \
QObject, QRegExp, QString, QSettings QObject, QRegExp, QString, QSettings, QSize
from calibre.gui2.jobs2 import DetailView from calibre.gui2.jobs2 import DetailView
from calibre.gui2 import human_readable, NONE, TableView, \ from calibre.gui2 import human_readable, NONE, TableView, \
@ -128,56 +128,17 @@ class ImageView(QLabel):
self.setMaximumWidth(width) self.setMaximumWidth(width)
self.setMaximumHeight(height) self.setMaximumHeight(height)
class LocationDelegate(QAbstractItemDelegate):
def __init__(self):
QAbstractItemDelegate.__init__(self)
self.pixmap = QPixmap(40, 40)
self.text = QString('Reader\n999.9 MB Available202')
def rects(self, option):
style = QApplication.style()
font = QFont(option.font)
font.setBold(True)
irect = style.itemPixmapRect(option.rect, Qt.AlignHCenter|Qt.AlignTop, self.pixmap)
trect = style.itemTextRect(QFontMetrics(font), option.rect,
Qt.AlignHCenter|Qt.AlignTop, True, self.text)
trect.moveTop(irect.bottom())
return irect, trect
def sizeHint(self, option, index):
irect, trect = self.rects(option)
return irect.united(trect).size()
def paint(self, painter, option, index):
style = QApplication.style()
painter.save()
if hasattr(QStyle, 'CE_ItemViewItem'):
QApplication.style().drawControl(QStyle.CE_ItemViewItem, option, painter)
highlight = getattr(index.model(), 'highlight_row', -1) == index.row()
mode = QIcon.Active if highlight else QIcon.Normal
pixmap = QIcon(index.model().data(index, Qt.DecorationRole)).pixmap(self.pixmap.size())
pixmap = style.generatedIconPixmap(mode, pixmap, option)
text = index.model().data(index, Qt.DisplayRole).toString()
irect, trect = self.rects(option)
style.drawItemPixmap(painter, irect, Qt.AlignHCenter|Qt.AlignTop, pixmap)
font = QFont(option.font)
font.setBold(highlight)
painter.setFont(font)
style.drawItemText(painter, trect, Qt.AlignHCenter|Qt.AlignBottom,
option.palette, True, text)
painter.restore()
class LocationModel(QAbstractListModel): class LocationModel(QAbstractListModel):
def __init__(self, parent): def __init__(self, parent):
QAbstractListModel.__init__(self, parent) QAbstractListModel.__init__(self, parent)
self.icons = [QVariant(QIcon(':/library')), self.icons = [QVariant(QIcon(':/library')),
QVariant(QIcon(':/images/reader.svg')), QVariant(QIcon(':/images/reader.svg')),
QVariant(QIcon(':/images/sd.svg'))] QVariant(QIcon(':/images/sd.svg'))]
self.text = [_('Library'), self.text = [_('Library'),
_('Reader\n%s available'), _('Reader\n%s\navailable'),
_('Card\n%s available')] _('Card\n%s\navailable')]
self.free = [-1, -1] self.free = [-1, -1]
self.highlight_row = 0 self.highlight_row = 0
self.tooltips = [ self.tooltips = [
@ -199,7 +160,13 @@ class LocationModel(QAbstractListModel):
elif role == Qt.DecorationRole: elif role == Qt.DecorationRole:
data = self.icons[row] data = self.icons[row]
elif role == Qt.ToolTipRole: elif role == Qt.ToolTipRole:
return QVariant(self.tooltips[row]) data = QVariant(self.tooltips[row])
elif role == Qt.SizeHintRole:
data = QVariant(QSize(155, 90))
elif role == Qt.FontRole:
font = QFont('monospace')
font.setBold(row == self.highlight_row)
data = QVariant(font)
return data return data
def headerData(self, section, orientation, role): def headerData(self, section, orientation, role):
@ -223,8 +190,6 @@ class LocationView(QListView):
self.setModel(LocationModel(self)) self.setModel(LocationModel(self))
self.reset() self.reset()
QObject.connect(self.selectionModel(), SIGNAL('currentChanged(QModelIndex, QModelIndex)'), self.current_changed) QObject.connect(self.selectionModel(), SIGNAL('currentChanged(QModelIndex, QModelIndex)'), self.current_changed)
self.delegate = LocationDelegate()
self.setItemDelegate(self.delegate)
self.setCursor(Qt.PointingHandCursor) self.setCursor(Qt.PointingHandCursor)
def current_changed(self, current, previous): def current_changed(self, current, previous):
@ -270,7 +235,6 @@ class FontFamilyModel(QAbstractListModel):
try: try:
family = self.families[index.row()] family = self.families[index.row()]
except: except:
import traceback
traceback.print_exc() traceback.print_exc()
return NONE return NONE
if role == Qt.DisplayRole: if role == Qt.DisplayRole:

View File

@ -7,7 +7,7 @@ This module provides a thin ctypes based wrapper around libunrar.
See ftp://ftp.rarlabs.com/rar/unrarsrc-3.7.5.tar.gz See ftp://ftp.rarlabs.com/rar/unrarsrc-3.7.5.tar.gz
""" """
import os, ctypes, sys, re import os, ctypes, sys, re
from ctypes import Structure, c_char_p, c_uint, c_void_p, POINTER, \ from ctypes import Structure as _Structure, c_char_p, c_uint, c_void_p, POINTER, \
byref, c_wchar_p, c_int, c_char, c_wchar byref, c_wchar_p, c_int, c_char, c_wchar
from tempfile import NamedTemporaryFile from tempfile import NamedTemporaryFile
from StringIO import StringIO from StringIO import StringIO
@ -18,9 +18,12 @@ from calibre.ptempfile import TemporaryDirectory
_librar_name = 'libunrar' _librar_name = 'libunrar'
cdll = ctypes.cdll cdll = ctypes.cdll
if iswindows: if iswindows:
Structure._pack_ = 1 class Structure(_Structure):
_pack_ = 1
_librar_name = 'unrar' _librar_name = 'unrar'
cdll = ctypes.windll cdll = ctypes.windll
else:
Structure = _Structure
if hasattr(sys, 'frozen') and iswindows: if hasattr(sys, 'frozen') and iswindows:
_libunrar = cdll.LoadLibrary(os.path.join(os.path.dirname(sys.executable), 'unrar.dll')) _libunrar = cdll.LoadLibrary(os.path.join(os.path.dirname(sys.executable), 'unrar.dll'))
_libunrar = load_library(_librar_name, cdll) _libunrar = load_library(_librar_name, cdll)

View File

@ -9,6 +9,7 @@ import time, logging, traceback, copy
from datetime import datetime from datetime import datetime
from calibre.web.feeds.feedparser import parse from calibre.web.feeds.feedparser import parse
from lxml import html
class Article(object): class Article(object):
@ -19,6 +20,17 @@ class Article(object):
self.id = id self.id = id
self.title = title.strip() if title else title self.title = title.strip() if title else title
self.url = url self.url = url
if summary and not isinstance(summary, unicode):
summary = summary.decode('utf-8', 'replace')
if summary and '<' in summary:
try:
s = html.fragment_fromstring(summary, create_parent=True)
summary = html.tostring(s, method='text', encoding=unicode)
except:
print 'Failed to process article summary, deleting:'
print summary.encode('utf-8')
traceback.print_exc()
summary = u''
self.summary = summary self.summary = summary
self.content = content self.content = content
self.date = published self.date = published

View File

@ -586,9 +586,9 @@ class BasicNewsRecipe(object, LoggingInterface):
if npos < 0: if npos < 0:
npos = pos npos = pos
ans = src[:npos+1] ans = src[:npos+1]
if isinstance(ans, unicode): if len(ans) < len(src):
return ans return ans+u'\u2026' if isinstance(ans, unicode) else ans + '...'
return ans+u'\u2026' if isinstance(ans, unicode) else ans + '...' return ans

View File

@ -17,7 +17,7 @@ recipe_modules = [
'blic', 'novosti', 'danas', 'vreme', 'times_online', 'the_scotsman', 'blic', 'novosti', 'danas', 'vreme', 'times_online', 'the_scotsman',
'nytimes_sub', 'security_watch', 'cyberpresse', 'st_petersburg_times', 'nytimes_sub', 'security_watch', 'cyberpresse', 'st_petersburg_times',
'clarin', 'financial_times', 'heise', 'le_monde', 'harpers', 'science_aas', 'clarin', 'financial_times', 'heise', 'le_monde', 'harpers', 'science_aas',
'science_news', 'the_nation', 'lrb' 'science_news', 'the_nation', 'lrb', 'harpers_full'
] ]
import re, imp, inspect, time, os import re, imp, inspect, time, os

View File

@ -0,0 +1,61 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2008, Darko Miletic <darko.miletic at gmail.com>'
'''
harpers.org - paid subscription/ printed issue articles
This recipe only get's article's published in text format
images and pdf's are ignored
'''
from calibre import strftime
from calibre.web.feeds.news import BasicNewsRecipe
class Harpers_full(BasicNewsRecipe):
title = u"Harper's Magazine - articles from printed edition"
__author__ = u'Darko Miletic'
description = u"Harper's Magazine: Founded June 1850."
oldest_article = 30
max_articles_per_feed = 100
no_stylesheets = True
use_embedded_content = False
simultaneous_downloads = 1
delay = 1
needs_subscription = True
INDEX = strftime('http://www.harpers.org/archive/%Y/%m')
LOGIN = 'http://www.harpers.org'
cover_url = strftime('http://www.harpers.org/media/pages/%Y/%m/gif/0001.gif')
keep_only_tags = [ dict(name='div', attrs={'id':'cached'}) ]
remove_tags = [
dict(name='table', attrs={'class':'rcnt'})
,dict(name='table', attrs={'class':'rcnt topline'})
]
def get_browser(self):
br = BasicNewsRecipe.get_browser()
if self.username is not None and self.password is not None:
br.open(self.LOGIN)
br.select_form(nr=1)
br['handle' ] = self.username
br['password'] = self.password
br.submit()
return br
def parse_index(self):
articles = []
print 'Processing ' + self.INDEX
soup = self.index_to_soup(self.INDEX)
for item in soup.findAll('div', attrs={'class':'title'}):
text_link = item.parent.find('img',attrs={'alt':'Text'})
if text_link:
url = self.LOGIN + item.a['href']
title = item.a.contents[0]
date = strftime(' %B %Y')
articles.append({
'title' :title
,'date' :date
,'url' :url
,'description':''
})
return [(soup.head.title.string, articles)]

View File

@ -141,12 +141,12 @@ class FeedTemplate(Template):
</div> </div>
<ul> <ul>
<py:for each="i, article in enumerate(feed.articles)"> <py:for each="i, article in enumerate(feed.articles)">
<li id="${'article_%d'%i}" py:if="getattr(article, 'downloaded', False)"> <li id="${'article_%d'%i}" py:if="getattr(article, 'downloaded', False)" style="padding-bottom:0.5em">
<a class="article" href="${article.url}">${article.title}</a> <a class="article" href="${article.url}">${article.title}</a>
<span class="article_date">${article.localtime.strftime(" [%a, %d %b %H:%M]")}</span> <span class="article_date">${article.localtime.strftime(" [%a, %d %b %H:%M]")}</span>
<p class="article_decription" py:if="article.summary"> <div class="article_decription" py:if="article.summary">
${Markup(cutoff(article.summary))} ${Markup(cutoff(article.summary))}
</p> </div>
</li> </li>
</py:for> </py:for>
</ul> </ul>

View File

@ -5,5 +5,3 @@
* Use multiprocessing for cpu_count instead of QThread * Use multiprocessing for cpu_count instead of QThread
* Windows build:
* Compile all dependencies with MSVC 2008 since this is what python now uses