Merge upstream changes
11
setup.py
@ -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,
|
||||||
|
@ -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.
|
||||||
|
@ -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)
|
||||||
|
@ -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:
|
||||||
|
48
src/calibre/devices/cybookg3/t2b.py
Normal 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:
|
||||||
|
@ -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
|
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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())
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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))
|
||||||
|
@ -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...'))
|
||||||
|
@ -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):
|
||||||
|
@ -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
|
||||||
|
48
src/calibre/gui2/dialogs/progress.py
Normal 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)
|
72
src/calibre/gui2/dialogs/progress.ui
Normal 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>
|
BIN
src/calibre/gui2/images/news/ambito.png
Normal file
After Width: | Height: | Size: 508 B |
BIN
src/calibre/gui2/images/news/elargentino.png
Normal file
After Width: | Height: | Size: 805 B |
BIN
src/calibre/gui2/images/news/ftd.png
Normal file
After Width: | Height: | Size: 383 B |
BIN
src/calibre/gui2/images/news/heise.png
Normal file
After Width: | Height: | Size: 636 B |
BIN
src/calibre/gui2/images/news/infobae.png
Normal file
After Width: | Height: | Size: 425 B |
BIN
src/calibre/gui2/images/news/pagina12.png
Normal file
After Width: | Height: | Size: 414 B |
BIN
src/calibre/gui2/images/news/security_watch.png
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
src/calibre/gui2/images/news/sueddeutsche.png
Normal file
After Width: | Height: | Size: 901 B |
BIN
src/calibre/gui2/images/news/zdnet.png
Normal file
After Width: | Height: | Size: 592 B |
@ -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):
|
||||||
|
@ -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())
|
||||||
|
@ -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):
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
@ -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))
|
||||||
|
@ -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')
|
||||||
|
@ -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
|
||||||
|
44
src/calibre/web/feeds/recipes/recipe_ambito.py
Normal 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?')
|
@ -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
|
||||||
|
55
src/calibre/web/feeds/recipes/recipe_elargentino.py
Normal 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
|
40
src/calibre/web/feeds/recipes/recipe_infobae.py
Normal 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
|
39
src/calibre/web/feeds/recipes/recipe_pagina12.py
Normal 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/')
|
@ -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
|
||||||
|
62
src/calibre/web/feeds/recipes/recipe_sueddeutsche.py
Normal 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
|
@ -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:
|
||||||
|