Merged upstream changes.

This commit is contained in:
Marshall T. Vandegrift 2009-01-13 18:56:03 -05:00
commit 9cce46ad08
55 changed files with 18094 additions and 14919 deletions

View File

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

View File

@ -43,7 +43,11 @@ def update_module(mod, path):
zp = os.path.join(os.path.dirname(sys.executable), 'library.zip')
elif isosx:
zp = os.path.join(os.path.dirname(getattr(sys, 'frameworks_dir')),
'Resources', 'lib', 'python2.5', 'site-packages.zip')
'Resources', 'lib',
'python'+'.'.join(map(str, sys.version_info[:2])),
'site-packages.zip')
else:
zp = os.path.join(getattr(sys, 'frozen_path'), 'loader.zip')
if zp is not None:
update_zipfile(zp, mod, path)
else:

View File

@ -9,31 +9,25 @@ import os, fnmatch
from calibre.devices.usbms.driver import USBMS
class CYBOOKG3(USBMS):
MIME_MAP = {
'mobi' : 'application/mobi',
'prc' : 'application/prc',
'html' : 'application/html',
'pdf' : 'application/pdf',
'rtf' : 'application/rtf',
'txt' : 'text/plain',
}
# Ordered list of supported formats
FORMATS = MIME_MAP.keys()
# Be sure these have an entry in calibre.devices.mime
FORMATS = ['mobi', 'prc', 'html', 'pdf', 'rtf', 'txt']
VENDOR_ID = 0x0bda
PRODUCT_ID = 0x0703
BCD = [0x110, 0x132]
VENDOR_NAME = 'BOOKEEN'
PRODUCT_NAME = 'CYBOOK_GEN3'
WINDOWS_MAIN_MEM = 'CYBOOK_GEN3__-FD'
WINDOWS_CARD_MEM = 'CYBOOK_GEN3__-SD'
OSX_NAME_MAIN_MEM = 'Bookeen Cybook Gen3 -FD Media'
OSX_NAME_CARD_MEM = 'Bookeen Cybook Gen3 -SD Media'
OSX_MAIN_MEM = 'Bookeen Cybook Gen3 -FD Media'
OSX_CARD_MEM = 'Bookeen Cybook Gen3 -SD Media'
MAIN_MEMORY_VOLUME_LABEL = 'Cybook Gen 3 Main Memory'
STORAGE_CARD_VOLUME_LABEL = 'Cybook Gen 3 Storage Card'
EBOOK_DIR = "eBooks"
EBOOK_DIR_MAIN = "eBooks"
def delete_books(self, paths, end_session=True):
for path in paths:

View File

@ -41,6 +41,20 @@ class Device(object):
'''Return the FDI description of this device for HAL on linux.'''
return ''
@classmethod
def can_handle(cls, device_info):
'''
Optional method to perform further checks on a device to see if this driver
is capable of handling it. If it is not it should return False. This method
is only called after the vendor, product ids and the bcd have matched, so
it can do some relatively time intensive checks. The default implementation
returns True.
:param device_info: On windows a device ID string. On Unix a tuple of
``(vendor_id, product_id, bcd)``.
'''
return True
def open(self):
'''
Perform any device specific initialization. Called after the device is

View File

@ -9,24 +9,30 @@ import os, fnmatch
from calibre.devices.usbms.driver import USBMS
class KINDLE(USBMS):
MIME_MAP = {
'azw' : 'application/azw',
'mobi' : 'application/mobi',
'prc' : 'application/prc',
'txt' : 'text/plain',
}
# Ordered list of supported formats
FORMATS = MIME_MAP.keys()
FORMATS = ['azw', 'mobi', 'prc', 'txt']
VENDOR_ID = 0x1949
PRODUCT_ID = 0x0001
BCD = 0x399
BCD = [0x399]
VENDOR_NAME = 'AMAZON'
PRODUCT_NAME = 'KINDLE'
WINDOWS_MAIN_MEM = 'KINDLE'
MAIN_MEMORY_VOLUME_LABEL = 'Kindle Main Memory'
STORAGE_CARD_VOLUME_LABEL = 'Kindle Storage Card'
EBOOK_DIR = "documents"
EBOOK_DIR_MAIN = "documents"
def delete_books(self, paths, end_session=True):
for path in paths:
if os.path.exists(path):
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')

View File

@ -0,0 +1,19 @@
__license__ = 'GPL v3'
__copyright__ = '2009, John Schember <john at nachtimwald.com>'
'''
Global Mime mapping of ebook types.
'''
MIME_MAP = {
'azw' : 'application/azw',
'epub' : 'application/epub+zip',
'html' : 'text/html',
'lrf' : 'application/x-sony-bbeb',
'lrx' : 'application/x-sony-bbeb',
'mobi' : 'application/mobi',
'pdf' : 'application/pdf',
'prc' : 'application/prc',
'rtf' : 'application/rtf',
'txt' : 'text/plain',
}

View File

@ -63,11 +63,13 @@ class DeviceScanner(object):
for device_id in self.devices:
if vid in device_id and pid in device_id:
if self.test_bcd_windows(device_id, getattr(device, 'BCD', None)):
if device.can_handle(device_id):
return True
else:
for vendor, product, bcdDevice in self.devices:
if device.VENDOR_ID == vendor and device.PRODUCT_ID == product:
if self.test_bcd(bcdDevice, getattr(device, 'BCD', None)):
if device.can_handle((vendor, product, bcdDevice)):
return True
return False

View File

@ -6,7 +6,7 @@ intended to be subclassed with the relevant parts implemented for a particular
device. This class handles devive detection.
'''
import os, time
import os, subprocess, time
from calibre.devices.interface import Device as _Device
from calibre.devices.errors import DeviceError
@ -23,11 +23,12 @@ class Device(_Device):
PRODUCT_ID = 0x0
BCD = None
VENDOR_NAME = ''
PRODUCT_NAME = ''
VENDOR_NAME = None
WINDOWS_MAIN_MEM = None
WINDOWS_CARD_MEM = None
OSX_NAME_MAIN_MEM = ''
OSX_NAME_CARD_MEM = ''
OSX_MAIN_MEM = None
OSX_CARD_MEM = None
MAIN_MEMORY_VOLUME_LABEL = ''
STORAGE_CARD_VOLUME_LABEL = ''
@ -148,43 +149,47 @@ class Device(_Device):
return (msz, 0, csz)
@classmethod
def windows_match_device(cls, device_id):
def windows_match_device(self, pnp_id, device_id):
pnp_id = pnp_id.upper()
if device_id and pnp_id is not None:
device_id = device_id.upper()
if 'VEN_'+cls.VENDOR_NAME in device_id and \
'PROD_'+cls.PRODUCT_NAME in device_id:
return True
vid, pid = hex(cls.VENDOR_ID)[2:], hex(cls.PRODUCT_ID)[2:]
while len(vid) < 4: vid = '0' + vid
while len(pid) < 4: pid = '0' + pid
if 'VID_'+vid in device_id and 'PID_'+pid in device_id:
if 'VEN_' + self.VENDOR_NAME in pnp_id and 'PROD_' + device_id in pnp_id:
return True
return False
# This only supports Windows >= 2000
def open_windows(self):
drives = []
wmi = __import__('wmi', globals(), locals(), [], -1)
c = wmi.WMI()
for drive in c.Win32_DiskDrive():
if self.__class__.windows_match_device(str(drive.PNPDeviceID)):
if drive.Partitions == 0:
continue
def windows_get_drive_prefix(self, drive):
prefix = None
try:
partition = drive.associators("Win32_DiskDriveToDiskPartition")[0]
logical_disk = partition.associators('Win32_LogicalDiskToPartition')[0]
prefix = logical_disk.DeviceID + os.sep
drives.append((drive.Index, prefix))
except IndexError:
continue
pass
return prefix
def open_windows(self):
drives = {}
wmi = __import__('wmi', globals(), locals(), [], -1)
c = wmi.WMI()
for drive in c.Win32_DiskDrive():
if self.windows_match_device(str(drive.PNPDeviceID), WINDOWS_MAIN_MEM):
drives['main'] = self.windows_get_drive_prefix(drive)
elif self.windows_match_device(str(drive.PNPDeviceID), WINDOWS_CARD_MEM):
drives['card'] = self.windows_get_drive_prefix(drive)
if 'main' and 'card' in drives.keys():
break
if not drives:
raise DeviceError(_('Unable to detect the %s disk drive. Try rebooting.') % self.__class__.__name__)
drives.sort(cmp=lambda a, b: cmp(a[0], b[0]))
self._main_prefix = drives[0][1]
if len(drives) > 1:
self._card_prefix = drives[1][1]
self._main_prefix = drives['main'] if 'main' in names.keys() else None
self._card_prefix = drives['card'] if 'card' in names.keys() else None
@classmethod
def get_osx_mountpoints(self, raw=None):
@ -207,9 +212,9 @@ class Device(_Device):
break
for i, line in enumerate(lines):
if line.strip().endswith('<class IOMedia>') and self.OSX_NAME_MAIN_MEM in line:
if self.OSX_MAIN_MEM is not None and line.strip().endswith('<class IOMedia>') and self.OSX_MAIN_MEM in line:
get_dev_node(lines[i+1:], 'main')
if line.strip().endswith('<class IOMedia>') and self.OSX_NAME_CARD_MEM in line:
if self.OSX_CARD_MEM is not None and line.strip().endswith('<class IOMedia>') and self.OSX_CARD_MEM in line:
get_dev_node(lines[i+1:], 'card')
if len(names.keys()) == 2:
break

View File

@ -12,10 +12,11 @@ from itertools import cycle
from calibre.devices.usbms.device import Device
from calibre.devices.usbms.books import BookList, Book
from calibre.devices.errors import FreeSpaceError
from calibre.devices.mime import MIME_MAP
class USBMS(Device):
EBOOK_DIR = ''
MIME_MAP = {}
EBOOK_DIR_MAIN = ''
EBOOK_DIR_CARD = ''
FORMATS = []
def __init__(self, key='-1', log_packets=False, report_progress=None):
@ -35,11 +36,12 @@ class USBMS(Device):
return bl
prefix = self._card_prefix if oncard else self._main_prefix
ebook_dir = self.EBOOK_DIR_CARD if oncard else self.EBOOK_DIR_MAIN
# Get all books in all directories under the root EBOOK_DIR directory
for path, dirs, files in os.walk(os.path.join(prefix, self.EBOOK_DIR)):
# Get all books in all directories under the root ebook_dir directory
for path, dirs, files in os.walk(os.path.join(prefix, ebook_dir)):
# Filter out anything that isn't in the list of supported ebook types
for book_type in self.MIME_MAP.keys():
for book_type in self.FORMATS:
for filename in fnmatch.filter(files, '*.%s' % (book_type)):
title, author, mime = self.__class__.extract_book_metadata_by_filename(filename)
@ -51,9 +53,9 @@ class USBMS(Device):
raise ValueError(_('The reader has no storage card connected.'))
if not on_card:
path = os.path.join(self._main_prefix, self.EBOOK_DIR)
path = os.path.join(self._main_prefix, self.EBOOK_DIR_MAIN)
else:
path = os.path.join(self._card_prefix, self.EBOOK_DIR)
path = os.path.join(self._card_prefix, self.EBOOK_DIR_CARD)
sizes = map(os.path.getsize, files)
size = sum(sizes)
@ -136,9 +138,10 @@ class USBMS(Device):
else:
book_title = os.path.splitext(filename)[0].replace('_', ' ')
fileext = os.path.splitext(filename)[1]
if fileext in cls.MIME_MAP.keys():
book_mime = cls.MIME_MAP[fileext]
fileext = os.path.splitext(filename)[1][1:]
if fileext in cls.FORMATS:
book_mime = MIME_MAP[fileext] if fileext in MIME_MAP.keys() else 'Unknown'
return book_title, book_author, book_mime

View File

@ -67,6 +67,7 @@ def txt2opf(path, tdir, opts):
def pdf2opf(path, tdir, opts):
from calibre.ebooks.lrf.pdf.convert_from import generate_html
generate_html(path, tdir)
opts.dont_split_on_page_breaks = True
return os.path.join(tdir, 'metadata.opf')
def epub2opf(path, tdir, opts):

View File

@ -335,7 +335,7 @@ class PreProcessor(object):
# Fix pdftohtml markup
PDFTOHTML = [
# Remove <hr> tags
(re.compile(r'<hr.*?>', re.IGNORECASE), lambda match: '<span style="page-break-after:always"> </span>'),
(re.compile(r'<hr.*?>', re.IGNORECASE), lambda match: '<br />'),
# Remove page numbers
(re.compile(r'\d+<br>', re.IGNORECASE), lambda match: ''),
# Remove <br> and replace <br><br> with <p>
@ -560,7 +560,7 @@ class Processor(Parser):
hr = etree.Element('hr')
if elem.getprevious() is None:
elem.getparent()[:0] = [hr]
else:
elif elem.getparent() is not None:
insert = None
for i, c in enumerate(elem.getparent()):
if c is elem:

View File

@ -144,16 +144,15 @@ class ReBinary(object):
NSRMAP = {'': None, XML_NS: 'xml'}
def __init__(self, root, path, oeb, map=HTML_MAP):
self.path = path
self.item = item
self.logger = oeb.logger
self.dir = os.path.dirname(path)
self.manifest = oeb.manifest
self.tags, self.tattrs = map
self.buf = StringIO()
self.anchors = []
self.page_breaks = []
self.is_html = is_html = map is HTML_MAP
self.stylizer = Stylizer(root, path, oeb) if is_html else None
self.stylizer = Stylizer(root, item.href, oeb) if is_html else None
self.tree_to_binary(root)
self.content = self.buf.getvalue()
self.ahc = self.build_ahc() if is_html else None
@ -210,6 +209,8 @@ class ReBinary(object):
if attr in ('href', 'src'):
value = urlnormalize(value)
path, frag = urldefrag(value)
if self.item:
path = self.item.abshref(path)
prefix = unichr(3)
if path in self.manifest.hrefs:
prefix = unichr(2)
@ -222,7 +223,7 @@ class ReBinary(object):
elif attr.startswith('ms--'):
attr = '%' + attr[4:]
elif tag == 'link' and attr == 'type' and value in OEB_STYLES:
value = OEB_CSS_MIME
value = CSS_MIME
if attr in tattrs:
self.write(tattrs[attr])
else:
@ -275,7 +276,7 @@ class ReBinary(object):
def build_ahc(self):
if len(self.anchors) > 6:
self.logger.log_warn("More than six anchors in file %r. " \
"Some links may not work properly." % self.path)
"Some links may not work properly." % self.item.href)
data = StringIO()
data.write(unichr(len(self.anchors)).encode('utf-8'))
for anchor, offset in self.anchors:
@ -473,7 +474,7 @@ class LitWriter(object):
secnum = 0
if not isinstance(data, basestring):
self._add_folder(name)
rebin = ReBinary(data, item.href, self._oeb, map=HTML_MAP)
rebin = ReBinary(data, item, self._oeb, map=HTML_MAP)
self._add_file(name + '/ahc', rebin.ahc, 0)
self._add_file(name + '/aht', rebin.aht, 0)
item.page_breaks = rebin.page_breaks
@ -552,7 +553,7 @@ class LitWriter(object):
meta.attrib['ms--minimum_level'] = '0'
meta.attrib['ms--attr5'] = '1'
meta.attrib['ms--guid'] = '{%s}' % str(uuid.uuid4()).upper()
rebin = ReBinary(meta, 'content.opf', self._oeb, map=OPF_MAP)
rebin = ReBinary(meta, None, self._oeb, map=OPF_MAP)
meta = rebin.content
self._meta = meta
self._add_file('/meta', meta)

View File

@ -425,7 +425,7 @@ def do_convert(path_to_file, opts, notification=lambda m, p: p, output_format='l
thumbnail = None
if not pages:
raise ValueError('Could not find any pages in the comic: %s'%source)
if not opts.no_process:
if not getattr(opts, 'no_process', False):
pages, failures, tdir2 = process_pages(pages, opts, notification)
if not pages:
raise ValueError('Could not find any valid pages in the comic: %s'%source)
@ -443,7 +443,7 @@ def do_convert(path_to_file, opts, notification=lambda m, p: p, output_format='l
if output_format == 'pdf':
create_pdf(pages, opts.profile, opts, thumbnail=thumbnail)
shutil.rmtree(tdir)
if not opts.no_process:
if not getattr(opts, 'no_process', False):
shutil.rmtree(tdir2)
@ -457,7 +457,7 @@ def main(args=sys.argv, notification=None, output_format='lrf'):
if not callable(notification):
pb = ProgressBar(terminal_controller, _('Rendering comic pages...'),
no_progress_bar=opts.no_progress_bar)
no_progress_bar=opts.no_progress_bar or getattr(opts, 'no_process', False))
notification = pb.update
source = os.path.abspath(args[1])

View File

@ -122,7 +122,7 @@ class HTMLConverter(object, LoggingInterface):
# Fix pdftohtml markup
PDFTOHTML = [
# Remove <hr> tags
(re.compile(r'<hr.*?>', re.IGNORECASE), lambda match: '<span style="page-break-after:always"> </span>'),
(re.compile(r'<hr.*?>', re.IGNORECASE), lambda match: '<br />'),
# Remove page numbers
(re.compile(r'\d+<br>', re.IGNORECASE), lambda match: ''),
# Remove <br> and replace <br><br> with <p>

View File

@ -799,18 +799,39 @@ class Text(LRFStream):
length = len(self.stream)
style = self.style.as_dict()
current_style = style.copy()
text_tags = set(list(TextAttr.tag_map.keys()) + \
list(Text.text_tags.keys()) + \
list(ruby_tags.keys()))
text_tags -= set([0xf500+i for i in range(10)])
text_tags.add(0xf5cc)
while stream.tell() < length:
# Is there some text beofre a tag?
pos = self.stream.find('\xf5', stream.tell()) - 1
if pos > 0:
self.add_text(self.stream[stream.tell():pos])
stream.seek(pos)
elif pos == -2: # No tags in this stream
# Is there some text before a tag?
def find_first_tag(start):
pos = self.stream.find('\xf5', start)
if pos == -1:
return -1
try:
stream.seek(pos-1)
_t = Tag(stream)
if _t.id in text_tags:
return pos-1
return find_first_tag(pos+1)
except:
return find_first_tag(pos+1)
start_pos = stream.tell()
tag_pos = find_first_tag(start_pos)
if tag_pos >= start_pos:
if tag_pos > start_pos:
self.add_text(self.stream[start_pos:tag_pos])
stream.seek(tag_pos)
else: # No tags in this stream
self.add_text(self.stream)
stream.seek(0, 2)
print repr(self.stream)
break
tag = Tag(stream)
@ -1166,7 +1187,8 @@ class TOCObject(LRFStream):
refpage = struct.unpack("<I", stream.read(4))[0]
refobj = struct.unpack("<I", stream.read(4))[0]
cnt = struct.unpack("<H", stream.read(2))[0]
label = unicode(stream.read(cnt), "utf_16")
raw = stream.read(cnt)
label = raw.decode('utf_16_le')
self._contents.append(TocLabel(refpage, refobj, label))
c -= 1

View File

@ -33,7 +33,6 @@ class EXTHHeader(object):
self.length, self.num_items = struct.unpack('>LL', raw[4:12])
raw = raw[12:]
pos = 0
self.mi = MetaInformation('Unknown', ['Unknown'])
self.has_fake_cover = True
@ -49,9 +48,17 @@ class EXTHHeader(object):
self.cover_offset, = struct.unpack('>L', content)
elif id == 202:
self.thumbnail_offset, = struct.unpack('>L', content)
#else:
# print 'unknown record', id, repr(content)
title = re.search(r'\0+([^\0]+)\0+', raw[pos:])
if title:
self.mi.title = title.group(1).decode(codec, 'ignore')
title = title.group(1).decode(codec, 'replace')
if len(title) > 2:
self.mi.title = title
else:
title = re.search(r'\0+([^\0]+)\0+', ''.join(reversed(raw[pos:])))
if title:
self.mi.title = ''.join(reversed(title.group(1).decode(codec, 'replace')))
def process_metadata(self, id, content, codec):
@ -67,7 +74,8 @@ class EXTHHeader(object):
if not self.mi.tags:
self.mi.tags = []
self.mi.tags.append(content.decode(codec, 'ignore'))
#else:
# print 'unhandled metadata record', id, repr(content), codec
class BookHeader(object):
@ -466,6 +474,10 @@ def get_metadata(stream):
cover = os.path.join(tdir, mi.cover)
if os.access(cover, os.R_OK):
mi.cover_data = ('JPEG', open(os.path.join(tdir, mi.cover), 'rb').read())
else:
path = os.path.join(tdir, 'images', '00001.jpg')
if os.access(path, os.R_OK):
mi.cover_data = ('JPEG', open(path, 'rb').read())
return mi
def option_parser():

View File

@ -29,7 +29,7 @@ def config(defaults=None):
c.add_opt('top_right_y', [ '-w', '--righty'], default=default_crop,
help=_('Number of pixels to crop from the right most y (default is %d)')%default_crop )
c.add_opt('bounding', ['-b', '--bounding'],
help=_('A file generated by ghostscript which allows each page to be individually cropped'))
help=_('A file generated by ghostscript which allows each page to be individually cropped [gs -dSAFER -dNOPAUSE -dBATCH -sDEVICE=bbox > bounding] '))
return c
@ -38,14 +38,24 @@ def option_parser():
return c.option_parser(usage=_('''\
%prog [options] file.pdf
Crop a pdf.
Crops a pdf.
'''))
def main(args=sys.argv):
parser = option_parser()
opts, args = parser.parse_args(args)
try:
source = os.path.abspath(args[1])
input_pdf = PdfFileReader(file(source, "rb"))
except:
print "Unable to read input"
return 2
info = input_pdf.getDocumentInfo()
title = 'Unknown'
author = 'Unknown'
if info.title:
title = info.title
author = info.author
if opts.bounding != None:
try:
bounding = open( opts.bounding , 'r' )
@ -53,7 +63,7 @@ def main(args=sys.argv):
except:
print 'Error opening %s' % opts.bounding
return 1
output_pdf = PdfFileWriter()
output_pdf = PdfFileWriter(title=title,author=author)
for page_number in range (0, input_pdf.getNumPages() ):
page = input_pdf.getPage(page_number)
if opts.bounding != None:

View File

@ -75,7 +75,13 @@ def save_recipes(recipes):
def load_recipes():
config.refresh()
return [Recipe().unpickle(r) for r in config.get('scheduled_recipes', [])]
recipes = []
for r in config.get('scheduled_recipes', []):
r = Recipe().unpickle(r)
if r.builtin and not str(r.id).startswith('recipe_'):
continue
recipes.append(r)
return recipes
class RecipeModel(QAbstractListModel, SearchQueryParser):
@ -438,7 +444,7 @@ class Scheduler(QObject):
self.lock.unlock()
def main(args=sys.argv):
app = QApplication([])
QApplication([])
from calibre.library.database2 import LibraryDatabase2
d = SchedulerDialog(LibraryDatabase2('/home/kovid/documents/library'))
d.exec_()

Binary file not shown.

After

Width:  |  Height:  |  Size: 586 B

View File

@ -406,6 +406,7 @@ class Document(QGraphicsScene):
for font in lrf.font_map:
fdata = QByteArray(lrf.font_map[font].data)
id = QFontDatabase.addApplicationFontFromData(fdata)
if id != -1:
font_map[font] = [str(i) for i in QFontDatabase.applicationFontFamilies(id)][0]
if load_substitutions:

View File

@ -889,6 +889,9 @@ class Main(MainWindow, Ui_MainWindow):
ids = [id for id in ids if self.library_view.model().db.has_id(id)]
files = [self.library_view.model().db.format(id, prefs['output_format'], index_is_id=True, as_file=True) for id in ids]
files = [f for f in files if f is not None]
if not files:
dynamic.set('news_to_be_synced', set([]))
return
metadata = self.library_view.model().get_metadata(ids, rows_are_ids=True)
names = []
for mi in metadata:
@ -1479,7 +1482,8 @@ in which you want to store your books files. Any existing books will be automati
return True
def shutdown(self):
def shutdown(self, write_settings=True):
if write_settings:
self.write_settings()
self.job_manager.terminate_all_jobs()
self.device_manager.keep_going = False
@ -1500,6 +1504,7 @@ in which you want to store your books files. Any existing books will be automati
def closeEvent(self, e):
self.write_settings()
if self.system_tray_icon.isVisible():
if not dynamic['systray_msg'] and not isosx:
info_dialog(self, 'calibre', 'calibre '+_('will keep running in the system tray. To close it, choose <b>Quit</b> in the context menu of the system tray.')).exec_()
@ -1509,7 +1514,7 @@ in which you want to store your books files. Any existing books will be automati
else:
if self.confirm_quit():
try:
self.shutdown()
self.shutdown(write_settings=False)
except:
pass
e.accept()

View File

@ -1551,9 +1551,6 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE;
def has_book(self, mi):
return bool(self.conn.get('SELECT id FROM books where title=?', (mi.title,), all=False))
def has_id(self, id):
return self.conn.get('SELECT id FROM books where id=?', (id,), all=False) is not None

View File

@ -217,7 +217,11 @@ class ResultCache(SearchQueryParser):
return self.index(id)
def has_id(self, id):
try:
return self._data[id] is not None
except IndexError:
pass
return False
def refresh_ids(self, conn, ids):
for id in ids:
@ -558,6 +562,14 @@ class LibraryDatabase2(LibraryDatabase):
return img
return f if as_file else f.read()
def has_book(self, mi):
title = mi.title
if title:
if not isinstance(title, unicode):
title = title.decode(preferred_encoding, 'replace')
return bool(self.conn.get('SELECT id FROM books where title=?', (title,), all=False))
return False
def has_cover(self, index, index_is_id=False):
id = index if index_is_id else self.id(index)
path = os.path.join(self.library_path, self.path(id, index_is_id=True), 'cover.jpg')

View File

@ -217,8 +217,7 @@ class Server(object):
pos = pos.replace(month = 1)
else:
pos = pos.replace(month = pos.month + 1)
_months = list(months(self.earliest, self.latest))[:-1][:12]
_months = list(months(self.earliest, self.latest))[:-1][-12:]
_months = [range_for_month(*m) for m in _months]
_months = [self.get_slice(*m) for m in _months]
x = [m.min for m in _months]

View File

@ -35,7 +35,7 @@ class Distribution(object):
('xdg-utils', '1.0.2', 'xdg-utils', 'xdg-utils', 'xdg-utils'),
('dbus-python', '0.82.2', 'dbus-python', 'python-dbus', 'dbus-python'),
('lxml', '2.0.5', 'lxml', 'python-lxml', 'python-lxml'),
('BeautifulSoup', '3.0.5', 'beautifulsoup', 'python-beautifulsoup', 'python-beautifulsoup'),
('BeautifulSoup', '3.0.5', 'beautifulsoup', 'python-beautifulsoup', 'python-BeautifulSoup'),
('help2man', '1.36.4', 'help2man', 'help2man', 'help2man'),
]
@ -205,23 +205,7 @@ select Install.</li>
<ol>
<li>Before trying to use the command line tools, you must run the app at least once. This will ask you for you password and then setup the symbolic links for the command line tools.</li>
<li>The app cannot be run from within the dmg. You must drag it to a folder on your filesystem (The Desktop, Applications, wherever).</li>
<li>In order for the conversion of RTF to LRF to support WMF images (common in older RTF files) you need to install ImageMagick.</li>
<li>In order for localization of the user interface in your language you must create the file <code>~/.MacOSX/environment.plist</code> as shown below:
<pre class="wiki">
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"&gt;
&lt;plist version="1.0"&gt;
&lt;dict&gt;
&lt;key&gt;LANG&lt;/key&gt;
&lt;string&gt;de_DE&lt;/string&gt;
&lt;/dict&gt;
&lt;/plist&gt;
</pre>
The example above is for the German language. Substitute the language code you need.
After creating the file you need to log out and log in again for the changes to become
active. Of course, this will only work if calibre has been translated for your language.
If not, head over to <a href="http://calibre.kovidgoyal.net/wiki/Development#Translations">Translations</a> to see how you can translate it.
</li>
<li>In order for localization of the user interface in your language, select your language in the configuration dialog (by clicking the hammer icon next to the search bar) and select your language.</li>
</ol>
'''))
return 'binary.html', data, None

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -338,7 +338,7 @@ class ZipInfo (object):
if isinstance(self.filename, unicode):
try:
return self.filename.encode('ascii'), self.flag_bits
except UnicodeEncodeError:
except:
return self.filename.encode('utf-8'), self.flag_bits | 0x800
else:
return self.filename, self.flag_bits

View File

@ -765,6 +765,8 @@ class BasicNewsRecipe(object, LoggingInterface):
self.log_debug(traceback.format_exc())
if cu is not None:
ext = cu.rpartition('.')[-1]
if '?' in ext:
ext = ''
ext = ext.lower() if ext else 'jpg'
self.report_progress(1, _('Downloading cover from %s')%cu)
cpath = os.path.join(self.output_dir, 'cover.'+ext)

View File

@ -21,7 +21,7 @@ recipe_modules = ['recipe_' + r for r in (
'linux_magazine', 'telegraph_uk', 'utne', 'sciencedaily', 'forbes',
'time_magazine', 'endgadget', 'fudzilla', 'nspm_int', 'nspm', 'pescanik',
'spiegel_int', 'themarketticker', 'tomshardware', 'xkcd', 'ftd', 'zdnet',
'joelonsoftware',
'joelonsoftware', 'telepolis', 'common_dreams', 'nin',
)]
import re, imp, inspect, time, os

View File

@ -42,3 +42,9 @@ class ChristianScienceMonitor(BasicNewsRecipe):
feeds[-1][1].append(art)
return feeds
def postprocess_html(self, soup, first_fetch):
html = soup.find('html')
if html is None:
return soup
html.extract()
return html

View File

@ -0,0 +1,16 @@
from calibre.web.feeds.news import BasicNewsRecipe
class CommonDreams(BasicNewsRecipe):
title = u'Common Dreams'
description = u'Progressive news and views'
__author__ = u'XanthanGum'
oldest_article = 7
max_articles_per_feed = 100
feeds = [
(u'Common Dreams Headlines',
u'http://www.commondreams.org/feed/headlines_rss'),
(u'Common Dreams Views', u'http://www.commondreams.org/feed/views_rss'),
(u'Common Dreams Newswire', u'http://www.commondreams.org/feed/newswire_rss')
]

View File

@ -49,7 +49,9 @@ class Economist(BasicNewsRecipe):
if not index_started:
continue
text = string.capwords(text)
if text not in feeds.keys():
feeds[text] = []
if text not in ans:
ans.append(text)
key = text
continue

View File

@ -0,0 +1,55 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2008, Darko Miletic <darko.miletic at gmail.com>'
'''
nin.co.yu
'''
import re, urllib
from calibre.web.feeds.news import BasicNewsRecipe
class Nin(BasicNewsRecipe):
title = 'NIN online'
__author__ = 'Darko Miletic'
description = 'Nedeljne informativne novine'
no_stylesheets = True
oldest_article = 15
simultaneous_downloads = 1
delay = 1
encoding = 'utf8'
needs_subscription = True
PREFIX = 'http://www.nin.co.yu'
INDEX = PREFIX + '/?change_lang=ls'
LOGIN = PREFIX + '/?logout=true'
html2lrf_options = [
'--comment' , description
, '--category' , 'news, politics, Serbia'
, '--publisher' , 'NIN'
]
preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')]
def get_browser(self):
br = BasicNewsRecipe.get_browser()
br.open(self.INDEX)
if self.username is not None and self.password is not None:
data = urllib.urlencode({ 'login_name':self.username
,'login_password':self.password
,'imageField.x':'32'
,'imageField.y':'15'
})
br.open(self.LOGIN,data)
return br
keep_only_tags =[dict(name='td', attrs={'width':'520'})]
remove_tags_after =dict(name='html')
feeds =[(u'NIN', u'http://www.nin.co.yu/misc/rss.php?feed=RSS2.0')]
def get_cover_url(self):
cover_url = None
soup = self.index_to_soup(self.INDEX)
link_item = soup.find('img',attrs={'width':'100','height':'137','border':'0'})
if link_item:
cover_url = self.PREFIX + link_item['src']
return cover_url

View File

@ -0,0 +1,34 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2008, Darko Miletic <darko.miletic at gmail.com>'
'''
www.heise.de/tp
'''
from calibre.web.feeds.news import BasicNewsRecipe
class Telepolis(BasicNewsRecipe):
title = 'Telepolis'
__author__ = 'Darko Miletic'
description = 'News from Germany in German'
oldest_article = 2
max_articles_per_feed = 100
no_stylesheets = True
use_embedded_content = False
encoding = 'utf-8'
html2lrf_options = [ '--comment' , description
, '--category' , 'blog,news'
]
keep_only_tags = [
dict(name='table', attrs={'class':'inhalt-table'})
,dict(name='table', attrs={'class':'blogtable' })
]
remove_tags = [
dict(name='table', attrs={'class':'img' })
,dict(name='img' , attrs={'src':'/tp/r4/icons/inline/extlink.gif'})
]
feeds = [(u'Telepolis Newsfeed', u'http://www.heise.de/tp/news.rdf')]

View File

@ -33,6 +33,7 @@ class TimesOnline(BasicNewsRecipe):
('Sports News', 'http://www.timesonline.co.uk/tol/feeds/rss/sport.xml'),
('Film News', 'http://www.timesonline.co.uk/tol/feeds/rss/film.xml'),
('Tech news', 'http://www.timesonline.co.uk/tol/feeds/rss/tech.xml'),
('Literary Supplement', 'http://www.timesonline.co.uk/tol/feeds/rss/thetls.xml'),
]
def print_version(self, url):

View File

@ -55,7 +55,7 @@ from utils import readNonWhitespace, readUntilWhitespace, ConvertFunctionsToVirt
# This class supports writing PDF files out, given pages produced by another
# class (typically {@link #PdfFileReader PdfFileReader}).
class PdfFileWriter(object):
def __init__(self):
def __init__(self,title=u"Unknown",author=u"Unknown"):
self._header = "%PDF-1.3"
self._objects = [] # array of indirect objects
@ -71,7 +71,9 @@ class PdfFileWriter(object):
# info object
info = DictionaryObject()
info.update({
NameObject("/Producer"): createStringObject(u"Python PDF Library - http://pybrary.net/pyPdf/")
NameObject("/Producer"): createStringObject(u"Python PDF Library - http://pybrary.net/pyPdf/"),
NameObject("/Author"): createStringObject(author),
NameObject("/Title"): createStringObject(title),
})
self._info = self._addObject(info)