Merge upstream changes

This commit is contained in:
Marshall T. Vandegrift 2009-01-18 21:48:37 -05:00
commit 97468e61fe
41 changed files with 705 additions and 110 deletions

View File

@ -415,8 +415,9 @@ 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=os.environ.get('INCLUDE',
'C:/WinDDK/6001.18001/inc/crt/'], 'C:/WinDDK/6001.18001/inc/api/;'
'C:/WinDDK/6001.18001/inc/crt/').split(';'),
extra_compile_args=['/X'] extra_compile_args=['/X']
)) ))
if isosx: if isosx:
@ -425,7 +426,11 @@ if __name__ == '__main__':
extra_link_args=['-framework', 'IOKit']) extra_link_args=['-framework', 'IOKit'])
) )
plugins = ['plugins/%s.so'%(x.name.rpartition('.')[-1]) for x in ext_modules] if not iswindows:
plugins = ['plugins/%s.so'%(x.name.rpartition('.')[-1]) for x in ext_modules]
else:
plugins = ['plugins/%s.pyd'%(x.name.rpartition('.')[-1]) for x in ext_modules] + \
['plugins/%s.pyd.manifest'%(x.name.rpartition('.')[-1]) for x in ext_modules if 'pictureflow' not in x.name]
setup( setup(
name = APPNAME, name = APPNAME,

View File

@ -2,7 +2,7 @@ __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net' __copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
__appname__ = 'calibre' __appname__ = 'calibre'
__version__ = '0.4.128' __version__ = '0.4.129'
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>" __author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
''' '''
Various run time constants. Various run time constants.

View File

@ -10,8 +10,8 @@ def devices():
from calibre.devices.prs505.driver import PRS505 from calibre.devices.prs505.driver import PRS505
from calibre.devices.prs700.driver import PRS700 from calibre.devices.prs700.driver import PRS700
from calibre.devices.cybookg3.driver import CYBOOKG3 from calibre.devices.cybookg3.driver import CYBOOKG3
#from calibre.devices.kindle.driver import KINDLE from calibre.devices.kindle.driver import KINDLE
return (PRS500, PRS505, PRS700, CYBOOKG3) return (PRS500, PRS505, PRS700, CYBOOKG3, KINDLE)
import time import time
@ -31,4 +31,4 @@ def strftime(epoch, zone=time.gmtime):
src = time.strftime("%w, %d %m %Y %H:%M:%S GMT", zone(epoch)).split() src = time.strftime("%w, %d %m %Y %H:%M:%S GMT", zone(epoch)).split()
src[0] = INVERSE_DAY_MAP[int(src[0][:-1])]+',' src[0] = INVERSE_DAY_MAP[int(src[0][:-1])]+','
src[2] = INVERSE_MONTH_MAP[int(src[2])] src[2] = INVERSE_MONTH_MAP[int(src[2])]
return ' '.join(src) return ' '.join(src)

View File

@ -4,9 +4,11 @@ __copyright__ = '2009, John Schember <john at nachtimwald.com>'
Device driver for Bookeen's Cybook Gen 3 Device driver for Bookeen's Cybook Gen 3
''' '''
import os, fnmatch import os, shutil
from itertools import cycle
from calibre.devices.usbms.driver import USBMS from calibre.devices.usbms.driver import USBMS
import calibre.devices.cybookg3.t2b as t2b
class CYBOOKG3(USBMS): class CYBOOKG3(USBMS):
# Ordered list of supported formats # Ordered list of supported formats
@ -29,6 +31,76 @@ class CYBOOKG3(USBMS):
EBOOK_DIR_MAIN = "eBooks" EBOOK_DIR_MAIN = "eBooks"
SUPPORTS_SUB_DIRS = True SUPPORTS_SUB_DIRS = True
def upload_books(self, files, names, on_card=False, end_session=True,
metadata=None):
if on_card and not self._card_prefix:
raise ValueError(_('The reader has no storage card connected.'))
if not on_card:
path = os.path.join(self._main_prefix, self.EBOOK_DIR_MAIN)
else:
path = os.path.join(self._card_prefix, self.EBOOK_DIR_CARD)
def get_size(obj):
if hasattr(obj, 'seek'):
obj.seek(0, os.SEEK_END)
size = obj.tell()
obj.seek(0)
return size
return os.path.getsize(obj)
sizes = map(get_size, files)
size = sum(sizes)
if on_card and size > self.free_space()[2] - 1024*1024:
raise FreeSpaceError(_("There is insufficient free space on the storage card"))
if not on_card and size > self.free_space()[0] - 2*1024*1024:
raise FreeSpaceError(_("There is insufficient free space in main memory"))
paths = []
names = iter(names)
metadata = iter(metadata)
for infile in files:
newpath = path
mdata = metadata.next()
if self.SUPPORTS_SUB_DIRS:
if 'tags' in mdata.keys():
for tag in mdata['tags']:
if tag.startswith('/'):
newpath += tag
newpath = os.path.normpath(newpath)
break
if not os.path.exists(newpath):
os.makedirs(newpath)
filepath = os.path.join(newpath, names.next())
paths.append(filepath)
if hasattr(infile, 'read'):
infile.seek(0)
dest = open(filepath, 'wb')
shutil.copyfileobj(infile, dest, 10*1024*1024)
dest.flush()
dest.close()
else:
shutil.copy2(infile, filepath)
coverdata = None
if 'cover' in mdata.keys():
if mdata['cover'] != None:
coverdata = mdata['cover'][2]
t2bfile = open('%s_6090.t2b' % (os.path.splitext(filepath)[0]), 'wb')
t2b.write_t2b(t2bfile, coverdata)
t2bfile.close()
return zip(paths, cycle([on_card]))
def delete_books(self, paths, end_session=True): def delete_books(self, paths, end_session=True):
for path in paths: for path in paths:
@ -36,17 +108,15 @@ class CYBOOKG3(USBMS):
os.unlink(path) os.unlink(path)
filepath, ext = os.path.splitext(path) filepath, ext = os.path.splitext(path)
basepath, filename = os.path.split(filepath)
# Delete the ebook auxiliary file # Delete the ebook auxiliary file
if os.path.exists(filepath + '.mbp'): if os.path.exists(filepath + '.mbp'):
os.unlink(filepath + '.mbp') os.unlink(filepath + '.mbp')
# Delete the thumbnails file auto generated for the ebook # Delete the thumbnails file auto generated for the ebook
for p, d, files in os.walk(basepath): if os.path.exists(filepath + '_6090.t2b'):
for filen in fnmatch.filter(files, filename + "*.t2b"): os.unlink(filepath + '_6090.t2b')
os.unlink(os.path.join(p, filen))
try: try:
os.removedirs(os.path.dirname(path)) os.removedirs(os.path.dirname(path))
except: except:

File diff suppressed because one or more lines are too long

View File

@ -16,13 +16,18 @@ class KINDLE(USBMS):
PRODUCT_ID = 0x0001 PRODUCT_ID = 0x0001
BCD = [0x399] BCD = [0x399]
VENDOR_NAME = 'AMAZON' VENDOR_NAME = 'KINDLE'
WINDOWS_MAIN_MEM = 'KINDLE' WINDOWS_MAIN_MEM = 'INTERNAL_STORAGE'
WINDOWS_CARD_MEM = 'CARD_STORAGE'
OSX_MAIN_MEM = 'Kindle Internal Storage Media'
OSX_CARD_MEM = 'Kindle Card Storage Media'
MAIN_MEMORY_VOLUME_LABEL = 'Kindle Main Memory' MAIN_MEMORY_VOLUME_LABEL = 'Kindle Main Memory'
STORAGE_CARD_VOLUME_LABEL = 'Kindle Storage Card' STORAGE_CARD_VOLUME_LABEL = 'Kindle Storage Card'
EBOOK_DIR_MAIN = "documents" EBOOK_DIR_MAIN = "documents"
SUPPORTS_SUB_DIRS = True
def delete_books(self, paths, end_session=True): def delete_books(self, paths, end_session=True):
for path in paths: for path in paths:

View File

@ -125,10 +125,11 @@ class USBMS(Device):
if os.path.exists(path): if os.path.exists(path):
# Delete the ebook # Delete the ebook
os.unlink(path) os.unlink(path)
try: if self.SUPPORTS_SUB_DIRS:
os.removedirs(os.path.dirname(path)) try:
except: os.removedirs(os.path.dirname(path))
pass except:
pass
@classmethod @classmethod
def remove_books_from_metadata(cls, paths, booklists): def remove_books_from_metadata(cls, paths, booklists):
@ -148,7 +149,18 @@ class USBMS(Device):
path = self.munge_path(path) path = self.munge_path(path)
src = open(path, 'rb') src = open(path, 'rb')
shutil.copyfileobj(src, outfile, 10*1024*1024) shutil.copyfileobj(src, outfile, 10*1024*1024)
def put_file(self, infile, path, replace_file=False, end_session=True):
path = self.munge_path(path)
if os.path.isdir(path):
path = os.path.join(path, infile.name)
if not replace_file and os.path.exists(path):
raise PathError('File already exists: ' + path)
dest = open(path, 'wb')
shutil.copyfileobj(infile, dest, 10*1024*1024)
dest.flush()
dest.close()
def munge_path(self, path): def munge_path(self, path):
if path.startswith('/') and not (path.startswith(self._main_prefix) or \ if path.startswith('/') and not (path.startswith(self._main_prefix) or \
(self._card_prefix and path.startswith(self._card_prefix))): (self._card_prefix and path.startswith(self._card_prefix))):
@ -157,6 +169,34 @@ class USBMS(Device):
path = path.replace('card:', self._card_prefix[:-1]) path = path.replace('card:', self._card_prefix[:-1])
return path return path
def list(self, path, recurse=False, end_session=True, munge=True):
if munge:
path = self.munge_path(path)
if os.path.isfile(path):
return [(os.path.dirname(path), [File(path)])]
entries = [File(os.path.join(path, f)) for f in os.listdir(path)]
dirs = [(path, entries)]
for _file in entries:
if recurse and _file.is_dir:
dirs[len(dirs):] = self.list(_file.path, recurse=True, munge=False)
return dirs
def mkdir(self, path, end_session=True):
if self.SUPPORTS_SUB_DIRS:
path = self.munge_path(path)
os.mkdir(path)
def rm(self, path, end_session=True):
path = self.munge_path(path)
self.delete_books([path])
def touch(self, path, end_session=True):
path = self.munge_path(path)
if not os.path.exists(path):
open(path, 'w').close()
if not os.path.isdir(path):
os.utime(path, None)
@classmethod @classmethod
def extract_book_metadata_by_filename(cls, filename): def extract_book_metadata_by_filename(cls, filename):
book_title = '' book_title = ''
@ -183,5 +223,3 @@ class USBMS(Device):
return book_title, book_author, book_mime return book_title, book_author, book_mime
# ls, rm, cp, mkdir, touch, cat

View File

@ -40,6 +40,7 @@ def convert(opts, recipe_arg, notification=None):
c.smart_update(recipe_opts, opts) c.smart_update(recipe_opts, opts)
opts = recipe_opts opts = recipe_opts
opts.chapter_mark = 'none' opts.chapter_mark = 'none'
opts.dont_split_on_page_breaks = True
opf = glob.glob(os.path.join(tdir, '*.opf')) opf = glob.glob(os.path.join(tdir, '*.opf'))
if not opf: if not opf:
raise Exception('Downloading of recipe: %s failed'%recipe_arg) raise Exception('Downloading of recipe: %s failed'%recipe_arg)

View File

@ -38,7 +38,10 @@ def any2lit(opts, path):
os.mkdir(oebdir) os.mkdir(oebdir)
opts.output = os.path.join(tdir, 'dummy.epub') opts.output = os.path.join(tdir, 'dummy.epub')
opts.profile = 'None' opts.profile = 'None'
orig_bfs = opts.base_font_size2
opts.base_font_size2 = 0
any2epub(opts, path, create_epub=False, oeb_cover=True, extract_to=oebdir) any2epub(opts, path, create_epub=False, oeb_cover=True, extract_to=oebdir)
opts.base_font_size2 = orig_bfs
opf = glob.glob(os.path.join(oebdir, '*.opf'))[0] opf = glob.glob(os.path.join(oebdir, '*.opf'))[0]
opts.output = orig_output opts.output = orig_output
logging.getLogger('html2epub').info(_('Creating LIT file from EPUB...')) logging.getLogger('html2epub').info(_('Creating LIT file from EPUB...'))
@ -56,4 +59,4 @@ def main(args=sys.argv):
return 0 return 0
if __name__ == '__main__': if __name__ == '__main__':
sys.exit(main()) sys.exit(main())

View File

@ -143,7 +143,7 @@ def warn(x):
class ReBinary(object): class ReBinary(object):
NSRMAP = {'': None, XML_NS: 'xml'} NSRMAP = {'': None, XML_NS: 'xml'}
def __init__(self, root, path, oeb, map=HTML_MAP): def __init__(self, root, item, oeb, map=HTML_MAP):
self.item = item self.item = item
self.logger = oeb.logger self.logger = oeb.logger
self.manifest = oeb.manifest self.manifest = oeb.manifest
@ -713,7 +713,7 @@ def option_parser():
'-o', '--output', default=None, '-o', '--output', default=None,
help=_('Output file. Default is derived from input filename.')) help=_('Output file. Default is derived from input filename.'))
parser.add_option( parser.add_option(
'--verbose', default=False, action='store_true', '-v', '--verbose', default=0, action='count',
help=_('Useful for debugging.')) help=_('Useful for debugging.'))
return parser return parser
@ -725,7 +725,7 @@ def oeb2lit(opts, inpath):
outpath = os.path.basename(inpath) outpath = os.path.basename(inpath)
outpath = os.path.splitext(outpath)[0] + '.lit' outpath = os.path.splitext(outpath)[0] + '.lit'
outpath = os.path.abspath(outpath) outpath = os.path.abspath(outpath)
context = Context('Firefox', 'MSReader') context = Context('Browser', 'MSReader')
oeb = OEBBook(inpath, logger=logger) oeb = OEBBook(inpath, logger=logger)
tocadder = HTMLTOCAdder() tocadder = HTMLTOCAdder()
tocadder.transform(oeb, context) tocadder.transform(oeb, context)

View File

@ -10,11 +10,6 @@ Based on ideas from comiclrf created by FangornUK.
import os, sys, shutil, traceback, textwrap import os, sys, shutil, traceback, textwrap
from uuid import uuid4 from uuid import uuid4
try:
from reportlab.pdfgen import canvas
_reportlab = True
except:
_reportlab = False
@ -396,10 +391,9 @@ def create_lrf(pages, profile, opts, thumbnail=None):
def create_pdf(pages, profile, opts, thumbnail=None): def create_pdf(pages, profile, opts, thumbnail=None):
width, height = PROFILES[profile] width, height = PROFILES[profile]
if not _reportlab: from reportlab.pdfgen import canvas
raise RuntimeError('Failed to load reportlab')
pdf = canvas.Canvas(filename=opts.output, pagesize=(width,height+15)) pdf = canvas.Canvas(filename=opts.output, pagesize=(width,height+15))
pdf.setAuthor(opts.author) pdf.setAuthor(opts.author)
pdf.setTitle(opts.title) pdf.setTitle(opts.title)

View File

@ -103,8 +103,8 @@ class OCFDirReader(OCFReader):
return open(os.path.join(self.root, path), *args, **kwargs) return open(os.path.join(self.root, path), *args, **kwargs)
class CoverRenderer(QObject): class CoverRenderer(QObject):
WIDTH = 1280 WIDTH = 600
HEIGHT = 1024 HEIGHT = 800
def __init__(self, url, size, loop): def __init__(self, url, size, loop):
QObject.__init__(self) QObject.__init__(self)
@ -113,7 +113,7 @@ class CoverRenderer(QObject):
pal = self.page.palette() pal = self.page.palette()
pal.setBrush(QPalette.Background, Qt.white) pal.setBrush(QPalette.Background, Qt.white)
self.page.setPalette(pal) self.page.setPalette(pal)
self.page.setViewportSize(QSize(600, 800)) self.page.setViewportSize(QSize(self.WIDTH, self.HEIGHT))
self.page.mainFrame().setScrollBarPolicy(Qt.Vertical, Qt.ScrollBarAlwaysOff) self.page.mainFrame().setScrollBarPolicy(Qt.Vertical, Qt.ScrollBarAlwaysOff)
self.page.mainFrame().setScrollBarPolicy(Qt.Horizontal, Qt.ScrollBarAlwaysOff) self.page.mainFrame().setScrollBarPolicy(Qt.Horizontal, Qt.ScrollBarAlwaysOff)
QObject.connect(self.page, SIGNAL('loadFinished(bool)'), self.render_html) QObject.connect(self.page, SIGNAL('loadFinished(bool)'), self.render_html)
@ -126,9 +126,9 @@ class CoverRenderer(QObject):
try: try:
if not ok: if not ok:
return return
size = self.page.mainFrame().contentsSize() #size = self.page.mainFrame().contentsSize()
width, height = fit_image(size.width(), size.height(), self.WIDTH, self.HEIGHT)[1:] #width, height = fit_image(size.width(), size.height(), self.WIDTH, self.HEIGHT)[1:]
self.page.setViewportSize(QSize(width, height)) #self.page.setViewportSize(QSize(width, height))
image = QImage(self.page.viewportSize(), QImage.Format_ARGB32) image = QImage(self.page.viewportSize(), QImage.Format_ARGB32)
image.setDotsPerMeterX(96*(100/2.54)) image.setDotsPerMeterX(96*(100/2.54))
image.setDotsPerMeterY(96*(100/2.54)) image.setDotsPerMeterY(96*(100/2.54))

View File

@ -42,7 +42,11 @@ def any2mobi(opts, path):
os.mkdir(oebdir) os.mkdir(oebdir)
opts.output = os.path.join(tdir, 'dummy.epub') opts.output = os.path.join(tdir, 'dummy.epub')
opts.profile = 'None' opts.profile = 'None'
opts.dont_split_on_page_breaks = True
orig_bfs = opts.base_font_size2
opts.base_font_size2 = 0
any2epub(opts, path, create_epub=False, oeb_cover=True, extract_to=oebdir) any2epub(opts, path, create_epub=False, oeb_cover=True, extract_to=oebdir)
opts.base_font_size2 = orig_bfs
opf = glob.glob(os.path.join(oebdir, '*.opf'))[0] opf = glob.glob(os.path.join(oebdir, '*.opf'))[0]
opts.output = orig_output opts.output = orig_output
logging.getLogger('html2epub').info(_('Creating Mobipocket file from EPUB...')) logging.getLogger('html2epub').info(_('Creating Mobipocket file from EPUB...'))

View File

@ -267,6 +267,12 @@ class MobiReader(object):
'xx-large' : '6', 'xx-large' : '6',
} }
for tag in root.iter(etree.Element): for tag in root.iter(etree.Element):
if tag.tag in ('country-region', 'place', 'placetype', 'placename',
'state', 'city'):
tag.tag = 'span'
for key in tag.attrib.keys():
tag.attrib.pop(key)
continue
styles, attrib = [], tag.attrib styles, attrib = [], tag.attrib
if attrib.has_key('style'): if attrib.has_key('style'):
style = attrib.pop('style').strip() style = attrib.pop('style').strip()
@ -466,16 +472,28 @@ class MobiReader(object):
def get_metadata(stream): def get_metadata(stream):
mr = MobiReader(stream) mr = MobiReader(stream)
if mr.book_header.exth is None: if mr.book_header.exth is None:
mi = MetaInformation(mr.name, ['Unknown']) mi = MetaInformation(mr.name, [_('Unknown')])
else: else:
tdir = tempfile.mkdtemp('_mobi_meta', __appname__) tdir = tempfile.mkdtemp('_mobi_meta', __appname__+'_')
atexit.register(shutil.rmtree, tdir) atexit.register(shutil.rmtree, tdir)
mr.extract_images([], tdir) mr.extract_images([], tdir)
mi = mr.create_opf('dummy.html') mi = mr.create_opf('dummy.html')
if mi.cover: if mi.cover:
cover = os.path.join(tdir, mi.cover) cover = os.path.join(tdir, mi.cover)
if not os.access(cover, os.R_OK):
fname = os.path.basename(cover)
match = re.match(r'(\d+)(.+)', fname)
if match:
num, ext = int(match.group(1), 10), match.group(2)
while num > 0:
num -= 1
candidate = os.path.join(os.path.dirname(cover), '%05d%s'%(num, ext))
if os.access(candidate, os.R_OK):
cover = candidate
break
if os.access(cover, os.R_OK): if os.access(cover, os.R_OK):
mi.cover_data = ('JPEG', open(os.path.join(tdir, mi.cover), 'rb').read()) mi.cover_data = ('JPEG', open(os.path.join(tdir, cover), 'rb').read())
else: else:
path = os.path.join(tdir, 'images', '00001.jpg') path = os.path.join(tdir, 'images', '00001.jpg')
if os.access(path, os.R_OK): if os.access(path, os.R_OK):

View File

@ -393,7 +393,7 @@ class MobiWriter(object):
return data return data
def _generate_images(self): def _generate_images(self):
self._oeb.logger.warn('Serializing images...') self._oeb.logger.info('Serializing images...')
images = [(index, href) for href, index in self._images.items()] images = [(index, href) for href, index in self._images.items()]
images.sort() images.sort()
metadata = self._oeb.metadata metadata = self._oeb.metadata
@ -502,7 +502,8 @@ def add_mobi_options(parser):
_('Mobipocket-specific options.')) _('Mobipocket-specific options.'))
group.add_option( group.add_option(
'-c', '--compress', default=False, action='store_true', '-c', '--compress', default=False, action='store_true',
help=_('Compress file text using PalmDOC compression.')) help=_('Compress file text using PalmDOC compression. '
'Results in smaller files, but takes a long time to run.'))
group.add_option( group.add_option(
'-r', '--rescale-images', default=False, action='store_true', '-r', '--rescale-images', default=False, action='store_true',
help=_('Modify images to meet Palm device size limitations.')) help=_('Modify images to meet Palm device size limitations.'))
@ -525,7 +526,7 @@ def option_parser():
'-o', '--output', default=None, '-o', '--output', default=None,
help=_('Output file. Default is derived from input filename.')) help=_('Output file. Default is derived from input filename.'))
parser.add_option( parser.add_option(
'-v', '--verbose', default=False, action='store_true', '-v', '--verbose', default=0, action='count',
help=_('Useful for debugging.')) help=_('Useful for debugging.'))
add_mobi_options(parser) add_mobi_options(parser)
return parser return parser

View File

@ -0,0 +1,48 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en'
''''''
from PyQt4.Qt import QDialog, SIGNAL, Qt
from calibre.gui2.dialogs.progress_ui import Ui_Dialog
class ProgressDialog(QDialog, Ui_Dialog):
def __init__(self, title, msg='', min=0, max=99, parent=None):
QDialog.__init__(self, parent)
self.setupUi(self)
self.setWindowTitle(title)
self.title.setText(title)
self.message.setText(msg)
self.setWindowModality(Qt.ApplicationModal)
self.set_min(min)
self.set_max(max)
self.canceled = False
self.connect(self.button_box, SIGNAL('rejected()'), self._canceled)
def set_msg(self, msg=''):
self.message.setText(msg)
def set_value(self, val):
self.bar.setValue(val)
def set_min(self, min):
self.bar.setMinimum(min)
def set_max(self, max):
self.bar.setMaximum(max)
def _canceled(self, *args):
self.canceled = True
self.button_box.setDisabled(True)
self.title.setText(_('Aborting...'))
def keyPressEvent(self, ev):
if ev.key() == Qt.Key_Escape:
self._canceled()
else:
QDialog.keyPressEvent(self, ev)

View File

@ -0,0 +1,72 @@
<ui version="4.0" >
<class>Dialog</class>
<widget class="QDialog" name="Dialog" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>712</width>
<height>308</height>
</rect>
</property>
<property name="windowTitle" >
<string>Dialog</string>
</property>
<property name="windowIcon" >
<iconset resource="../images.qrc" >
<normaloff>:/images/jobs.svg</normaloff>:/images/jobs.svg</iconset>
</property>
<layout class="QGridLayout" name="gridLayout" >
<item row="0" column="0" >
<widget class="QLabel" name="title" >
<property name="font" >
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text" >
<string>TextLabel</string>
</property>
<property name="alignment" >
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap" >
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0" >
<widget class="QProgressBar" name="bar" >
<property name="value" >
<number>0</number>
</property>
</widget>
</item>
<item row="2" column="0" >
<widget class="QLabel" name="message" >
<property name="text" >
<string>TextLabel</string>
</property>
<property name="alignment" >
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap" >
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0" >
<widget class="QDialogButtonBox" name="button_box" >
<property name="standardButtons" >
<set>QDialogButtonBox::Abort</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="../images.qrc" />
</resources>
<connections/>
</ui>

Binary file not shown.

After

Width:  |  Height:  |  Size: 508 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 805 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 383 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 636 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 425 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 414 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 901 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 592 B

View File

@ -198,13 +198,18 @@ class BooksModel(QAbstractTableModel):
''' Return list indices of all cells in index.row()''' ''' Return list indices of all cells in index.row()'''
return [ self.index(index.row(), c) for c in range(self.columnCount(None))] return [ self.index(index.row(), c) for c in range(self.columnCount(None))]
def save_to_disk(self, rows, path, single_dir=False, single_format=None): def save_to_disk(self, rows, path, single_dir=False, single_format=None,
callback=None):
rows = [row.row() for row in rows] rows = [row.row() for row in rows]
if single_format is None: if single_format is None:
return self.db.export_to_dir(path, rows, self.sorted_on[0] == 'authors', return self.db.export_to_dir(path, rows,
single_dir=single_dir) self.sorted_on[0] == 'authors',
single_dir=single_dir,
callback=callback)
else: else:
return self.db.export_single_format_to_dir(path, rows, single_format) return self.db.export_single_format_to_dir(path, rows,
single_format,
callback=callback)
def delete_books(self, indices): def delete_books(self, indices):

View File

@ -28,6 +28,7 @@ from calibre.gui2.cover_flow import CoverFlow, DatabaseImages, pictureflowerror
from calibre.library.database import LibraryDatabase from calibre.library.database import LibraryDatabase
from calibre.gui2.dialogs.scheduler import Scheduler from calibre.gui2.dialogs.scheduler import Scheduler
from calibre.gui2.update import CheckForUpdates from calibre.gui2.update import CheckForUpdates
from calibre.gui2.dialogs.progress import ProgressDialog
from calibre.gui2.main_window import MainWindow, option_parser as _option_parser from calibre.gui2.main_window import MainWindow, option_parser as _option_parser
from calibre.gui2.main_ui import Ui_MainWindow from calibre.gui2.main_ui import Ui_MainWindow
from calibre.gui2.device import DeviceManager from calibre.gui2.device import DeviceManager
@ -339,6 +340,8 @@ class Main(MainWindow, Ui_MainWindow):
self.connect(self.search, SIGNAL('search(PyQt_PyObject, PyQt_PyObject)'), self.connect(self.search, SIGNAL('search(PyQt_PyObject, PyQt_PyObject)'),
self.tags_view.model().reinit) self.tags_view.model().reinit)
self.connect(self.library_view.model(), SIGNAL('count_changed(int)'), self.location_view.count_changed) self.connect(self.library_view.model(), SIGNAL('count_changed(int)'), self.location_view.count_changed)
self.connect(self.library_view.model(), SIGNAL('count_changed(int)'),
self.tags_view.recount)
self.library_view.model().count_changed() self.library_view.model().count_changed()
########################### Cover Flow ################################ ########################### Cover Flow ################################
self.cover_flow = None self.cover_flow = None
@ -598,29 +601,26 @@ class Main(MainWindow, Ui_MainWindow):
root = choose_dir(self, 'recursive book import root dir dialog', 'Select root folder') root = choose_dir(self, 'recursive book import root dir dialog', 'Select root folder')
if not root: if not root:
return return
progress = QProgressDialog('', '&'+_('Stop'), progress = ProgressDialog(_('Adding books recursively...'),
0, 0, self) min=0, max=0, parent=self)
progress.setWindowModality(Qt.ApplicationModal)
progress.setWindowTitle(_('Adding books recursively...'))
progress.show() progress.show()
def callback(msg): def callback(msg):
if msg != '.': if msg != '.':
progress.setLabelText((_('Added ')+msg) if msg else _('Searching...')) progress.set_msg((_('Added ')+msg) if msg else _('Searching...'))
stop = progress.wasCanceled()
QApplication.processEvents() QApplication.processEvents()
QApplication.sendPostedEvents() QApplication.sendPostedEvents()
QApplication.flush() QApplication.flush()
return stop return progress.canceled
try: try:
duplicates = self.library_view.model().db.recursive_import(root, single, callback=callback) duplicates = self.library_view.model().db.recursive_import(root, single, callback=callback)
finally: finally:
progress.hide() progress.hide()
progress.close()
if duplicates: if duplicates:
files = _('<p>Books with the same title as the following already exist in the database. Add them anyway?<ul>') files = _('<p>Books with the same title as the following already exist in the database. Add them anyway?<ul>')
for mi, formats in duplicates: for mi, formats in duplicates:
files += '<li>'+mi.title+'</li>\n' files += '<li>'+mi.title+'</li>\n'
d = WarningDialog(_('Duplicates found!'), _('Duplicates found!'), files+'</ul></p>', self) d = WarningDialog(_('Duplicates found!'), _('Duplicates found!'),
files+'</ul></p>', self)
if d.exec_() == QDialog.Accepted: if d.exec_() == QDialog.Accepted:
for mi, formats in duplicates: for mi, formats in duplicates:
self.library_view.model().db.import_book(mi, formats ) self.library_view.model().db.import_book(mi, formats )
@ -686,15 +686,13 @@ class Main(MainWindow, Ui_MainWindow):
return return
# Get format and metadata information # Get format and metadata information
formats, metadata, names, infos = [], [], [], [] formats, metadata, names, infos = [], [], [], []
progress = QProgressDialog(_('Reading metadata...'), _('Stop'), 0, len(paths), self) progress = ProgressDialog(_('Adding books...'), _('Reading metadata...'),
progress.setWindowTitle(_('Adding books...')) min=0, max=len(paths), parent=self)
progress.setWindowModality(Qt.ApplicationModal)
progress.setLabelText(_('Reading metadata...'))
progress.show() progress.show()
try: try:
for c, book in enumerate(paths): for c, book in enumerate(paths):
progress.setValue(c) progress.set_value(c)
if progress.wasCanceled(): if progress.canceled:
return return
format = os.path.splitext(book)[1] format = os.path.splitext(book)[1]
format = format[1:] if format else None format = format[1:] if format else None
@ -713,15 +711,14 @@ class Main(MainWindow, Ui_MainWindow):
infos.append({'title':mi.title, 'authors':', '.join(mi.authors), infos.append({'title':mi.title, 'authors':', '.join(mi.authors),
'cover':self.default_thumbnail, 'tags':[]}) 'cover':self.default_thumbnail, 'tags':[]})
title = mi.title if isinstance(mi.title, unicode) else mi.title.decode(preferred_encoding, 'replace') title = mi.title if isinstance(mi.title, unicode) else mi.title.decode(preferred_encoding, 'replace')
progress.setLabelText(_('Read metadata from ')+title) progress.set_msg(_('Read metadata from ')+title)
if not to_device: if not to_device:
progress.setLabelText(_('Adding books to database...')) progress.set_msg(_('Adding books to database...'))
model = self.library_view.model() model = self.library_view.model()
paths = list(paths) paths = list(paths)
duplicates, number_added = model.add_books(paths, formats, metadata) duplicates, number_added = model.add_books(paths, formats, metadata)
progress.cancel()
if duplicates: if duplicates:
files = _('<p>Books with the same title as the following already exist in the database. Add them anyway?<ul>') files = _('<p>Books with the same title as the following already exist in the database. Add them anyway?<ul>')
for mi in duplicates[2]: for mi in duplicates[2]:
@ -734,9 +731,7 @@ class Main(MainWindow, Ui_MainWindow):
else: else:
self.upload_books(paths, list(map(sanitize_file_name, names)), infos, on_card=on_card) self.upload_books(paths, list(map(sanitize_file_name, names)), infos, on_card=on_card)
finally: finally:
progress.setValue(progress.maximum())
progress.hide() progress.hide()
progress.close()
def upload_books(self, files, names, metadata, on_card=False, memory=None): def upload_books(self, files, names, metadata, on_card=False, memory=None):
''' '''
@ -979,28 +974,49 @@ class Main(MainWindow, Ui_MainWindow):
self.save_to_disk(checked, True) self.save_to_disk(checked, True)
def save_to_disk(self, checked, single_dir=False, single_format=None): def save_to_disk(self, checked, single_dir=False, single_format=None):
rows = self.current_view().selectionModel().selectedRows() rows = self.current_view().selectionModel().selectedRows()
if not rows or len(rows) == 0: if not rows or len(rows) == 0:
d = error_dialog(self, _('Cannot save to disk'), _('No books selected')) d = error_dialog(self, _('Cannot save to disk'), _('No books selected'))
d.exec_() d.exec_()
return return
progress = ProgressDialog(_('Saving to disk...'), min=0, max=len(rows),
parent=self)
def callback(count, msg):
progress.set_value(count)
progress.set_msg(_('Saved')+' '+msg)
QApplication.processEvents()
QApplication.sendPostedEvents()
QApplication.flush()
return not progress.canceled
dir = choose_dir(self, 'save to disk dialog', _('Choose destination directory')) dir = choose_dir(self, 'save to disk dialog', _('Choose destination directory'))
if not dir: if not dir:
return return
if self.current_view() == self.library_view:
failures = self.current_view().model().save_to_disk(rows, dir, progress.show()
single_dir=single_dir, single_format=single_format) QApplication.processEvents()
if failures and single_format is not None: QApplication.sendPostedEvents()
msg = _('<p>Could not save the following books to disk, because the %s format is not available for them:<ul>')%single_format.upper() QApplication.flush()
for f in failures: try:
msg += '<li>%s</li>'%f[1] if self.current_view() == self.library_view:
msg += '</ul>' failures = self.current_view().model().save_to_disk(rows, dir,
warning_dialog(self, _('Could not save some ebooks'), msg).exec_() single_dir=single_dir, callback=callback,
QDesktopServices.openUrl(QUrl('file:'+dir)) single_format=single_format)
else: if failures and single_format is not None:
paths = self.current_view().model().paths(rows) msg = _('<p>Could not save the following books to disk, because the %s format is not available for them:<ul>')%single_format.upper()
self.device_manager.save_books(Dispatcher(self.books_saved), paths, dir) for f in failures:
msg += '<li>%s</li>'%f[1]
msg += '</ul>'
warning_dialog(self, _('Could not save some ebooks'), msg).exec_()
QDesktopServices.openUrl(QUrl('file:'+dir))
else:
paths = self.current_view().model().paths(rows)
self.device_manager.save_books(Dispatcher(self.books_saved), paths, dir)
finally:
progress.hide()
def books_saved(self, job): def books_saved(self, job):
if job.exception is not None: if job.exception is not None:
@ -1115,6 +1131,7 @@ class Main(MainWindow, Ui_MainWindow):
os.remove(f.name) os.remove(f.name)
except: except:
pass pass
self.tags_view.recount()
if self.current_view() is self.library_view: if self.current_view() is self.library_view:
current = self.library_view.currentIndex() current = self.library_view.currentIndex()
self.library_view.model().current_changed(current, QModelIndex()) self.library_view.model().current_changed(current, QModelIndex())

View File

@ -7,7 +7,7 @@ __docformat__ = 'restructuredtext en'
Browsing book collection by tags. Browsing book collection by tags.
''' '''
from PyQt4.Qt import QAbstractItemModel, Qt, QVariant, QTreeView, QModelIndex, \ from PyQt4.Qt import QAbstractItemModel, Qt, QVariant, QTreeView, QModelIndex, \
QFont, SIGNAL, QSize, QColor, QIcon QFont, SIGNAL, QSize, QColor, QIcon, QPoint
from calibre.gui2 import config from calibre.gui2 import config
NONE = QVariant() NONE = QVariant()
@ -36,6 +36,14 @@ class TagsView(QTreeView):
if self._model.toggle(index): if self._model.toggle(index):
self.emit(SIGNAL('tags_marked(PyQt_PyObject, PyQt_PyObject)'), self.emit(SIGNAL('tags_marked(PyQt_PyObject, PyQt_PyObject)'),
self._model.tokens(), self.match_all.isChecked()) self._model.tokens(), self.match_all.isChecked())
def recount(self, *args):
ci = self.currentIndex()
if not ci.isValid():
ci = self.indexAt(QPoint(10, 10))
self.model().refresh()
if ci.isValid():
self.scrollTo(ci, QTreeView.PositionAtTop)
class TagsModel(QAbstractItemModel): class TagsModel(QAbstractItemModel):

View File

@ -1390,10 +1390,11 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE;
return [i[0] for i in self.conn.get('SELECT id FROM books')] return [i[0] for i in self.conn.get('SELECT id FROM books')]
def export_to_dir(self, dir, indices, byauthor=False, single_dir=False, def export_to_dir(self, dir, indices, byauthor=False, single_dir=False,
index_is_id=False): index_is_id=False, callback=None):
if not os.path.exists(dir): if not os.path.exists(dir):
raise IOError('Target directory does not exist: '+dir) raise IOError('Target directory does not exist: '+dir)
by_author = {} by_author = {}
count = 0
for index in indices: for index in indices:
id = index if index_is_id else self.id(index) id = index if index_is_id else self.id(index)
au = self.conn.get('SELECT author_sort FROM books WHERE id=?', au = self.conn.get('SELECT author_sort FROM books WHERE id=?',
@ -1403,8 +1404,6 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE;
if not au: if not au:
au = _('Unknown') au = _('Unknown')
au = au.split(',')[0] au = au.split(',')[0]
else:
au = au.replace(',', ';')
if not by_author.has_key(au): if not by_author.has_key(au):
by_author[au] = [] by_author[au] = []
by_author[au].append(index) by_author[au].append(index)
@ -1456,6 +1455,11 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE;
print 'Error setting metadata for book:', mi.title print 'Error setting metadata for book:', mi.title
traceback.print_exc() traceback.print_exc()
f.close() f.close()
count += 1
if callable(callback):
if not callback(count, mi.title):
return
def import_book(self, mi, formats): def import_book(self, mi, formats):
@ -1569,12 +1573,13 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE;
return duplicates return duplicates
def export_single_format_to_dir(self, dir, indices, format, index_is_id=False): def export_single_format_to_dir(self, dir, indices, format,
index_is_id=False, callback=None):
dir = os.path.abspath(dir) dir = os.path.abspath(dir)
if not index_is_id: if not index_is_id:
indices = map(self.id, indices) indices = map(self.id, indices)
failures = [] failures = []
for id in indices: for count, id in enumerate(indices):
try: try:
data = self.format(id, format, index_is_id=True) data = self.format(id, format, index_is_id=True)
if not data: if not data:
@ -1599,6 +1604,9 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE;
except: except:
pass pass
f.close() f.close()
if callable(callback):
if not callback(count, title):
break
return failures return failures

View File

@ -192,6 +192,8 @@ def setup_completion(fatal_errors):
from calibre.ebooks.epub.from_any import option_parser as any2epub from calibre.ebooks.epub.from_any import option_parser as any2epub
from calibre.ebooks.lit.from_any import option_parser as any2lit from calibre.ebooks.lit.from_any import option_parser as any2lit
from calibre.ebooks.epub.from_comic import option_parser as comic2epub from calibre.ebooks.epub.from_comic import option_parser as comic2epub
from calibre.ebooks.mobi.from_any import option_parser as any2mobi
from calibre.ebooks.mobi.writer import option_parser as oeb2mobi
from calibre.gui2.main import option_parser as guiop from calibre.gui2.main import option_parser as guiop
any_formats = ['epub', 'htm', 'html', 'xhtml', 'xhtm', 'rar', 'zip', any_formats = ['epub', 'htm', 'html', 'xhtml', 'xhtm', 'rar', 'zip',
'txt', 'lit', 'rtf', 'pdf', 'prc', 'mobi', 'fb2', 'odt'] 'txt', 'lit', 'rtf', 'pdf', 'prc', 'mobi', 'fb2', 'odt']
@ -216,6 +218,8 @@ def setup_completion(fatal_errors):
f.write(opts_and_exts('calibre', guiop, any_formats)) f.write(opts_and_exts('calibre', guiop, any_formats))
f.write(opts_and_exts('any2epub', any2epub, any_formats)) f.write(opts_and_exts('any2epub', any2epub, any_formats))
f.write(opts_and_exts('any2lit', any2lit, any_formats)) f.write(opts_and_exts('any2lit', any2lit, any_formats))
f.write(opts_and_exts('any2mobi', any2mobi, any_formats))
f.write(opts_and_exts('oeb2mobi', oeb2mobi, ['mobi', 'prc']))
f.write(opts_and_exts('lrf2lrs', lrf2lrsop, ['lrf'])) f.write(opts_and_exts('lrf2lrs', lrf2lrsop, ['lrf']))
f.write(opts_and_exts('lrf-meta', metaop, ['lrf'])) f.write(opts_and_exts('lrf-meta', metaop, ['lrf']))
f.write(opts_and_exts('rtf-meta', metaop, ['rtf'])) f.write(opts_and_exts('rtf-meta', metaop, ['rtf']))
@ -232,7 +236,7 @@ def setup_completion(fatal_errors):
f.write(opts_and_exts('lit2oeb', lit2oeb, ['lit'])) f.write(opts_and_exts('lit2oeb', lit2oeb, ['lit']))
f.write(opts_and_exts('comic2lrf', comicop, ['cbz', 'cbr'])) f.write(opts_and_exts('comic2lrf', comicop, ['cbz', 'cbr']))
f.write(opts_and_exts('comic2epub', comic2epub, ['cbz', 'cbr'])) f.write(opts_and_exts('comic2epub', comic2epub, ['cbz', 'cbr']))
f.write(opts_and_exts('comic2pdf', comic2epub, ['cbz', 'cbr'])) f.write(opts_and_exts('comic2pdf', comic2epub, ['cbz', 'cbr']))
f.write(opts_and_words('feeds2disk', feeds2disk, feed_titles)) f.write(opts_and_words('feeds2disk', feeds2disk, feed_titles))
f.write(opts_and_words('feeds2lrf', feeds2lrf, feed_titles)) f.write(opts_and_words('feeds2lrf', feeds2lrf, feed_titles))
f.write(opts_and_words('feeds2lrf', feeds2epub, feed_titles)) f.write(opts_and_words('feeds2lrf', feeds2epub, feed_titles))

View File

@ -38,7 +38,6 @@ class cmd_commit(_cmd_commit):
print attributes['summary'] print attributes['summary']
return attributes['summary'] return attributes['summary']
def expand_bug(self, msg, nick, config, bug_tracker, type='trac'): def expand_bug(self, msg, nick, config, bug_tracker, type='trac'):
prefix = '%s_%s_'%(type, nick) prefix = '%s_%s_'%(type, nick)
username = config.get_user_option(prefix+'username') username = config.get_user_option(prefix+'username')

View File

@ -22,7 +22,7 @@ recipe_modules = ['recipe_' + r for r in (
'time_magazine', 'endgadget', 'fudzilla', 'nspm_int', 'nspm', 'pescanik', 'time_magazine', 'endgadget', 'fudzilla', 'nspm_int', 'nspm', 'pescanik',
'spiegel_int', 'themarketticker', 'tomshardware', 'xkcd', 'ftd', 'zdnet', 'spiegel_int', 'themarketticker', 'tomshardware', 'xkcd', 'ftd', 'zdnet',
'joelonsoftware', 'telepolis', 'common_dreams', 'nin', 'tomshardware_de', 'joelonsoftware', 'telepolis', 'common_dreams', 'nin', 'tomshardware_de',
'pagina12', 'infobae', 'ambito', 'elargentino', 'sueddeutsche',
)] )]
import re, imp, inspect, time, os import re, imp, inspect, time, os

View File

@ -0,0 +1,44 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2008, Darko Miletic <darko.miletic at gmail.com>'
'''
ambito.com
'''
from calibre.web.feeds.news import BasicNewsRecipe
class Ambito(BasicNewsRecipe):
title = 'Ambito.com'
__author__ = 'Darko Miletic'
description = 'Informacion Libre las 24 horas'
oldest_article = 2
max_articles_per_feed = 100
no_stylesheets = True
use_embedded_content = False
encoding = 'iso--8859-1'
cover_url = 'http://www.ambito.com/img/logo_.jpg'
html2lrf_options = [
'--comment' , description
, '--category' , 'news, Argentina'
, '--publisher' , title
]
feeds = [
(u'Principales Noticias', u'http://www.ambito.com/rss/noticiasp.asp' )
,(u'Economia' , u'http://www.ambito.com/rss/noticias.asp?S=Econom%EDa' )
,(u'Politica' , u'http://www.ambito.com/rss/noticias.asp?S=Pol%EDtica' )
,(u'Informacion General' , u'http://www.ambito.com/rss/noticias.asp?S=Informaci%F3n%20General')
,(u'Agro' , u'http://www.ambito.com/rss/noticias.asp?S=Agro' )
,(u'Internacionales' , u'http://www.ambito.com/rss/noticias.asp?S=Internacionales' )
,(u'Deportes' , u'http://www.ambito.com/rss/noticias.asp?S=Deportes' )
,(u'Espectaculos' , u'http://www.ambito.com/rss/noticias.asp?S=Espect%E1culos' )
,(u'Tecnologia' , u'http://www.ambito.com/rss/noticias.asp?S=Tecnologia' )
,(u'Salud' , u'http://www.ambito.com/rss/noticias.asp?S=Salud' )
,(u'Ambito Nacional' , u'http://www.ambito.com/rss/noticias.asp?S=Ambito%20Nacional' )
]
def print_version(self, url):
return url.replace('http://www.ambito.com/noticia.asp?','http://www.ambito.com/noticias/imprimir.asp?')

View File

@ -6,6 +6,7 @@ __copyright__ = '2008, Darko Miletic <darko.miletic at gmail.com>'
clarin.com clarin.com
''' '''
from calibre import strftime
from calibre.web.feeds.news import BasicNewsRecipe from calibre.web.feeds.news import BasicNewsRecipe
class Clarin(BasicNewsRecipe): class Clarin(BasicNewsRecipe):
@ -15,33 +16,35 @@ class Clarin(BasicNewsRecipe):
oldest_article = 2 oldest_article = 2
max_articles_per_feed = 100 max_articles_per_feed = 100
use_embedded_content = False use_embedded_content = False
cover_url = 'http://www.clarin.com/shared/v10/img/Hd/lg_Clarin.gif' no_stylesheets = True
cover_url = strftime('http://www.clarin.com/diario/%Y/%m/%d/portada.jpg')
html2lrf_options = [ html2lrf_options = [
'--comment', description '--comment', description
, '--base-font-size', '10' , '--base-font-size', '10'
, '--category', 'news, Argentina' , '--category', 'news, Argentina'
, '--publisher', 'Grupo Clarin' , '--publisher', 'Grupo Clarin'
] ]
remove_tags = [ remove_tags = [
dict(name='a' , attrs={'class':'Imp' }) dict(name='a' , attrs={'class':'Imp' })
,dict(name='div' , attrs={'class':'Perma' }) ,dict(name='div' , attrs={'class':'Perma' })
,dict(name='h1' , text='Imprimir' ) ,dict(name='h1' , text='Imprimir' )
] ]
feeds = [ feeds = [
(u'Ultimo Momento', u'http://www.clarin.com/diario/hoy/um/sumariorss.xml') (u'Ultimo Momento', u'http://www.clarin.com/diario/hoy/um/sumariorss.xml')
,(u'El Pais' , u'http://www.clarin.com/diario/hoy/elpais.xml' ) ,(u'El Pais' , u'http://www.clarin.com/diario/hoy/elpais.xml' )
,(u'Opinion' , u'http://www.clarin.com/diario/hoy/opinion.xml' ) ,(u'Opinion' , u'http://www.clarin.com/diario/hoy/opinion.xml' )
,(u'El Mundo' , u'http://www.clarin.com/diario/hoy/elmundo.xml' ) ,(u'El Mundo' , u'http://www.clarin.com/diario/hoy/elmundo.xml' )
,(u'Sociedad' , u'http://www.clarin.com/diario/hoy/sociedad.xml' ) ,(u'Sociedad' , u'http://www.clarin.com/diario/hoy/sociedad.xml' )
,(u'La Ciudad' , u'http://www.clarin.com/diario/hoy/laciudad.xml' ) ,(u'La Ciudad' , u'http://www.clarin.com/diario/hoy/laciudad.xml' )
,(u'Policiales' , u'http://www.clarin.com/diario/hoy/policiales.xml' ) ,(u'Policiales' , u'http://www.clarin.com/diario/hoy/policiales.xml' )
,(u'Deportes' , u'http://www.clarin.com/diario/hoy/deportes.xml' ) ,(u'Deportes' , u'http://www.clarin.com/diario/hoy/deportes.xml' )
] ]
def get_article_url(self, article): def get_article_url(self, article):
artl = article.get('link', None) artl = article.get('link', None)
rest = artl.partition('-0')[-1] rest = artl.partition('-0')[-1]
lmain = rest.partition('.')[0] lmain = rest.partition('.')[0]
return 'http://www.servicios.clarin.com/notas/jsp/clarin/v9/notas/imprimir.jsp?pagid=' + lmain return 'http://www.servicios.clarin.com/notas/jsp/clarin/v9/notas/imprimir.jsp?pagid=' + lmain

View File

@ -0,0 +1,55 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2008, Darko Miletic <darko.miletic at gmail.com>'
'''
elargentino.com
'''
from calibre.web.feeds.news import BasicNewsRecipe
class ElArgentino(BasicNewsRecipe):
title = 'ElArgentino.com'
__author__ = 'Darko Miletic'
description = 'Informacion Libre las 24 horas'
oldest_article = 2
max_articles_per_feed = 100
no_stylesheets = True
use_embedded_content = False
encoding = 'utf8'
cover_url = 'http://www.elargentino.com/TemplateWeb/MediosFooter/tapa_elargentino.png'
html2lrf_options = [
'--comment' , description
, '--category' , 'news, Argentina'
, '--publisher' , 'ElArgentino.com'
]
remove_tags = [
dict(name='div', attrs={'id':'noprint' })
,dict(name='div', attrs={'class':'encabezadoImprimir'})
,dict(name='a' , attrs={'target':'_blank' })
]
feeds = [
(u'Portada' , u'http://www.elargentino.com/Highlights.aspx?Content-Type=text/xml&ChannelDesc=Home' )
,(u'Pais' , u'http://www.elargentino.com/Highlights.aspx?ParentType=Section&ParentId=112&Content-Type=text/xml&ChannelDesc=Pa%C3%ADs' )
,(u'Economia' , u'http://www.elargentino.com/Highlights.aspx?ParentType=Section&ParentId=107&Content-Type=text/xml&ChannelDesc=Econom%C3%ADa' )
,(u'Mundo' , u'http://www.elargentino.com/Highlights.aspx?ParentType=Section&ParentId=113&Content-Type=text/xml&ChannelDesc=Mundo' )
,(u'Tecnologia' , u'http://www.elargentino.com/Highlights.aspx?ParentType=Section&ParentId=118&Content-Type=text/xml&ChannelDesc=Tecnolog%C3%ADa' )
,(u'Espectaculos', u'http://www.elargentino.com/Highlights.aspx?ParentType=Section&ParentId=114&Content-Type=text/xml&ChannelDesc=Espect%C3%A1culos')
,(u'Deportes' , u'http://www.elargentino.com/Highlights.aspx?ParentType=Section&ParentId=106&Content-Type=text/xml&ChannelDesc=Deportes' )
,(u'Sociedad' , u'http://www.elargentino.com/Highlights.aspx?ParentType=Section&ParentId=109&Content-Type=text/xml&ChannelDesc=Sociedad' )
,(u'Entrevistas' , u'http://www.elargentino.com/Highlights.aspx?ParentType=Section&ParentId=115&Content-Type=text/xml&ChannelDesc=Entrevistas' )
]
def print_version(self, url):
main, sep, article_part = url.partition('/nota-')
article_id, rsep, rrest = article_part.partition('-')
return u'http://www.elargentino.com/Impresion.aspx?Id=' + article_id
def preprocess_html(self, soup):
mtag = '<meta http-equiv="Content-Type" content="text/html; charset=utf-8">'
soup.head.insert(0,mtag)
soup.prettify()
return soup

View File

@ -0,0 +1,40 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2008, Darko Miletic <darko.miletic at gmail.com>'
'''
infobae.com
'''
from calibre.web.feeds.news import BasicNewsRecipe
class Infobae(BasicNewsRecipe):
title = 'Infobae.com'
__author__ = 'Darko Miletic'
description = 'Informacion Libre las 24 horas'
oldest_article = 2
max_articles_per_feed = 100
no_stylesheets = True
use_embedded_content = False
encoding = 'iso-8859-1'
cover_url = 'http://www.infobae.com/imgs/header/header.gif'
html2lrf_options = [
'--comment' , description
, '--category' , 'news, Argentina'
, '--publisher' , 'Infobae.com'
]
feeds = [
(u'Noticias' , u'http://www.infobae.com/adjuntos/html/RSS/hoy.xml' )
,(u'Salud' , u'http://www.infobae.com/adjuntos/html/RSS/salud.xml' )
,(u'Tecnologia', u'http://www.infobae.com/adjuntos/html/RSS/tecnologia.xml')
,(u'Deportes' , u'http://www.infobae.com/adjuntos/html/RSS/deportes.xml' )
]
def print_version(self, url):
main, sep, article_part = url.partition('contenidos/')
article_id, rsep, rrest = article_part.partition('-')
return u'http://www.infobae.com/notas/nota_imprimir.php?Idx=' + article_id

View File

@ -0,0 +1,39 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2008, Darko Miletic <darko.miletic at gmail.com>'
'''
pagina12.com.ar
'''
from calibre import strftime
from calibre.web.feeds.news import BasicNewsRecipe
class Pagina12(BasicNewsRecipe):
title = u'Pagina/12'
__author__ = 'Darko Miletic'
description = 'Noticias de Argentina y el resto del mundo'
oldest_article = 2
max_articles_per_feed = 100
no_stylesheets = True
use_embedded_content = False
encoding = 'cp1252'
cover_url = strftime('http://www.pagina12.com.ar/fotos/%Y%m%d/diario/TAPAN.jpg')
html2lrf_options = [
'--comment' , description
, '--category' , 'news, Argentina'
, '--publisher' , 'La Pagina S.A.'
]
remove_tags = [
dict(name='div', attrs={'id':'volver'})
,dict(name='div', attrs={'id':'logo' })
]
feeds = [(u'Pagina/12', u'http://www.pagina12.com.ar/diario/rss/principal.xml')]
def print_version(self, url):
return url.replace('http://www.pagina12.com.ar/','http://www.pagina12.com.ar/imprimir/')

View File

@ -4,7 +4,7 @@ class SecurityWatch(BasicNewsRecipe):
title = u'securitywatch' title = u'securitywatch'
description = 'security news' description = 'security news'
timefmt = ' [%d %b %Y]' timefmt = ' [%d %b %Y]'
__author__ = 'Oliver' __author__ = 'Oliver Niesner'
no_stylesheets = True no_stylesheets = True
oldest_article = 14 oldest_article = 14
max_articles_per_feed = 100 max_articles_per_feed = 100

View File

@ -0,0 +1,62 @@
__license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
'''
Fetch sueddeutsche.
'''
from calibre.web.feeds.news import BasicNewsRecipe
class Sueddeutsche(BasicNewsRecipe):
title = u'Sueddeutsche'
description = 'News from Germany'
__author__ = 'Oliver Niesner'
use_embedded_content = False
timefmt = ' [%d %b %Y]'
max_articles_per_feed = 40
no_stylesheets = True
encoding = 'latin1'
remove_tags_after = [dict(name='div', attrs={'class':'artikelBox navigatorBox'})]
#dict(name='table', attrs={'class':'bgf2f2f2 absatz print100'})]
remove_tags = [dict(name='div', attrs={'class':'bannerSuperBanner'}),
dict(name='div', attrs={'class':'bannerSky'}),
dict(name='div', attrs={'class':'footerLinks'}),
dict(name='div', attrs={'class':'seitenanfang'}),
dict(name='td', attrs={'class':'mar5'}),
dict(name='table', attrs={'class':'pageAktiv'}),
dict(name='table', attrs={'class':'xartable'}),
dict(name='table', attrs={'class':'wpnavi'}),
dict(name='table', attrs={'class':'bgcontent absatz'}),
dict(name='table', attrs={'class':'footer'}),
dict(name='table', attrs={'class':'artikelBox'}),
dict(name='table', attrs={'class':'kommentare'}),
dict(name='table', attrs={'class':'pageBoxBot'}),
dict(name='div', attrs={'class':'artikelBox navigatorBox'}),
dict(name='div', attrs={'class':'similar-article-box'}),
dict(name='div', attrs={'class':'videoBigHack'}),
dict(name='td', attrs={'class':'artikelDruckenRight'}),
dict(name='span', attrs={'class':'hidePrint'}),
dict(id='headerLBox'),
dict(id='rechteSpalte'),
dict(id='newsticker-list-small'),
dict(id='ntop5'),
dict(id='ntop5send'),
dict(id='ntop5commented'),
dict(id='nnav-bgheader'),
dict(id='nnav-headerteaser'),
dict(id='nnav-head'),
dict(id='nnav-top'),
dict(id='nnav-logodiv'),
dict(id='nnav-logo'),
dict(id='nnav-oly'),
dict(id='readcomment')]
feeds = [ (u'Sueddeutsche', u'http://www.sueddeutsche.de/app/service/rss/alles/rss.xml') ]
def postprocess_html(self, soup, first_fetch):
for t in soup.findAll(['table', 'tr', 'td']):
t.name = 'div'
return soup

View File

@ -395,7 +395,11 @@ class RecursiveFetcher(object, LoggingInterface):
if self.download_stylesheets: if self.download_stylesheets:
self.process_stylesheets(soup, newbaseurl) self.process_stylesheets(soup, newbaseurl)
res = os.path.join(linkdiskpath, basename(iurl)) _fname = basename(iurl)
if not isinstance(_fname, unicode):
_fname.decode('latin1', 'replace')
_fname.encode('ascii', 'replace').replace('%', '')
res = os.path.join(linkdiskpath, _fname)
self.downloaded_paths.append(res) self.downloaded_paths.append(res)
self.filemap[nurl] = res self.filemap[nurl] = res
if recursion_level < self.max_recursions: if recursion_level < self.max_recursions: