mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Sync to trunk.
This commit is contained in:
commit
e9a27c63e0
@ -352,6 +352,7 @@ def main():
|
|||||||
'BeautifulSoup', 'calibre.ebooks.lrf.fonts.prs500.*',
|
'BeautifulSoup', 'calibre.ebooks.lrf.fonts.prs500.*',
|
||||||
'dateutil', 'email.iterators',
|
'dateutil', 'email.iterators',
|
||||||
'email.generator',
|
'email.generator',
|
||||||
|
'calibre.ebooks.metadata.amazon',
|
||||||
],
|
],
|
||||||
'packages' : ['PIL', 'Authorization', 'lxml', 'dns'],
|
'packages' : ['PIL', 'Authorization', 'lxml', 'dns'],
|
||||||
'excludes' : ['IPython'],
|
'excludes' : ['IPython'],
|
||||||
|
@ -70,11 +70,18 @@ def option_recommendation_to_cli_option(add_option, rec):
|
|||||||
switches.append('--'+opt.long_switch)
|
switches.append('--'+opt.long_switch)
|
||||||
attrs = dict(dest=opt.name, help=opt.help,
|
attrs = dict(dest=opt.name, help=opt.help,
|
||||||
choices=opt.choices, default=rec.recommended_value)
|
choices=opt.choices, default=rec.recommended_value)
|
||||||
if opt.long_switch == 'verbose':
|
|
||||||
attrs['action'] = 'count'
|
|
||||||
if isinstance(rec.recommended_value, type(True)):
|
if isinstance(rec.recommended_value, type(True)):
|
||||||
attrs['action'] = 'store_false' if rec.recommended_value else \
|
attrs['action'] = 'store_false' if rec.recommended_value else \
|
||||||
'store_true'
|
'store_true'
|
||||||
|
else:
|
||||||
|
if isinstance(rec.recommended_value, int):
|
||||||
|
attrs['type'] = 'int'
|
||||||
|
if isinstance(rec.recommended_value, float):
|
||||||
|
attrs['type'] = 'float'
|
||||||
|
|
||||||
|
if opt.long_switch == 'verbose':
|
||||||
|
attrs['action'] = 'count'
|
||||||
|
attrs.pop('type', '')
|
||||||
add_option(Option(*switches, **attrs))
|
add_option(Option(*switches, **attrs))
|
||||||
|
|
||||||
def add_input_output_options(parser, plumber):
|
def add_input_output_options(parser, plumber):
|
||||||
|
@ -616,8 +616,7 @@ OptionRecommendation(name='list_recipes',
|
|||||||
self.opts.dest = self.opts.output_profile
|
self.opts.dest = self.opts.output_profile
|
||||||
|
|
||||||
from calibre.ebooks.oeb.transforms.metadata import MergeMetadata
|
from calibre.ebooks.oeb.transforms.metadata import MergeMetadata
|
||||||
MergeMetadata()(self.oeb, self.user_metadata,
|
MergeMetadata()(self.oeb, self.user_metadata, self.opts)
|
||||||
self.opts.prefer_metadata_cover)
|
|
||||||
pr(0.2)
|
pr(0.2)
|
||||||
self.flush()
|
self.flush()
|
||||||
|
|
||||||
|
@ -58,8 +58,8 @@ class LRFOptions(object):
|
|||||||
|
|
||||||
|
|
||||||
for x in ('top', 'bottom', 'left', 'right'):
|
for x in ('top', 'bottom', 'left', 'right'):
|
||||||
setattr(self, x+'_margin', (self.profile.dpi/72.) * getattr(opts,
|
setattr(self, x+'_margin',
|
||||||
'margin_'+x))
|
(self.profile.dpi/72.) * float(getattr(opts, 'margin_'+x)))
|
||||||
|
|
||||||
for x in ('wordspace', 'header', 'header_format',
|
for x in ('wordspace', 'header', 'header_format',
|
||||||
'minimum_indent', 'serif_family',
|
'minimum_indent', 'serif_family',
|
||||||
|
@ -15,8 +15,8 @@ from calibre import relpath
|
|||||||
|
|
||||||
_author_pat = re.compile(',?\s+and\s+', re.IGNORECASE)
|
_author_pat = re.compile(',?\s+and\s+', re.IGNORECASE)
|
||||||
def string_to_authors(raw):
|
def string_to_authors(raw):
|
||||||
raw = _author_pat.sub('&', raw)
|
|
||||||
raw = raw.replace('&&', u'\uffff')
|
raw = raw.replace('&&', u'\uffff')
|
||||||
|
raw = _author_pat.sub('&', raw)
|
||||||
authors = [a.strip().replace(u'\uffff', '&') for a in raw.split('&')]
|
authors = [a.strip().replace(u'\uffff', '&') for a in raw.split('&')]
|
||||||
return authors
|
return authors
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ from __future__ import with_statement
|
|||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2008, Marshall T. Vandegrift <llasram@gmail.com>'
|
__copyright__ = '2008, Marshall T. Vandegrift <llasram@gmail.com>'
|
||||||
|
|
||||||
import sys, os, uuid, copy, re
|
import sys, os, uuid, copy, re, cStringIO
|
||||||
from itertools import izip
|
from itertools import izip
|
||||||
from urlparse import urldefrag, urlparse
|
from urlparse import urldefrag, urlparse
|
||||||
from urllib import unquote as urlunquote
|
from urllib import unquote as urlunquote
|
||||||
@ -22,7 +22,7 @@ from calibre.ebooks.oeb.base import OEB_DOCS, OEB_STYLES, OEB_IMAGES, \
|
|||||||
PAGE_MAP_MIME, JPEG_MIME, NCX_MIME, SVG_MIME
|
PAGE_MAP_MIME, JPEG_MIME, NCX_MIME, SVG_MIME
|
||||||
from calibre.ebooks.oeb.base import XMLDECL_RE, COLLAPSE_RE, \
|
from calibre.ebooks.oeb.base import XMLDECL_RE, COLLAPSE_RE, \
|
||||||
ENTITY_RE, MS_COVER_TYPE, iterlinks
|
ENTITY_RE, MS_COVER_TYPE, iterlinks
|
||||||
from calibre.ebooks.oeb.base import namespace, barename, qname, XPath, xpath, \
|
from calibre.ebooks.oeb.base import namespace, barename, XPath, xpath, \
|
||||||
urlnormalize, BINARY_MIME, \
|
urlnormalize, BINARY_MIME, \
|
||||||
OEBError, OEBBook, DirContainer
|
OEBError, OEBBook, DirContainer
|
||||||
from calibre.ebooks.oeb.writer import OEBWriter
|
from calibre.ebooks.oeb.writer import OEBWriter
|
||||||
@ -30,6 +30,7 @@ from calibre.ebooks.oeb.entitydefs import ENTITYDEFS
|
|||||||
from calibre.ebooks.metadata.epub import CoverRenderer
|
from calibre.ebooks.metadata.epub import CoverRenderer
|
||||||
from calibre.startup import get_lang
|
from calibre.startup import get_lang
|
||||||
from calibre.ptempfile import TemporaryDirectory
|
from calibre.ptempfile import TemporaryDirectory
|
||||||
|
from calibre.constants import __appname__, __version__
|
||||||
|
|
||||||
__all__ = ['OEBReader']
|
__all__ = ['OEBReader']
|
||||||
|
|
||||||
@ -123,53 +124,25 @@ class OEBReader(object):
|
|||||||
return opf
|
return opf
|
||||||
|
|
||||||
def _metadata_from_opf(self, opf):
|
def _metadata_from_opf(self, opf):
|
||||||
uid = opf.get('unique-identifier', None)
|
from calibre.ebooks.metadata.opf2 import OPF
|
||||||
self.oeb.uid = None
|
from calibre.ebooks.metadata import MetaInformation
|
||||||
metadata = self.oeb.metadata
|
from calibre.ebooks.oeb.transforms.metadata import meta_info_to_oeb_metadata
|
||||||
for elem in xpath(opf, '/o2:package/o2:metadata//*'):
|
stream = cStringIO.StringIO(etree.tostring(opf))
|
||||||
term = elem.tag
|
mi = MetaInformation(OPF(stream))
|
||||||
value = elem.text
|
if not mi.title:
|
||||||
attrib = dict(elem.attrib)
|
mi.title = self.oeb.translate(__('Unknown'))
|
||||||
nsmap = elem.nsmap
|
if not mi.authors:
|
||||||
if term == OPF('meta'):
|
mi.authors = [self.oeb.translate(__('Unknown'))]
|
||||||
term = qname(attrib.pop('name', None), nsmap)
|
if not mi.book_producer:
|
||||||
value = attrib.pop('content', None)
|
mi.book_producer = '%(a)s (%(v)s) [http://%(a)s.kovidgoyal.net]'%\
|
||||||
if value:
|
dict(a=__appname__, v=__version__)
|
||||||
value = COLLAPSE_RE.sub(' ', value.strip())
|
if not mi.language:
|
||||||
if term and (value or attrib):
|
mi.language = get_lang()
|
||||||
metadata.add(term, value, attrib, nsmap=nsmap)
|
meta_info_to_oeb_metadata(mi, self.oeb.metadata, self.logger)
|
||||||
haveuuid = haveid = False
|
bookid = "urn:uuid:%s" % str(uuid.uuid4()) if mi.application_id is None \
|
||||||
for ident in metadata.identifier:
|
else mi.application_id
|
||||||
if unicode(ident).startswith('urn:uuid:'):
|
self.oeb.metadata.add('identifier', bookid, id='calibre-uuid')
|
||||||
haveuuid = True
|
self.oeb.uid = self.oeb.metadata.identifier[0]
|
||||||
if 'id' in ident.attrib:
|
|
||||||
haveid = True
|
|
||||||
if not (haveuuid and haveid):
|
|
||||||
bookid = "urn:uuid:%s" % str(uuid.uuid4())
|
|
||||||
metadata.add('identifier', bookid, id='calibre-uuid')
|
|
||||||
if uid is None:
|
|
||||||
self.logger.warn(u'Unique-identifier not specified')
|
|
||||||
for item in metadata.identifier:
|
|
||||||
if not item.id:
|
|
||||||
continue
|
|
||||||
if uid is None or item.id == uid:
|
|
||||||
self.oeb.uid = item
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
self.logger.warn(u'Unique-identifier %r not found' % uid)
|
|
||||||
for ident in metadata.identifier:
|
|
||||||
if 'id' in ident.attrib:
|
|
||||||
self.oeb.uid = metadata.identifier[0]
|
|
||||||
break
|
|
||||||
if not metadata.language:
|
|
||||||
self.logger.warn(u'Language not specified')
|
|
||||||
metadata.add('language', get_lang())
|
|
||||||
if not metadata.creator:
|
|
||||||
self.logger.warn('Creator not specified')
|
|
||||||
metadata.add('creator', self.oeb.translate(__('Unknown')))
|
|
||||||
if not metadata.title:
|
|
||||||
self.logger.warn('Title not specified')
|
|
||||||
metadata.add('title', self.oeb.translate(__('Unknown')))
|
|
||||||
|
|
||||||
def _manifest_prune_invalid(self):
|
def _manifest_prune_invalid(self):
|
||||||
'''
|
'''
|
||||||
|
@ -169,11 +169,11 @@ class Stylizer(object):
|
|||||||
if not matches and class_sel_pat.match(text):
|
if not matches and class_sel_pat.match(text):
|
||||||
found = False
|
found = False
|
||||||
for x in tree.xpath('//*[@class]'):
|
for x in tree.xpath('//*[@class]'):
|
||||||
if x.get('class').lower() == text[1:].lower():
|
if text.lower().endswith('.'+x.get('class').lower()):
|
||||||
matches.append(x)
|
matches.append(x)
|
||||||
found = True
|
found = True
|
||||||
if found:
|
if found:
|
||||||
self.logger.warn('Ignoring case mismatch for CSS selector: %s in %s'
|
self.logger.warn('Ignoring case mismatches for CSS selector: %s in %s'
|
||||||
%(text, item.href))
|
%(text, item.href))
|
||||||
for elem in matches:
|
for elem in matches:
|
||||||
self.style(elem)._update_cssdict(cssdict)
|
self.style(elem)._update_cssdict(cssdict)
|
||||||
|
@ -8,66 +8,65 @@ __docformat__ = 'restructuredtext en'
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
def meta_info_to_oeb_metadata(mi, m, log):
|
||||||
|
if mi.title:
|
||||||
|
m.clear('title')
|
||||||
|
m.add('title', mi.title)
|
||||||
|
if mi.title_sort:
|
||||||
|
if not m.title:
|
||||||
|
m.add('title', mi.title_sort)
|
||||||
|
m.title[0].file_as = mi.title_sort
|
||||||
|
if mi.authors:
|
||||||
|
m.filter('creator', lambda x : x.role.lower() == 'aut')
|
||||||
|
for a in mi.authors:
|
||||||
|
attrib = {'role':'aut'}
|
||||||
|
if mi.author_sort:
|
||||||
|
attrib['file_as'] = mi.author_sort
|
||||||
|
m.add('creator', a, attrib=attrib)
|
||||||
|
if mi.book_producer:
|
||||||
|
m.filter('contributor', lambda x : x.role.lower() == 'bkp')
|
||||||
|
m.add('contributor', mi.book_producer, role='bkp')
|
||||||
|
if mi.comments:
|
||||||
|
m.clear('description')
|
||||||
|
m.add('description', mi.comments)
|
||||||
|
if mi.publisher:
|
||||||
|
m.clear('publisher')
|
||||||
|
m.add('publisher', mi.publisher)
|
||||||
|
if mi.series:
|
||||||
|
m.clear('series')
|
||||||
|
m.add('series', mi.series)
|
||||||
|
if mi.isbn:
|
||||||
|
has = False
|
||||||
|
for x in m.identifier:
|
||||||
|
if x.scheme.lower() == 'isbn':
|
||||||
|
x.content = mi.isbn
|
||||||
|
has = True
|
||||||
|
if not has:
|
||||||
|
m.add('identifier', mi.isbn, scheme='ISBN')
|
||||||
|
if mi.language:
|
||||||
|
m.clear('language')
|
||||||
|
m.add('language', mi.language)
|
||||||
|
if mi.series_index is not None:
|
||||||
|
m.clear('series_index')
|
||||||
|
m.add('series_index', mi.format_series_index())
|
||||||
|
if mi.rating is not None:
|
||||||
|
m.clear('rating')
|
||||||
|
m.add('rating', '%.2f'%mi.rating)
|
||||||
|
if mi.tags:
|
||||||
|
m.clear('subject')
|
||||||
|
for t in mi.tags:
|
||||||
|
m.add('subject', t)
|
||||||
|
|
||||||
|
|
||||||
class MergeMetadata(object):
|
class MergeMetadata(object):
|
||||||
'Merge in user metadata, including cover'
|
'Merge in user metadata, including cover'
|
||||||
|
|
||||||
def __call__(self, oeb, mi, prefer_metadata_cover=False,
|
def __call__(self, oeb, mi, opts):
|
||||||
prefer_author_sort=False):
|
|
||||||
from calibre.ebooks.oeb.base import DC
|
|
||||||
self.oeb, self.log = oeb, oeb.log
|
self.oeb, self.log = oeb, oeb.log
|
||||||
m = self.oeb.metadata
|
m = self.oeb.metadata
|
||||||
|
meta_info_to_oeb_metadata(mi, m, oeb.log)
|
||||||
self.log('Merging user specified metadata...')
|
self.log('Merging user specified metadata...')
|
||||||
if mi.title:
|
cover_id = self.set_cover(mi, opts.prefer_metadata_cover)
|
||||||
m.clear('title')
|
|
||||||
m.add('title', mi.title)
|
|
||||||
if mi.title_sort:
|
|
||||||
if not m.title:
|
|
||||||
m.add(DC('title'), mi.title_sort)
|
|
||||||
m.title[0].file_as = mi.title_sort
|
|
||||||
if prefer_author_sort and mi.author_sort:
|
|
||||||
mi.authors = [mi.author_sort]
|
|
||||||
if mi.authors:
|
|
||||||
m.filter('creator', lambda x : x.role.lower() == 'aut')
|
|
||||||
for a in mi.authors:
|
|
||||||
attrib = {'role':'aut'}
|
|
||||||
if mi.author_sort:
|
|
||||||
attrib['file_as'] = mi.author_sort
|
|
||||||
m.add('creator', a, attrib=attrib)
|
|
||||||
if mi.comments:
|
|
||||||
m.clear('description')
|
|
||||||
m.add('description', mi.comments)
|
|
||||||
if mi.publisher:
|
|
||||||
m.clear('publisher')
|
|
||||||
m.add('publisher', mi.publisher)
|
|
||||||
if mi.series:
|
|
||||||
m.clear('series')
|
|
||||||
m.add('series', mi.series)
|
|
||||||
if mi.isbn:
|
|
||||||
has = False
|
|
||||||
for x in m.identifier:
|
|
||||||
if x.scheme.lower() == 'isbn':
|
|
||||||
x.content = mi.isbn
|
|
||||||
has = True
|
|
||||||
if not has:
|
|
||||||
m.add('identifier', mi.isbn, scheme='ISBN')
|
|
||||||
if mi.language:
|
|
||||||
m.clear('language')
|
|
||||||
m.add('language', mi.language)
|
|
||||||
if mi.book_producer:
|
|
||||||
m.filter('creator', lambda x : x.role.lower() == 'bkp')
|
|
||||||
m.add('creator', mi.book_producer, role='bkp')
|
|
||||||
if mi.series_index is not None:
|
|
||||||
m.clear('series_index')
|
|
||||||
m.add('series_index', mi.format_series_index())
|
|
||||||
if mi.rating is not None:
|
|
||||||
m.clear('rating')
|
|
||||||
m.add('rating', '%.2f'%mi.rating)
|
|
||||||
if mi.tags:
|
|
||||||
m.clear('subject')
|
|
||||||
for t in mi.tags:
|
|
||||||
m.add('subject', t)
|
|
||||||
|
|
||||||
cover_id = self.set_cover(mi, prefer_metadata_cover)
|
|
||||||
m.clear('cover')
|
m.clear('cover')
|
||||||
if cover_id is not None:
|
if cover_id is not None:
|
||||||
m.add('cover', cover_id)
|
m.add('cover', cover_id)
|
||||||
|
@ -69,7 +69,7 @@ def pdftohtml(output_dir, pdf_path, no_images):
|
|||||||
if not os.path.exists(index) or os.stat(index).st_size < 100:
|
if not os.path.exists(index) or os.stat(index).st_size < 100:
|
||||||
raise DRMError()
|
raise DRMError()
|
||||||
|
|
||||||
with open(index, 'rb+wb') as i:
|
with open(index, 'r+b') as i:
|
||||||
raw = i.read()
|
raw = i.read()
|
||||||
raw = '<!-- created by calibre\'s pdftohtml -->\n' + raw
|
raw = '<!-- created by calibre\'s pdftohtml -->\n' + raw
|
||||||
i.seek(0)
|
i.seek(0)
|
||||||
|
@ -318,8 +318,9 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
|||||||
pm = QMenu()
|
pm = QMenu()
|
||||||
ap = self.action_preferences
|
ap = self.action_preferences
|
||||||
pm.addAction(ap.icon(), ap.text())
|
pm.addAction(ap.icon(), ap.text())
|
||||||
pm.addAction(self.preferences_action)
|
|
||||||
pm.addAction(_('Run welcome wizard'))
|
pm.addAction(_('Run welcome wizard'))
|
||||||
|
self.connect(pm.actions()[0], SIGNAL('triggered(bool)'),
|
||||||
|
self.do_config)
|
||||||
self.connect(pm.actions()[1], SIGNAL('triggered(bool)'),
|
self.connect(pm.actions()[1], SIGNAL('triggered(bool)'),
|
||||||
self.run_wizard)
|
self.run_wizard)
|
||||||
self.action_preferences.setMenu(pm)
|
self.action_preferences.setMenu(pm)
|
||||||
@ -933,16 +934,14 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
|||||||
details = ['%s: %s'%(title, reason) for title,
|
details = ['%s: %s'%(title, reason) for title,
|
||||||
reason in x.failures.values()]
|
reason in x.failures.values()]
|
||||||
details = '%s\n'%('\n'.join(details))
|
details = '%s\n'%('\n'.join(details))
|
||||||
warning_dialog(_('Failed to download some metadata'),
|
warning_dialog(self, _('Failed to download some metadata'),
|
||||||
_('Failed to download metadata for the following:'),
|
_('Failed to download metadata for the following:'),
|
||||||
details, self).exec_()
|
det_msg=details).exec_()
|
||||||
else:
|
else:
|
||||||
err = _('Failed to download metadata:')
|
err = _('Failed to download metadata:')
|
||||||
error_dialog(self, _('Error'), err, det_msg=x.tb).exec_()
|
error_dialog(self, _('Error'), err, det_msg=x.tb).exec_()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def edit_metadata(self, checked, bulk=None):
|
def edit_metadata(self, checked, bulk=None):
|
||||||
'''
|
'''
|
||||||
Edit metadata of selected books in library.
|
Edit metadata of selected books in library.
|
||||||
|
@ -482,6 +482,9 @@ class Wizard(QWizard):
|
|||||||
self.device_page = DevicePage()
|
self.device_page = DevicePage()
|
||||||
self.library_page = LibraryPage()
|
self.library_page = LibraryPage()
|
||||||
self.finish_page = FinishPage()
|
self.finish_page = FinishPage()
|
||||||
|
bt = unicode(self.buttonText(self.FinishButton))
|
||||||
|
t = unicode(self.finish_page.finish_text.text())
|
||||||
|
self.finish_page.finish_text.setText(t%bt)
|
||||||
self.kindle_page = KindlePage()
|
self.kindle_page = KindlePage()
|
||||||
self.stanza_page = StanzaPage()
|
self.stanza_page = StanzaPage()
|
||||||
self.setPage(self.library_page.ID, self.library_page)
|
self.setPage(self.library_page.ID, self.library_page)
|
||||||
|
@ -21,9 +21,9 @@
|
|||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="label">
|
<widget class="QLabel" name="finish_text">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string><h2>Congratulations!</h2> You have succesfully setup calibre. Press the Finish button to apply your settings.</string>
|
<string><h2>Congratulations!</h2> You have successfully setup calibre. Press the %s button to apply your settings.</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="wordWrap">
|
<property name="wordWrap">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
|
@ -12,7 +12,7 @@ from ctypes import Structure as _Structure, c_char_p, c_uint, c_void_p, POINTER,
|
|||||||
from tempfile import NamedTemporaryFile
|
from tempfile import NamedTemporaryFile
|
||||||
from StringIO import StringIO
|
from StringIO import StringIO
|
||||||
|
|
||||||
from calibre import iswindows, load_library, CurrentDir
|
from calibre import iswindows, load_library, CurrentDir, prints
|
||||||
from calibre.ptempfile import TemporaryDirectory
|
from calibre.ptempfile import TemporaryDirectory
|
||||||
|
|
||||||
_librar_name = 'libunrar'
|
_librar_name = 'libunrar'
|
||||||
@ -173,7 +173,7 @@ def extract(path, dir):
|
|||||||
try:
|
try:
|
||||||
if open_archive_data.OpenResult != 0:
|
if open_archive_data.OpenResult != 0:
|
||||||
raise UnRARException(_interpret_open_error(open_archive_data.OpenResult, path))
|
raise UnRARException(_interpret_open_error(open_archive_data.OpenResult, path))
|
||||||
print 'Archive:', path
|
prints('Archive:', path)
|
||||||
#print get_archive_info(open_archive_data.Flags)
|
#print get_archive_info(open_archive_data.Flags)
|
||||||
header_data = RARHeaderDataEx(CmtBuf=None)
|
header_data = RARHeaderDataEx(CmtBuf=None)
|
||||||
#_libunrar.RARSetCallback(arc_data, callback_func, mode)
|
#_libunrar.RARSetCallback(arc_data, callback_func, mode)
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -13,6 +13,96 @@ import time, atexit, os
|
|||||||
class LockError(Exception):
|
class LockError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
class WindowsExclFile(object):
|
||||||
|
|
||||||
|
def __init__(self, path, timeout=20):
|
||||||
|
self.name = path
|
||||||
|
import win32file as w
|
||||||
|
import pywintypes
|
||||||
|
|
||||||
|
while timeout > 0:
|
||||||
|
timeout -= 1
|
||||||
|
try:
|
||||||
|
self._handle = w.CreateFile(path,
|
||||||
|
w.GENERIC_READ|w.GENERIC_WRITE, # Open for reading and writing
|
||||||
|
0, # Open exclusive
|
||||||
|
None, # No security attributes
|
||||||
|
w.OPEN_ALWAYS, # If file does not exist, create it
|
||||||
|
w.FILE_ATTRIBUTE_NORMAL, #Normal attributes
|
||||||
|
None, #No template file
|
||||||
|
)
|
||||||
|
break
|
||||||
|
except pywintypes.error, err:
|
||||||
|
if getattr(err, 'args', [-1])[0] in (0x20, 0x21):
|
||||||
|
time.sleep(1)
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
|
def seek(self, amt, frm=0):
|
||||||
|
import win32file as w
|
||||||
|
if frm not in (0, 1, 2):
|
||||||
|
raise ValueError('Invalid from for seek: %s'%frm)
|
||||||
|
frm = {0:w.FILE_BEGIN, 1: w.FILE_CURRENT, 2:w.FILE_END}[frm]
|
||||||
|
if frm is w.FILE_END:
|
||||||
|
amt = 0 - amt
|
||||||
|
w.SetFilePointer(self._handle, amt, frm)
|
||||||
|
|
||||||
|
def tell(self):
|
||||||
|
import win32file as w
|
||||||
|
return w.SetFilePointer(self._handle, 0, w.FILE_CURRENT)
|
||||||
|
|
||||||
|
def flush(self):
|
||||||
|
import win32file as w
|
||||||
|
w.FlushFileBuffers(self._handle)
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
if self._handle is not None:
|
||||||
|
import win32file as w
|
||||||
|
self.flush()
|
||||||
|
w.CloseHandle(self._handle)
|
||||||
|
self._handle = None
|
||||||
|
|
||||||
|
def read(self, bytes=-1):
|
||||||
|
import win32file as w
|
||||||
|
sz = w.GetFileSize(self._handle)
|
||||||
|
max = sz - self.tell()
|
||||||
|
if bytes < 0: bytes = max
|
||||||
|
bytes = min(max, bytes)
|
||||||
|
if bytes < 1:
|
||||||
|
return ''
|
||||||
|
hr, ans = w.ReadFile(self._handle, bytes, None)
|
||||||
|
if hr != 0:
|
||||||
|
raise IOError('Error reading file: %s'%hr)
|
||||||
|
return ans
|
||||||
|
|
||||||
|
def readlines(self, sizehint=-1):
|
||||||
|
return self.read().splitlines()
|
||||||
|
|
||||||
|
def write(self, bytes):
|
||||||
|
if isinstance(bytes, unicode):
|
||||||
|
bytes = bytes.encode('utf-8')
|
||||||
|
import win32file as w
|
||||||
|
w.WriteFile(self._handle, bytes, None)
|
||||||
|
|
||||||
|
def truncate(self, size=None):
|
||||||
|
import win32file as w
|
||||||
|
pos = self.tell()
|
||||||
|
if size is None:
|
||||||
|
size = pos
|
||||||
|
t = min(size, pos)
|
||||||
|
self.seek(t)
|
||||||
|
w.SetEndOfFile(self._handle)
|
||||||
|
self.seek(pos)
|
||||||
|
|
||||||
|
def isatty(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def closed(self):
|
||||||
|
return self._handle is None
|
||||||
|
|
||||||
|
|
||||||
class ExclusiveFile(object):
|
class ExclusiveFile(object):
|
||||||
|
|
||||||
def __init__(self, path, timeout=15):
|
def __init__(self, path, timeout=15):
|
||||||
@ -20,17 +110,10 @@ class ExclusiveFile(object):
|
|||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
self.file = open(self.path, 'a+b')
|
self.file = WindowsExclFile(self.path, self.timeout) if iswindows else open(self.path, 'a+b')
|
||||||
self.file.seek(0)
|
self.file.seek(0)
|
||||||
timeout = self.timeout
|
timeout = self.timeout
|
||||||
if iswindows:
|
if not iswindows:
|
||||||
name = ('Local\\'+(__appname__+self.file.name).replace('\\', '_'))[:201]
|
|
||||||
while self.timeout < 0 or timeout >= 0:
|
|
||||||
self.mutex = win32event.CreateMutex(None, False, name)
|
|
||||||
if win32api.GetLastError() != winerror.ERROR_ALREADY_EXISTS: break
|
|
||||||
time.sleep(1)
|
|
||||||
timeout -= 1
|
|
||||||
else:
|
|
||||||
while self.timeout < 0 or timeout >= 0:
|
while self.timeout < 0 or timeout >= 0:
|
||||||
try:
|
try:
|
||||||
fcntl.lockf(self.file.fileno(), fcntl.LOCK_EX|fcntl.LOCK_NB)
|
fcntl.lockf(self.file.fileno(), fcntl.LOCK_EX|fcntl.LOCK_NB)
|
||||||
@ -38,14 +121,12 @@ class ExclusiveFile(object):
|
|||||||
except IOError:
|
except IOError:
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
timeout -= 1
|
timeout -= 1
|
||||||
if timeout < 0 and self.timeout >= 0:
|
if timeout < 0 and self.timeout >= 0:
|
||||||
self.file.close()
|
self.file.close()
|
||||||
raise LockError
|
raise LockError('Failed to lock')
|
||||||
return self.file
|
return self.file
|
||||||
|
|
||||||
def __exit__(self, type, value, traceback):
|
def __exit__(self, type, value, traceback):
|
||||||
if iswindows:
|
|
||||||
win32api.CloseHandle(self.mutex)
|
|
||||||
self.file.close()
|
self.file.close()
|
||||||
|
|
||||||
def _clean_lock_file(file):
|
def _clean_lock_file(file):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user