diff --git a/setup.py b/setup.py index e7c16e6aa6..2f0c5aad26 100644 --- a/setup.py +++ b/setup.py @@ -415,8 +415,9 @@ if __name__ == '__main__': ext_modules.append(Extension('calibre.plugins.winutil', sources=['src/calibre/utils/windows/winutil.c'], libraries=['shell32', 'setupapi'], - include_dirs=['C:/WinDDK/6001.18001/inc/api/', - 'C:/WinDDK/6001.18001/inc/crt/'], + include_dirs=os.environ.get('INCLUDE', + 'C:/WinDDK/6001.18001/inc/api/;' + 'C:/WinDDK/6001.18001/inc/crt/').split(';'), extra_compile_args=['/X'] )) if isosx: @@ -425,7 +426,11 @@ if __name__ == '__main__': 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( name = APPNAME, diff --git a/src/calibre/constants.py b/src/calibre/constants.py index 180433532d..0004d80b6e 100644 --- a/src/calibre/constants.py +++ b/src/calibre/constants.py @@ -2,7 +2,7 @@ __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net' __docformat__ = 'restructuredtext en' __appname__ = 'calibre' -__version__ = '0.4.128' +__version__ = '0.4.129' __author__ = "Kovid Goyal " ''' Various run time constants. diff --git a/src/calibre/devices/__init__.py b/src/calibre/devices/__init__.py index f300c666b9..ebb95a4e86 100644 --- a/src/calibre/devices/__init__.py +++ b/src/calibre/devices/__init__.py @@ -10,8 +10,8 @@ def devices(): from calibre.devices.prs505.driver import PRS505 from calibre.devices.prs700.driver import PRS700 from calibre.devices.cybookg3.driver import CYBOOKG3 - #from calibre.devices.kindle.driver import KINDLE - return (PRS500, PRS505, PRS700, CYBOOKG3) + from calibre.devices.kindle.driver import KINDLE + return (PRS500, PRS505, PRS700, CYBOOKG3, KINDLE) 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[0] = INVERSE_DAY_MAP[int(src[0][:-1])]+',' src[2] = INVERSE_MONTH_MAP[int(src[2])] - return ' '.join(src) \ No newline at end of file + return ' '.join(src) diff --git a/src/calibre/devices/cybookg3/driver.py b/src/calibre/devices/cybookg3/driver.py index 6c026c8669..ea6376df0d 100644 --- a/src/calibre/devices/cybookg3/driver.py +++ b/src/calibre/devices/cybookg3/driver.py @@ -4,9 +4,11 @@ __copyright__ = '2009, John Schember ' 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 +import calibre.devices.cybookg3.t2b as t2b class CYBOOKG3(USBMS): # Ordered list of supported formats @@ -29,6 +31,76 @@ class CYBOOKG3(USBMS): EBOOK_DIR_MAIN = "eBooks" 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): for path in paths: @@ -36,17 +108,15 @@ class CYBOOKG3(USBMS): os.unlink(path) filepath, ext = os.path.splitext(path) - basepath, filename = os.path.split(filepath) # Delete the ebook auxiliary file if os.path.exists(filepath + '.mbp'): os.unlink(filepath + '.mbp') # Delete the thumbnails file auto generated for the ebook - for p, d, files in os.walk(basepath): - for filen in fnmatch.filter(files, filename + "*.t2b"): - os.unlink(os.path.join(p, filen)) - + if os.path.exists(filepath + '_6090.t2b'): + os.unlink(filepath + '_6090.t2b') + try: os.removedirs(os.path.dirname(path)) except: diff --git a/src/calibre/devices/cybookg3/t2b.py b/src/calibre/devices/cybookg3/t2b.py new file mode 100644 index 0000000000..0baa7a1060 --- /dev/null +++ b/src/calibre/devices/cybookg3/t2b.py @@ -0,0 +1,48 @@ +__license__ = 'GPL v3' +__copyright__ = '2009, John Schember ' +''' +Write a t2b file to disk. +''' + +import Image, StringIO + +DEFAULT_T2B_DATA = '\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x0f\xff\xff\xff\xf0\xff\x0f\xc3\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x00\x00\xff\xff\xff\xf0\xff\x0f\xc3\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\xff\xf0\xff\xff\xff\xf0\xff\xff\xc3\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc3\xff\xff\xff\xff\xff\xf0\xff\xff\xc3\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x07\xff\xff\xfc\x00?\xf0\xff\x0f\xc3\x00?\xf0\xc0\xfe\x00?\xff\xff\xff\xff\xff\xff\xff\x0f\xff\xff\xf0<\x0f\xf0\xff\x0f\xc0,\x0f\xf0\x0e\xf0,\x0f\xff\xff\xff\xff\xff\xff\xff\x0f\xff\xff\xff\xff\xc3\xf0\xff\x0f\xc0\xff\x0f\xf0\xff\xf0\xff\xc7\xff\xff\xff\xff\xff\xff\xff\x0f\xff\xff\xff\xff\xc3\xf0\xff\x0f\xc3\xff\xc3\xf0\xff\xc3\xff\xc3\xff\xff\xff\xff\xff\xff\xff\x0f\xff\xff\xff\x00\x03\xf0\xff\x0f\xc3\xff\xc3\xf0\xff\xc3\xff\xc3\xff\xff\xff\xff\xff\xff\xff\x0f\xff\xff\xf0\x1f\xc3\xf0\xff\x0f\xc3\xff\xc3\xf0\xff\xc0\x00\x03\xff\xff\xff\xff\xff\xff\xff\x0b\xff\xff\xf0\xff\xc3\xf0\xff\x0f\xc3\xff\xc3\xf0\xff\xc3\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc3\xff\xff\xf3\xff\xc3\xf0\xff\x0f\xc3\xff\xc3\xf0\xff\xc3\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0\xff\xfc\xf0\xff\x03\xf0\xff\x0f\xc0\xff\x0f\xf0\xff\xf0\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x0f\x00\xf08\x03\xf0\xff\x0f\xc0,\x0f\xf0\xff\xf0\x1f\x03\xff\xff\xff\xff\xff\xff\xff\xff\x00\x0f\xfc\x00\xc3\xf0\xff\x0f\xc3\x00?\xf0\xff\xff\x00\x0f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x0f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x03\xfe\x94\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x00\x00\x0f\xff\xff\xff\xff\xff\xff\xfc\x7f\xfe\x94\xff\xff\xff\xff\xff\xff\xff\xff\xfc\x0f\xff\xfe\xa9@\xff\xff\xff\xff\xff\xff\xfc?\xfe\xa4\xff\xff\xff\xff\xff\xff\xff\xff\xfc\xff\xff\xff\xe9P\xff\xff\xff\xff\xff\xff\xfe/\xfe\xa8\xff\xff\xff\xff\xff\xff\xff\xff\xfc\xff\xff\xff\xf9T\xff\xff\xff\xff\xf0@\x00+\xfa\xa8?\xff\xff\xff\xff\xff\xff\xff\xfc\xbf\xff\xff\xf9T\xff\xff\xff\xff\xcb\xe4}*\xaa\xaa?\xff\xff\xff\xff\xff\xff\xff\xfc\xbf\xff\xff\xe9T\xff\xff\xff\xff\xc7\xe4\xfd\x1a\xaa\xaa?\xff\xff\xff\xff\xff\xff\xff\xfc\xaf\xea\xaa\xa6\xa4\xff@\x00\x0f\xc3\xe8\xfe\x1a\xaa\xaa?\xff\xff\xff\xff\xff\xff\xff\xfcj\x95UZ\xa4\x00\x7f\xfe\x90\x03\xe8\xfe\n\xaa\xaa?\xff\xff\xff\xff\xff\xff\xff\xfcj\x95UZ\xa4?\xff\xff\xa5C\xe8\xfe\x06\xaa\xaa?\xff\xff\xff\xff\xff\xff\xff\xfcj\x95UZ\xa4?\xff\xff\xeaC\xe8\xbe\x06\xaa\xaa\x0f\xff\xff\xff\xff\xff\xff\xff\xfcj\x95UZ\xa4/\xff\xff\xea\x82\xe8j\x06\xaa\xaa\x0f\xff\xff\xff\xff\xff\xff\xff\xfcj\x95UZ\xa4/\xff\xff\xaa\x82\xe8*F\xaa\xaa\x8f\xff\xff\xff\xff\xff\xff\xff\xfcj\x95UZ\xa4+\xff\xfe\xaa\x82\xe8*\x86\xaa\xaa\x8f\xff\xff\x80\xff\xff\xff\xff\xfcj\x95UV\xa4\x1a\xfa\xaa\xaa\x82\xe8*\x86\xaa\xaa\x8f\xf0\x00T?\xff\xff\xff\xfcj\x95UV\xa4\x1a\xfa\xaa\xaa\x82\xe8*\x81\xaa\xaa\x8c\x03\xff\x95?\xff\xff\xff\xfcj\x95UV\xa4\x1a\xfa\xaa\xaa\x82\xe8*\x81\xaa\xaa\x80\xbf\xff\x95?\xff\xff\xff\xfcj\x95UV\xa4\x1a\xfa\xaa\xaa\x82\xe8*\x81\xaa\xaa\x9b\xff\xff\x95\x0f\xff\xff\xff\xfcj\x95UV\xa4\x1a\xfa\xaa\xaa\x82\xe8\x1a\x81\xaa\xaa\x9a\xff\xfe\x95\x0f\xff\xff\xff\xfcj\x95UV\xa4\x1a\xfa\xaa\xaa\x82\xe8\n\x81\xaa\xaa\xa6\xbf\xfeUO\xff\xff\xff\xfcj\x95UV\xa4\x1a\xfa\xaa\xaa\x82\xa8\n\x91j\xaa\xa5\xaa\xa9ZO\xff\xff\xff\xfcj\x95UV\xa4\x1a\xfa\xaa\xaa\x82\xa8\n\xa0j\xaa\xa5Z\x95ZO\xff\xff\xff\xfcj\x95UV\xa4*\xfa\xaa\xaa\x82\xa9\n\xa0j\xaa\xa5UUZC\xff\xff\xff\xfcj\x95UV\xa4*\xfa\xaa\xaa\x82\xaa\n\xa0j\xaa\xa4UUZS\xff\xff\xff\xfcZ\x95UV\xa4*\xfa\xaa\xaa\x82\xaa\n\xa0j\xaa\xa4UUZS\xff\xff\xff\xfcZ\x95UU\xa4*\xfa\xaa\xaa\x82\xaa\n\xa0j\xaa\xa8UUVS\xff\xff\xff\xfcZ\x95UU\xa4*\xea\xaa\xaa\x82\xaa\x06\xa0Z\xaa\xa8UUV\x93\xff\xff\xff\xfcZ\x95UU\xa4*\xaa\xaa\xaa\x81\xaa\x02\xa0\x1a\xaa\xa8UUV\x90\xff\xff\xff\xfcZ\x95UU\xa4*\xaa\xaa\xaa\x80\xaa\x02\xa0\x1a\xaa\xa8\x15UU\x94\xff\xff\xff\xfcZ\x95UU\xa4*\xaa\xaa\xaa\x80\xaa"\xa0\x1a\xaa\xa8\x15UU\x94\xff\xff\xff\xfcZ\x95UU\xa4*\xaa\xaa\xaa\x80\xaa2\xa4\x16\xaa\xa8\x15UU\x94\xff\xff\xff\xfcZ\x95UU\xa4*\xaa\xaa\xaa\x80\xaa2\xa8\x16\xa6\xa9\x15UU\x94\xff\xff\xff\xfcZ\x95UU\xa4*\xaa\xaa\xaa\x80\xaa2\xa8\x16\xa6\xa9\x05UUT?\xff\xff\xfcZ\x95UU\xa4*\xaa\xaa\xaa\x84\xaa2\xa8\x16\xaa\xaa\x05UUU?\xff\xff\xfcZ\x95UU\xa4*\xaa\xaa\xaa\x88\xaa2\xa8\x06\xaa\xaa\x05UUU?\xff\xff\xfcZ\x95UU\xa4*\xaa\xaa\xaa\x8c\xaa1\xa8\xc5\xaa\xaa\x05UUU?\xff\xff\xfcZ\x95UU\xa4*\xaa\xaa\xaa\x8c\xaa0\xa8E\xa9\xaa\x05UUU/\xff\xff\xfcZ\x95UU\xa4*\xaa\xaa\xaa\x8c\xaa<\xa8\x05\xa9\xaaAUUU\x0f\xff\xff\xfcZ\x95UU\xa4*\xaa\xaa\xaa\x8c\xaa<\xa8\x05\xa9\xaaAUUUO\xff\xff\xfcZ\x95UU\xa4*\xaa\xaa\xaa\x8c\xaa<\xa9\x05\xaa\xaaAUUUO\xff\xff\xfcZ\x95UU\xa4*\xaa\xaa\xaa\x8c\xaa\x1c\xaa\x01\xaa\xaa\x81UUUO\xff\xff\xfcZ\x95UU\xa4*\xaa\xaa\xaa\x8c\xaa\x0c\xaa\x01\xaa\xaa\x81UUUO\xff\xff\xfcZ\x95UU\xa4*\xaa\xaa\xaa\x8c\xaa\x0c\xaa1j\xaa\x80UUUC\xff\xff\xfcZ\x95UU\xa4*\xaa\xaa\xaa\x8c\xaa\x0cj1jj\x90UUUS\xff\xff\xfcZ\x95UU\xa4*\xaa\xaa\xaa\x8c\xaa\x0c*1jj\x90UUUS\xff\xff\xfcZ\x95UU\xa4*\xaa\xaa\xaa\x8c\xaaL*1jj\xa0UUUS\xff\xff\xfcZ\x95UU\xa4*\xaa\xaa\xaa\x8c\xaa\x8f* j\xaa\xa0\x15UUS\xff\xff\xfcZ\x95UU\xa4*\xaa\xaa\xaa\x8c\xaa\x8f*@j\xaa\xa0\x15UUP\xff\xff\xfcZ\x95UU\xa4*\xaa\xaa\xaa\x8c\xaa\x8f*\x8cZ\xaa\xa1\x15UUT\xff\xff\xfcZ\x95UU\xa4j\xaa\xaa\xaa\x8c\xaa\x8f*\x8cZ\x9a\xa0\x15UUT\xff\xff\xfcZ\x95UU\xa4j\xaa\xaa\xaa\x8c\xaa\x8f*\x8cZ\x9a\xa0\x15UUT\xff\xff\xfcZ\x95UU\xa4j\xaa\xaa\xaa\x8c\xaa\x8f\x1a\x8cZ\x9a\xa4\x15UUT?\xff\xfcZ\x95UU\x94j\xaa\xaa\xaa\x8cj\x8f\n\x8cVj\xa4\x05UU\xa4?\xff\xfcVUUU\xa4j\xaa\xaa\xaa\x8cj\x8fJ\x8c\x16\xaa\xa8\xc5UZ\xa5?\xff\xfcUUUV\xa4j\xaa\xaa\xaa\x8cj\x8f\xca\x8f\x16\xaa\xa8\xc5V\xaa\xa5?\xff\xfcUj\xaa\xaa\xa4j\xaa\xaa\xaa\x8cj\x8f\xca\x8f\x1a\xaa\xa8\x05Z\xaaU?\xff\xfcV\xaa\xaa\xaa\xa5j\xaa\xaa\xaa\x8e*\x8f\xca\x83\x1a\xaa\xa4\x01eUU?\xff\xfcZ\xaa\xaa\xaa\xa5j\xaa\xaa\xaa\x8f*\x8f\xca\x83\x1a\xa5U\x01U\x00\x00\x0f\xff\xfcUUUUUZ\xaa\xaa\xaaO%\x8f\xc6\x93\x15\x00\x001@\x0f\xff\xff\xff\xfcP\x00\x00\x00\x15\x00\x00\x00\x00\x0f\x00\x07\xc0\x03\x00\xff\xff0\x1f\xff\xff\xff\xff\xfc\x00\xff\xff\xf8\x00?\xff\xff\xff\x0f?\xc7\xc3\xf7\x0f\xff\xff\xf1\xff\xff\xff\xff\xff\xfc\xff\xff\xff\xff\xf4\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff' + +def reduce_color(c): + if c <= 64: + return 0 + elif c > 64 and c <= 128: + return 1 + elif c > 128 and c <= 192: + return 2 + else: + return 3 + +def i2b(n): + return "".join([str((n >> y) & 1) for y in range(1, -1, -1)]) + +def write_t2b(t2bfile, coverdata=None): + ''' + t2bfile is a file handle ready to write binary data to disk. + coverdata is a string representation of a JPEG file. + ''' + if coverdata != None: + coverdata = StringIO.StringIO(coverdata) + cover = Image.open(coverdata).convert("L") + cover.thumbnail((96, 144)) + t2bcover = Image.new('L', (96, 144), 'white') + + x, y = cover.size + t2bcover.paste(cover, ((96-x)/2, (144-y)/2)) + + px = [] + pxs = t2bcover.getdata() + for i in range(len(pxs)): + px.append(pxs[i]) + if len(px) >= 4: + binstr = i2b(reduce_color(px[0])) + i2b(reduce_color(px[1])) + i2b(reduce_color(px[2])) + i2b(reduce_color(px[3])) + t2bfile.write(chr(int(binstr, 2))) + px = [] + else: + t2bfile.write(DEFAULT_T2B_DATA) + diff --git a/src/calibre/devices/kindle/driver.py b/src/calibre/devices/kindle/driver.py index d5ef7008bc..d4cbb5b90b 100755 --- a/src/calibre/devices/kindle/driver.py +++ b/src/calibre/devices/kindle/driver.py @@ -16,13 +16,18 @@ class KINDLE(USBMS): PRODUCT_ID = 0x0001 BCD = [0x399] - VENDOR_NAME = 'AMAZON' - WINDOWS_MAIN_MEM = 'KINDLE' + VENDOR_NAME = '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' STORAGE_CARD_VOLUME_LABEL = 'Kindle Storage Card' EBOOK_DIR_MAIN = "documents" + SUPPORTS_SUB_DIRS = True def delete_books(self, paths, end_session=True): for path in paths: diff --git a/src/calibre/devices/usbms/driver.py b/src/calibre/devices/usbms/driver.py index 187a4c1a50..75f04219e7 100644 --- a/src/calibre/devices/usbms/driver.py +++ b/src/calibre/devices/usbms/driver.py @@ -125,10 +125,11 @@ class USBMS(Device): if os.path.exists(path): # Delete the ebook os.unlink(path) - try: - os.removedirs(os.path.dirname(path)) - except: - pass + if self.SUPPORTS_SUB_DIRS: + try: + os.removedirs(os.path.dirname(path)) + except: + pass @classmethod def remove_books_from_metadata(cls, paths, booklists): @@ -148,7 +149,18 @@ class USBMS(Device): path = self.munge_path(path) src = open(path, 'rb') 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): if path.startswith('/') and not (path.startswith(self._main_prefix) or \ (self._card_prefix and path.startswith(self._card_prefix))): @@ -157,6 +169,34 @@ class USBMS(Device): path = path.replace('card:', self._card_prefix[:-1]) 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 def extract_book_metadata_by_filename(cls, filename): book_title = '' @@ -183,5 +223,3 @@ class USBMS(Device): return book_title, book_author, book_mime -# ls, rm, cp, mkdir, touch, cat - diff --git a/src/calibre/ebooks/epub/from_feeds.py b/src/calibre/ebooks/epub/from_feeds.py index bbadbc54de..fd1759712d 100644 --- a/src/calibre/ebooks/epub/from_feeds.py +++ b/src/calibre/ebooks/epub/from_feeds.py @@ -40,6 +40,7 @@ def convert(opts, recipe_arg, notification=None): c.smart_update(recipe_opts, opts) opts = recipe_opts opts.chapter_mark = 'none' + opts.dont_split_on_page_breaks = True opf = glob.glob(os.path.join(tdir, '*.opf')) if not opf: raise Exception('Downloading of recipe: %s failed'%recipe_arg) diff --git a/src/calibre/ebooks/lit/from_any.py b/src/calibre/ebooks/lit/from_any.py index cd8b5a115c..f6813caf91 100644 --- a/src/calibre/ebooks/lit/from_any.py +++ b/src/calibre/ebooks/lit/from_any.py @@ -38,7 +38,10 @@ def any2lit(opts, path): os.mkdir(oebdir) opts.output = os.path.join(tdir, 'dummy.epub') 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) + opts.base_font_size2 = orig_bfs opf = glob.glob(os.path.join(oebdir, '*.opf'))[0] opts.output = orig_output logging.getLogger('html2epub').info(_('Creating LIT file from EPUB...')) @@ -56,4 +59,4 @@ def main(args=sys.argv): return 0 if __name__ == '__main__': - sys.exit(main()) \ No newline at end of file + sys.exit(main()) diff --git a/src/calibre/ebooks/lit/writer.py b/src/calibre/ebooks/lit/writer.py index 94b8017f2e..6d97471d75 100644 --- a/src/calibre/ebooks/lit/writer.py +++ b/src/calibre/ebooks/lit/writer.py @@ -143,7 +143,7 @@ def warn(x): class ReBinary(object): 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.logger = oeb.logger self.manifest = oeb.manifest @@ -713,7 +713,7 @@ def option_parser(): '-o', '--output', default=None, help=_('Output file. Default is derived from input filename.')) parser.add_option( - '--verbose', default=False, action='store_true', + '-v', '--verbose', default=0, action='count', help=_('Useful for debugging.')) return parser @@ -725,7 +725,7 @@ def oeb2lit(opts, inpath): outpath = os.path.basename(inpath) outpath = os.path.splitext(outpath)[0] + '.lit' outpath = os.path.abspath(outpath) - context = Context('Firefox', 'MSReader') + context = Context('Browser', 'MSReader') oeb = OEBBook(inpath, logger=logger) tocadder = HTMLTOCAdder() tocadder.transform(oeb, context) diff --git a/src/calibre/ebooks/lrf/comic/convert_from.py b/src/calibre/ebooks/lrf/comic/convert_from.py index 0569bf3733..c22f1745ae 100755 --- a/src/calibre/ebooks/lrf/comic/convert_from.py +++ b/src/calibre/ebooks/lrf/comic/convert_from.py @@ -10,11 +10,6 @@ Based on ideas from comiclrf created by FangornUK. import os, sys, shutil, traceback, textwrap 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): width, height = PROFILES[profile] - - if not _reportlab: - raise RuntimeError('Failed to load reportlab') - + + from reportlab.pdfgen import canvas + pdf = canvas.Canvas(filename=opts.output, pagesize=(width,height+15)) pdf.setAuthor(opts.author) pdf.setTitle(opts.title) diff --git a/src/calibre/ebooks/metadata/epub.py b/src/calibre/ebooks/metadata/epub.py index a7114c48d8..a8c2105c02 100644 --- a/src/calibre/ebooks/metadata/epub.py +++ b/src/calibre/ebooks/metadata/epub.py @@ -103,8 +103,8 @@ class OCFDirReader(OCFReader): return open(os.path.join(self.root, path), *args, **kwargs) class CoverRenderer(QObject): - WIDTH = 1280 - HEIGHT = 1024 + WIDTH = 600 + HEIGHT = 800 def __init__(self, url, size, loop): QObject.__init__(self) @@ -113,7 +113,7 @@ class CoverRenderer(QObject): pal = self.page.palette() pal.setBrush(QPalette.Background, Qt.white) 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.Horizontal, Qt.ScrollBarAlwaysOff) QObject.connect(self.page, SIGNAL('loadFinished(bool)'), self.render_html) @@ -126,9 +126,9 @@ class CoverRenderer(QObject): try: if not ok: return - size = self.page.mainFrame().contentsSize() - width, height = fit_image(size.width(), size.height(), self.WIDTH, self.HEIGHT)[1:] - self.page.setViewportSize(QSize(width, height)) + #size = self.page.mainFrame().contentsSize() + #width, height = fit_image(size.width(), size.height(), self.WIDTH, self.HEIGHT)[1:] + #self.page.setViewportSize(QSize(width, height)) image = QImage(self.page.viewportSize(), QImage.Format_ARGB32) image.setDotsPerMeterX(96*(100/2.54)) image.setDotsPerMeterY(96*(100/2.54)) diff --git a/src/calibre/ebooks/mobi/from_any.py b/src/calibre/ebooks/mobi/from_any.py index a376b1601d..9af2e5fe68 100644 --- a/src/calibre/ebooks/mobi/from_any.py +++ b/src/calibre/ebooks/mobi/from_any.py @@ -42,7 +42,11 @@ def any2mobi(opts, path): os.mkdir(oebdir) opts.output = os.path.join(tdir, 'dummy.epub') 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) + opts.base_font_size2 = orig_bfs opf = glob.glob(os.path.join(oebdir, '*.opf'))[0] opts.output = orig_output logging.getLogger('html2epub').info(_('Creating Mobipocket file from EPUB...')) diff --git a/src/calibre/ebooks/mobi/reader.py b/src/calibre/ebooks/mobi/reader.py index 63ef7b0b14..2ed41ac898 100644 --- a/src/calibre/ebooks/mobi/reader.py +++ b/src/calibre/ebooks/mobi/reader.py @@ -267,6 +267,12 @@ class MobiReader(object): 'xx-large' : '6', } 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 if attrib.has_key('style'): style = attrib.pop('style').strip() @@ -466,16 +472,28 @@ class MobiReader(object): def get_metadata(stream): mr = MobiReader(stream) if mr.book_header.exth is None: - mi = MetaInformation(mr.name, ['Unknown']) + mi = MetaInformation(mr.name, [_('Unknown')]) else: - tdir = tempfile.mkdtemp('_mobi_meta', __appname__) + tdir = tempfile.mkdtemp('_mobi_meta', __appname__+'_') atexit.register(shutil.rmtree, tdir) mr.extract_images([], tdir) mi = mr.create_opf('dummy.html') if 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): - 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: path = os.path.join(tdir, 'images', '00001.jpg') if os.access(path, os.R_OK): diff --git a/src/calibre/ebooks/mobi/writer.py b/src/calibre/ebooks/mobi/writer.py index 9162bf8d2d..62d444ee95 100644 --- a/src/calibre/ebooks/mobi/writer.py +++ b/src/calibre/ebooks/mobi/writer.py @@ -393,7 +393,7 @@ class MobiWriter(object): return data 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.sort() metadata = self._oeb.metadata @@ -502,7 +502,8 @@ def add_mobi_options(parser): _('Mobipocket-specific options.')) group.add_option( '-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( '-r', '--rescale-images', default=False, action='store_true', help=_('Modify images to meet Palm device size limitations.')) @@ -525,7 +526,7 @@ def option_parser(): '-o', '--output', default=None, help=_('Output file. Default is derived from input filename.')) parser.add_option( - '-v', '--verbose', default=False, action='store_true', + '-v', '--verbose', default=0, action='count', help=_('Useful for debugging.')) add_mobi_options(parser) return parser diff --git a/src/calibre/gui2/dialogs/progress.py b/src/calibre/gui2/dialogs/progress.py new file mode 100644 index 0000000000..2543cefb4d --- /dev/null +++ b/src/calibre/gui2/dialogs/progress.py @@ -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) \ No newline at end of file diff --git a/src/calibre/gui2/dialogs/progress.ui b/src/calibre/gui2/dialogs/progress.ui new file mode 100644 index 0000000000..60488be62d --- /dev/null +++ b/src/calibre/gui2/dialogs/progress.ui @@ -0,0 +1,72 @@ + + Dialog + + + + 0 + 0 + 712 + 308 + + + + Dialog + + + + :/images/jobs.svg:/images/jobs.svg + + + + + + + 75 + true + + + + TextLabel + + + Qt::AlignCenter + + + true + + + + + + + 0 + + + + + + + TextLabel + + + Qt::AlignCenter + + + true + + + + + + + QDialogButtonBox::Abort + + + + + + + + + + diff --git a/src/calibre/gui2/images/news/ambito.png b/src/calibre/gui2/images/news/ambito.png new file mode 100644 index 0000000000..e0a6f409cf Binary files /dev/null and b/src/calibre/gui2/images/news/ambito.png differ diff --git a/src/calibre/gui2/images/news/elargentino.png b/src/calibre/gui2/images/news/elargentino.png new file mode 100644 index 0000000000..1e3e64d8a5 Binary files /dev/null and b/src/calibre/gui2/images/news/elargentino.png differ diff --git a/src/calibre/gui2/images/news/ftd.png b/src/calibre/gui2/images/news/ftd.png new file mode 100644 index 0000000000..a37c7a22f4 Binary files /dev/null and b/src/calibre/gui2/images/news/ftd.png differ diff --git a/src/calibre/gui2/images/news/heise.png b/src/calibre/gui2/images/news/heise.png new file mode 100644 index 0000000000..1f83f6cc65 Binary files /dev/null and b/src/calibre/gui2/images/news/heise.png differ diff --git a/src/calibre/gui2/images/news/infobae.png b/src/calibre/gui2/images/news/infobae.png new file mode 100644 index 0000000000..6a25d93325 Binary files /dev/null and b/src/calibre/gui2/images/news/infobae.png differ diff --git a/src/calibre/gui2/images/news/pagina12.png b/src/calibre/gui2/images/news/pagina12.png new file mode 100644 index 0000000000..e6384e8ad9 Binary files /dev/null and b/src/calibre/gui2/images/news/pagina12.png differ diff --git a/src/calibre/gui2/images/news/security_watch.png b/src/calibre/gui2/images/news/security_watch.png new file mode 100644 index 0000000000..1c048d6577 Binary files /dev/null and b/src/calibre/gui2/images/news/security_watch.png differ diff --git a/src/calibre/gui2/images/news/sueddeutsche.png b/src/calibre/gui2/images/news/sueddeutsche.png new file mode 100644 index 0000000000..b7a5de767b Binary files /dev/null and b/src/calibre/gui2/images/news/sueddeutsche.png differ diff --git a/src/calibre/gui2/images/news/zdnet.png b/src/calibre/gui2/images/news/zdnet.png new file mode 100644 index 0000000000..9e2605dc52 Binary files /dev/null and b/src/calibre/gui2/images/news/zdnet.png differ diff --git a/src/calibre/gui2/library.py b/src/calibre/gui2/library.py index 792d011883..8a737fd608 100644 --- a/src/calibre/gui2/library.py +++ b/src/calibre/gui2/library.py @@ -198,13 +198,18 @@ class BooksModel(QAbstractTableModel): ''' Return list indices of all cells in index.row()''' 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] if single_format is None: - return self.db.export_to_dir(path, rows, self.sorted_on[0] == 'authors', - single_dir=single_dir) + return self.db.export_to_dir(path, rows, + self.sorted_on[0] == 'authors', + single_dir=single_dir, + callback=callback) 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): diff --git a/src/calibre/gui2/main.py b/src/calibre/gui2/main.py index 6ff5df412d..0910f85dac 100644 --- a/src/calibre/gui2/main.py +++ b/src/calibre/gui2/main.py @@ -28,6 +28,7 @@ from calibre.gui2.cover_flow import CoverFlow, DatabaseImages, pictureflowerror from calibre.library.database import LibraryDatabase from calibre.gui2.dialogs.scheduler import Scheduler 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_ui import Ui_MainWindow 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.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.tags_view.recount) self.library_view.model().count_changed() ########################### Cover Flow ################################ 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') if not root: return - progress = QProgressDialog('', '&'+_('Stop'), - 0, 0, self) - progress.setWindowModality(Qt.ApplicationModal) - progress.setWindowTitle(_('Adding books recursively...')) + progress = ProgressDialog(_('Adding books recursively...'), + min=0, max=0, parent=self) progress.show() def callback(msg): if msg != '.': - progress.setLabelText((_('Added ')+msg) if msg else _('Searching...')) - stop = progress.wasCanceled() + progress.set_msg((_('Added ')+msg) if msg else _('Searching...')) QApplication.processEvents() QApplication.sendPostedEvents() QApplication.flush() - return stop + return progress.canceled try: duplicates = self.library_view.model().db.recursive_import(root, single, callback=callback) finally: - progress.hide() - progress.close() + progress.hide() if duplicates: files = _('

Books with the same title as the following already exist in the database. Add them anyway?

    ') for mi, formats in duplicates: files += '
  • '+mi.title+'
  • \n' - d = WarningDialog(_('Duplicates found!'), _('Duplicates found!'), files+'

', self) + d = WarningDialog(_('Duplicates found!'), _('Duplicates found!'), + files+'

', self) if d.exec_() == QDialog.Accepted: for mi, formats in duplicates: self.library_view.model().db.import_book(mi, formats ) @@ -686,15 +686,13 @@ class Main(MainWindow, Ui_MainWindow): return # Get format and metadata information formats, metadata, names, infos = [], [], [], [] - progress = QProgressDialog(_('Reading metadata...'), _('Stop'), 0, len(paths), self) - progress.setWindowTitle(_('Adding books...')) - progress.setWindowModality(Qt.ApplicationModal) - progress.setLabelText(_('Reading metadata...')) + progress = ProgressDialog(_('Adding books...'), _('Reading metadata...'), + min=0, max=len(paths), parent=self) progress.show() try: for c, book in enumerate(paths): - progress.setValue(c) - if progress.wasCanceled(): + progress.set_value(c) + if progress.canceled: return format = os.path.splitext(book)[1] 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), 'cover':self.default_thumbnail, 'tags':[]}) 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: - progress.setLabelText(_('Adding books to database...')) + progress.set_msg(_('Adding books to database...')) model = self.library_view.model() paths = list(paths) duplicates, number_added = model.add_books(paths, formats, metadata) - progress.cancel() if duplicates: files = _('

Books with the same title as the following already exist in the database. Add them anyway?

    ') for mi in duplicates[2]: @@ -734,9 +731,7 @@ class Main(MainWindow, Ui_MainWindow): else: self.upload_books(paths, list(map(sanitize_file_name, names)), infos, on_card=on_card) finally: - progress.setValue(progress.maximum()) progress.hide() - progress.close() 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) def save_to_disk(self, checked, single_dir=False, single_format=None): + rows = self.current_view().selectionModel().selectedRows() if not rows or len(rows) == 0: d = error_dialog(self, _('Cannot save to disk'), _('No books selected')) d.exec_() 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')) if not dir: return - if self.current_view() == self.library_view: - failures = self.current_view().model().save_to_disk(rows, dir, - single_dir=single_dir, single_format=single_format) - if failures and single_format is not None: - msg = _('

    Could not save the following books to disk, because the %s format is not available for them:

      ')%single_format.upper() - for f in failures: - msg += '
    • %s
    • '%f[1] - msg += '
    ' - 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) + + progress.show() + QApplication.processEvents() + QApplication.sendPostedEvents() + QApplication.flush() + try: + if self.current_view() == self.library_view: + failures = self.current_view().model().save_to_disk(rows, dir, + single_dir=single_dir, callback=callback, + single_format=single_format) + if failures and single_format is not None: + msg = _('

    Could not save the following books to disk, because the %s format is not available for them:

      ')%single_format.upper() + for f in failures: + msg += '
    • %s
    • '%f[1] + msg += '
    ' + 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): if job.exception is not None: @@ -1115,6 +1131,7 @@ class Main(MainWindow, Ui_MainWindow): os.remove(f.name) except: pass + self.tags_view.recount() if self.current_view() is self.library_view: current = self.library_view.currentIndex() self.library_view.model().current_changed(current, QModelIndex()) diff --git a/src/calibre/gui2/tags.py b/src/calibre/gui2/tags.py index 167236c960..a6772a3b44 100644 --- a/src/calibre/gui2/tags.py +++ b/src/calibre/gui2/tags.py @@ -7,7 +7,7 @@ __docformat__ = 'restructuredtext en' Browsing book collection by tags. ''' 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 NONE = QVariant() @@ -36,6 +36,14 @@ class TagsView(QTreeView): if self._model.toggle(index): self.emit(SIGNAL('tags_marked(PyQt_PyObject, PyQt_PyObject)'), 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): diff --git a/src/calibre/library/database.py b/src/calibre/library/database.py index ecf272618f..2979c95d26 100644 --- a/src/calibre/library/database.py +++ b/src/calibre/library/database.py @@ -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')] 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): raise IOError('Target directory does not exist: '+dir) by_author = {} + count = 0 for index in indices: id = index if index_is_id else self.id(index) 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: au = _('Unknown') au = au.split(',')[0] - else: - au = au.replace(',', ';') if not by_author.has_key(au): by_author[au] = [] 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 traceback.print_exc() f.close() + count += 1 + if callable(callback): + if not callback(count, mi.title): + return + def import_book(self, mi, formats): @@ -1569,12 +1573,13 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE; 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) if not index_is_id: indices = map(self.id, indices) failures = [] - for id in indices: + for count, id in enumerate(indices): try: data = self.format(id, format, index_is_id=True) if not data: @@ -1599,6 +1604,9 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE; except: pass f.close() + if callable(callback): + if not callback(count, title): + break return failures diff --git a/src/calibre/linux.py b/src/calibre/linux.py index 8314bc4bb9..be5864033a 100644 --- a/src/calibre/linux.py +++ b/src/calibre/linux.py @@ -192,6 +192,8 @@ def setup_completion(fatal_errors): 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.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 any_formats = ['epub', 'htm', 'html', 'xhtml', 'xhtm', 'rar', 'zip', '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('any2epub', any2epub, 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('lrf-meta', metaop, ['lrf'])) 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('comic2lrf', comicop, ['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('feeds2lrf', feeds2lrf, feed_titles)) f.write(opts_and_words('feeds2lrf', feeds2epub, feed_titles)) diff --git a/src/calibre/trac/bzr_commit_plugin.py b/src/calibre/trac/bzr_commit_plugin.py index 8cd8b53d09..5ec6e8f7e8 100644 --- a/src/calibre/trac/bzr_commit_plugin.py +++ b/src/calibre/trac/bzr_commit_plugin.py @@ -38,7 +38,6 @@ class cmd_commit(_cmd_commit): print attributes['summary'] return attributes['summary'] - def expand_bug(self, msg, nick, config, bug_tracker, type='trac'): prefix = '%s_%s_'%(type, nick) username = config.get_user_option(prefix+'username') diff --git a/src/calibre/web/feeds/recipes/__init__.py b/src/calibre/web/feeds/recipes/__init__.py index faeb65c193..f0687ece28 100644 --- a/src/calibre/web/feeds/recipes/__init__.py +++ b/src/calibre/web/feeds/recipes/__init__.py @@ -22,7 +22,7 @@ recipe_modules = ['recipe_' + r for r in ( 'time_magazine', 'endgadget', 'fudzilla', 'nspm_int', 'nspm', 'pescanik', 'spiegel_int', 'themarketticker', 'tomshardware', 'xkcd', 'ftd', 'zdnet', 'joelonsoftware', 'telepolis', 'common_dreams', 'nin', 'tomshardware_de', - + 'pagina12', 'infobae', 'ambito', 'elargentino', 'sueddeutsche', )] import re, imp, inspect, time, os diff --git a/src/calibre/web/feeds/recipes/recipe_ambito.py b/src/calibre/web/feeds/recipes/recipe_ambito.py new file mode 100644 index 0000000000..56528c27c0 --- /dev/null +++ b/src/calibre/web/feeds/recipes/recipe_ambito.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python + +__license__ = 'GPL v3' +__copyright__ = '2008, Darko Miletic ' +''' +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?') diff --git a/src/calibre/web/feeds/recipes/recipe_clarin.py b/src/calibre/web/feeds/recipes/recipe_clarin.py index 7dda99a08d..322e06dd66 100644 --- a/src/calibre/web/feeds/recipes/recipe_clarin.py +++ b/src/calibre/web/feeds/recipes/recipe_clarin.py @@ -6,6 +6,7 @@ __copyright__ = '2008, Darko Miletic ' clarin.com ''' +from calibre import strftime from calibre.web.feeds.news import BasicNewsRecipe class Clarin(BasicNewsRecipe): @@ -15,33 +16,35 @@ class Clarin(BasicNewsRecipe): oldest_article = 2 max_articles_per_feed = 100 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 = [ '--comment', description , '--base-font-size', '10' , '--category', 'news, Argentina' , '--publisher', 'Grupo Clarin' ] - + remove_tags = [ dict(name='a' , attrs={'class':'Imp' }) ,dict(name='div' , attrs={'class':'Perma' }) ,dict(name='h1' , text='Imprimir' ) ] - - feeds = [ - (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'Opinion' , u'http://www.clarin.com/diario/hoy/opinion.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'La Ciudad' , u'http://www.clarin.com/diario/hoy/laciudad.xml' ) + + feeds = [ + (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'Opinion' , u'http://www.clarin.com/diario/hoy/opinion.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'La Ciudad' , u'http://www.clarin.com/diario/hoy/laciudad.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): artl = article.get('link', None) rest = artl.partition('-0')[-1] lmain = rest.partition('.')[0] - return 'http://www.servicios.clarin.com/notas/jsp/clarin/v9/notas/imprimir.jsp?pagid=' + lmain \ No newline at end of file + return 'http://www.servicios.clarin.com/notas/jsp/clarin/v9/notas/imprimir.jsp?pagid=' + lmain diff --git a/src/calibre/web/feeds/recipes/recipe_elargentino.py b/src/calibre/web/feeds/recipes/recipe_elargentino.py new file mode 100644 index 0000000000..7aee232787 --- /dev/null +++ b/src/calibre/web/feeds/recipes/recipe_elargentino.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python + +__license__ = 'GPL v3' +__copyright__ = '2008, Darko Miletic ' +''' +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 = '' + soup.head.insert(0,mtag) + soup.prettify() + return soup diff --git a/src/calibre/web/feeds/recipes/recipe_infobae.py b/src/calibre/web/feeds/recipes/recipe_infobae.py new file mode 100644 index 0000000000..b6975d9a60 --- /dev/null +++ b/src/calibre/web/feeds/recipes/recipe_infobae.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python + +__license__ = 'GPL v3' +__copyright__ = '2008, Darko Miletic ' +''' +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 diff --git a/src/calibre/web/feeds/recipes/recipe_pagina12.py b/src/calibre/web/feeds/recipes/recipe_pagina12.py new file mode 100644 index 0000000000..8b427e8ad1 --- /dev/null +++ b/src/calibre/web/feeds/recipes/recipe_pagina12.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python + +__license__ = 'GPL v3' +__copyright__ = '2008, Darko Miletic ' +''' +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/') diff --git a/src/calibre/web/feeds/recipes/recipe_security_watch.py b/src/calibre/web/feeds/recipes/recipe_security_watch.py index 846e429c5d..4975628dcc 100644 --- a/src/calibre/web/feeds/recipes/recipe_security_watch.py +++ b/src/calibre/web/feeds/recipes/recipe_security_watch.py @@ -4,7 +4,7 @@ class SecurityWatch(BasicNewsRecipe): title = u'securitywatch' description = 'security news' timefmt = ' [%d %b %Y]' - __author__ = 'Oliver' + __author__ = 'Oliver Niesner' no_stylesheets = True oldest_article = 14 max_articles_per_feed = 100 diff --git a/src/calibre/web/feeds/recipes/recipe_sueddeutsche.py b/src/calibre/web/feeds/recipes/recipe_sueddeutsche.py new file mode 100644 index 0000000000..fa97b73c80 --- /dev/null +++ b/src/calibre/web/feeds/recipes/recipe_sueddeutsche.py @@ -0,0 +1,62 @@ +__license__ = 'GPL v3' +__copyright__ = '2008, Kovid Goyal ' + +''' +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 diff --git a/src/calibre/web/fetch/simple.py b/src/calibre/web/fetch/simple.py index 2a8b03a545..43f7aa0626 100644 --- a/src/calibre/web/fetch/simple.py +++ b/src/calibre/web/fetch/simple.py @@ -395,7 +395,11 @@ class RecursiveFetcher(object, LoggingInterface): if self.download_stylesheets: 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.filemap[nurl] = res if recursion_level < self.max_recursions: