mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Merge version 0.7.12
This commit is contained in:
commit
a80c304fa4
@ -4,6 +4,78 @@
|
|||||||
# for important features/bug fixes.
|
# for important features/bug fixes.
|
||||||
# Also, each release can have new and improved recipes.
|
# Also, each release can have new and improved recipes.
|
||||||
|
|
||||||
|
- version: 0.7.12
|
||||||
|
date: 2010-07-30
|
||||||
|
|
||||||
|
bug fixes:
|
||||||
|
- title: "Fix a typo that caused a harmless error message after setting preferences"
|
||||||
|
|
||||||
|
- title: "Linux build system: No longer search for poppler-qt4 libraries as they are not needed"
|
||||||
|
|
||||||
|
- version: 0.7.11
|
||||||
|
date: 2010-07-30
|
||||||
|
|
||||||
|
new features:
|
||||||
|
- title: "EPUB metadata: When setting metadata in an EPUB file, if it has a well defined image based cover, update it"
|
||||||
|
|
||||||
|
- title: "Support for Droid X, Samsung Vibrant and Promedia ebook reader"
|
||||||
|
|
||||||
|
- title: "Add entry to Connect/share menu to conveniently stop and start the Content Server"
|
||||||
|
|
||||||
|
- title: "News download: Make the navbars on the section index pages more useful, adding links to net and previous section"
|
||||||
|
|
||||||
|
- title: "Add a button to reset confirm dialogs to Preferences->General"
|
||||||
|
|
||||||
|
|
||||||
|
bug fixes:
|
||||||
|
- title: "Fix crash in edit metadata dialog if you click OK before cover download completes"
|
||||||
|
tickets: [6337]
|
||||||
|
|
||||||
|
- title: "Kobo driver: Show a warning when the user tries to export/view .kobo files. Also add support for the new sofroot vendor id"
|
||||||
|
|
||||||
|
- title: "Update check. Do not be fooled by a redirecting proxy when checking for new version"
|
||||||
|
tickets: [6325]
|
||||||
|
|
||||||
|
- title: "Add book count to tooltip of library button in toolbar"
|
||||||
|
tickets: [6340]
|
||||||
|
|
||||||
|
- title: "Content server: When serving OPDS feeds send the correct content-type header."
|
||||||
|
tickets: [6329]
|
||||||
|
|
||||||
|
- title: "PDF Output: Don't insert blank pages before every individual HTML file in the ebook."
|
||||||
|
tickets: [6315]
|
||||||
|
|
||||||
|
- title: "Fix saving of cover when path to book folder contains non ascii characters"
|
||||||
|
tickets: [6328]
|
||||||
|
|
||||||
|
- title: "Fix regression that broke showing send to actions for multiple email accounts"
|
||||||
|
|
||||||
|
- title: "Fix bug in handlling of hexadecimal entities"
|
||||||
|
tickets: [6305]
|
||||||
|
|
||||||
|
- title: "SONY driver: More fixes to handle broken media.xml files"
|
||||||
|
tickets: [6296]
|
||||||
|
|
||||||
|
- title: "Linux installer: Fix rendering of viewer icon and restrict all icons to 128x128 since GNOME can't handle large icons"
|
||||||
|
|
||||||
|
- title: "RTF Input: Fix handling of hard linebreaks"
|
||||||
|
tickets: [6208]
|
||||||
|
|
||||||
|
- title: "RTF Output: Fix regression that broke rendering of bold and italic text"
|
||||||
|
tickets: [6098]
|
||||||
|
|
||||||
|
new recipes:
|
||||||
|
- title: "Draw and Cook"
|
||||||
|
author: Startson17
|
||||||
|
|
||||||
|
improved recipes:
|
||||||
|
- La Nacion
|
||||||
|
- Vecernje Novosti
|
||||||
|
- Der Tagesspiegel
|
||||||
|
- Die Zeit Nachrichten
|
||||||
|
- Toms Hardware (DE)
|
||||||
|
- Welt Online
|
||||||
|
|
||||||
- version: 0.7.10
|
- version: 0.7.10
|
||||||
date: 2010-07-23
|
date: 2010-07-23
|
||||||
|
|
||||||
|
60
resources/recipes/DrawAndCook.recipe
Normal file
60
resources/recipes/DrawAndCook.recipe
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class DrawAndCook(BasicNewsRecipe):
|
||||||
|
title = 'DrawAndCook'
|
||||||
|
__author__ = 'Starson17'
|
||||||
|
description = 'Drawings of recipes!'
|
||||||
|
language = 'en'
|
||||||
|
publisher = 'Starson17'
|
||||||
|
category = 'news, food, recipes'
|
||||||
|
use_embedded_content= False
|
||||||
|
no_stylesheets = True
|
||||||
|
oldest_article = 24
|
||||||
|
remove_javascript = True
|
||||||
|
remove_empty_feeds = True
|
||||||
|
cover_url = 'http://farm5.static.flickr.com/4043/4471139063_4dafced67f_o.jpg'
|
||||||
|
max_articles_per_feed = 30
|
||||||
|
|
||||||
|
remove_attributes = ['style', 'font']
|
||||||
|
|
||||||
|
def parse_index(self):
|
||||||
|
feeds = []
|
||||||
|
for title, url in [
|
||||||
|
("They Draw and Cook", "http://www.theydrawandcook.com/")
|
||||||
|
]:
|
||||||
|
articles = self.make_links(url)
|
||||||
|
if articles:
|
||||||
|
feeds.append((title, articles))
|
||||||
|
print 'feeds are: ', feeds
|
||||||
|
return feeds
|
||||||
|
|
||||||
|
def make_links(self, url):
|
||||||
|
soup = self.index_to_soup(url)
|
||||||
|
title = ''
|
||||||
|
date = ''
|
||||||
|
current_articles = []
|
||||||
|
soup = self.index_to_soup(url)
|
||||||
|
recipes = soup.findAll('div', attrs={'class': 'date-outer'})
|
||||||
|
for recipe in recipes:
|
||||||
|
title = recipe.h3.a.string
|
||||||
|
page_url = recipe.h3.a['href']
|
||||||
|
current_articles.append({'title': title, 'url': page_url, 'description':'', 'date':date})
|
||||||
|
return current_articles
|
||||||
|
|
||||||
|
|
||||||
|
keep_only_tags = [dict(name='h3', attrs={'class':'post-title entry-title'})
|
||||||
|
,dict(name='div', attrs={'class':'post-body entry-content'})
|
||||||
|
]
|
||||||
|
|
||||||
|
remove_tags = [dict(name='div', attrs={'class':['separator']})
|
||||||
|
,dict(name='div', attrs={'class':['post-share-buttons']})
|
||||||
|
]
|
||||||
|
|
||||||
|
extra_css = '''
|
||||||
|
h1{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:large;}
|
||||||
|
h2{font-family:Arial,Helvetica,sans-serif; font-weight:normal;font-size:small;}
|
||||||
|
img {max-width:100%; min-width:100%;}
|
||||||
|
p{font-family:Arial,Helvetica,sans-serif;font-size:small;}
|
||||||
|
body{font-family:Helvetica,Arial,sans-serif;font-size:small;}
|
||||||
|
'''
|
||||||
|
|
@ -98,7 +98,7 @@ class ZeitDe(BasicNewsRecipe):
|
|||||||
return soup
|
return soup
|
||||||
info = Tag(soup,'ul',[('class','ebinfobox')])
|
info = Tag(soup,'ul',[('class','ebinfobox')])
|
||||||
tools = soup.find('ul', attrs={'class':'tools'})
|
tools = soup.find('ul', attrs={'class':'tools'})
|
||||||
author = tools.find('li','author first')
|
#author = tools.find('li','author first')
|
||||||
for tag in ['author first', 'date', 'date first', 'author', 'source']:
|
for tag in ['author first', 'date', 'date first', 'author', 'source']:
|
||||||
line = tools.find('li', tag)
|
line = tools.find('li', tag)
|
||||||
if line:
|
if line:
|
||||||
|
@ -115,7 +115,6 @@ if iswindows:
|
|||||||
poppler_lib_dirs = consolidate('POPPLER_LIB_DIR', sw_lib_dir)
|
poppler_lib_dirs = consolidate('POPPLER_LIB_DIR', sw_lib_dir)
|
||||||
popplerqt4_lib_dirs = poppler_lib_dirs
|
popplerqt4_lib_dirs = poppler_lib_dirs
|
||||||
poppler_libs = ['poppler']
|
poppler_libs = ['poppler']
|
||||||
popplerqt4_libs = poppler_libs + ['QtCore4', 'QtGui4']
|
|
||||||
magick_inc_dirs = [os.path.join(prefix, 'build', 'ImageMagick-6.5.6')]
|
magick_inc_dirs = [os.path.join(prefix, 'build', 'ImageMagick-6.5.6')]
|
||||||
magick_lib_dirs = [os.path.join(magick_inc_dirs[0], 'VisualMagick', 'lib')]
|
magick_lib_dirs = [os.path.join(magick_inc_dirs[0], 'VisualMagick', 'lib')]
|
||||||
magick_libs = ['CORE_RL_wand_', 'CORE_RL_magick_']
|
magick_libs = ['CORE_RL_wand_', 'CORE_RL_magick_']
|
||||||
@ -129,8 +128,8 @@ elif isosx:
|
|||||||
popplerqt4_inc_dirs = poppler_inc_dirs + [poppler_inc_dirs[0]+'/qt4']
|
popplerqt4_inc_dirs = poppler_inc_dirs + [poppler_inc_dirs[0]+'/qt4']
|
||||||
poppler_lib_dirs = consolidate('POPPLER_LIB_DIR',
|
poppler_lib_dirs = consolidate('POPPLER_LIB_DIR',
|
||||||
'/sw/lib')
|
'/sw/lib')
|
||||||
|
poppler_libs = ['poppler']
|
||||||
popplerqt4_lib_dirs = poppler_lib_dirs
|
popplerqt4_lib_dirs = poppler_lib_dirs
|
||||||
poppler_libs = popplerqt4_libs = ['poppler']
|
|
||||||
podofo_inc = '/sw/podofo'
|
podofo_inc = '/sw/podofo'
|
||||||
podofo_lib = '/sw/lib'
|
podofo_lib = '/sw/lib'
|
||||||
magick_inc_dirs = consolidate('MAGICK_INC',
|
magick_inc_dirs = consolidate('MAGICK_INC',
|
||||||
@ -162,9 +161,6 @@ else:
|
|||||||
poppler_libs = pkgconfig_libs('poppler', '', '')
|
poppler_libs = pkgconfig_libs('poppler', '', '')
|
||||||
if not poppler_libs:
|
if not poppler_libs:
|
||||||
poppler_libs = ['poppler']
|
poppler_libs = ['poppler']
|
||||||
popplerqt4_libs = pkgconfig_libs('poppler-qt4', '', '')
|
|
||||||
if not popplerqt4_libs:
|
|
||||||
popplerqt4_libs = ['poppler-qt4', 'poppler']
|
|
||||||
magick_libs = pkgconfig_libs('MagickWand', '', '')
|
magick_libs = pkgconfig_libs('MagickWand', '', '')
|
||||||
if not magick_libs:
|
if not magick_libs:
|
||||||
magick_libs = ['MagickWand', 'MagickCore']
|
magick_libs = ['MagickWand', 'MagickCore']
|
||||||
|
@ -2,7 +2,7 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
__appname__ = 'calibre'
|
__appname__ = 'calibre'
|
||||||
__version__ = '0.7.10'
|
__version__ = '0.7.12'
|
||||||
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
@ -22,7 +22,8 @@ class ANDROID(USBMS):
|
|||||||
0x0bb4 : { 0x0c02 : [0x100], 0x0c01 : [0x100], 0x0ff9 : [0x0100]},
|
0x0bb4 : { 0x0c02 : [0x100], 0x0c01 : [0x100], 0x0ff9 : [0x0100]},
|
||||||
|
|
||||||
# Motorola
|
# Motorola
|
||||||
0x22b8 : { 0x41d9 : [0x216], 0x2d67 : [0x100], 0x41db : [0x216]},
|
0x22b8 : { 0x41d9 : [0x216], 0x2d67 : [0x100], 0x41db : [0x216],
|
||||||
|
0x4285 : [0x216]},
|
||||||
|
|
||||||
# Sony Ericsson
|
# Sony Ericsson
|
||||||
0xfce : { 0xd12e : [0x0100]},
|
0xfce : { 0xd12e : [0x0100]},
|
||||||
@ -52,9 +53,9 @@ class ANDROID(USBMS):
|
|||||||
'GT-I5700', 'SAMSUNG', 'DELL', 'LINUX']
|
'GT-I5700', 'SAMSUNG', 'DELL', 'LINUX']
|
||||||
WINDOWS_MAIN_MEM = ['ANDROID_PHONE', 'A855', 'A853', 'INC.NEXUS_ONE',
|
WINDOWS_MAIN_MEM = ['ANDROID_PHONE', 'A855', 'A853', 'INC.NEXUS_ONE',
|
||||||
'__UMS_COMPOSITE', '_MB200', 'MASS_STORAGE', '_-_CARD',
|
'__UMS_COMPOSITE', '_MB200', 'MASS_STORAGE', '_-_CARD',
|
||||||
'GT-I9000', 'FILE-STOR_GADGET']
|
'GT-I9000', 'FILE-STOR_GADGET', 'SGH-T959']
|
||||||
WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD',
|
WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD',
|
||||||
'FILE-STOR_GADGET']
|
'FILE-STOR_GADGET', 'SGH-T959']
|
||||||
|
|
||||||
OSX_MAIN_MEM = 'HTC Android Phone Media'
|
OSX_MAIN_MEM = 'HTC Android Phone Media'
|
||||||
|
|
||||||
|
@ -355,3 +355,17 @@ class KOBO(USBMS):
|
|||||||
# print "Internal: " + filename
|
# print "Internal: " + filename
|
||||||
|
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
def get_file(self, path, *args, **kwargs):
|
||||||
|
tpath = self.munge_path(path)
|
||||||
|
extension = os.path.splitext(tpath)[1]
|
||||||
|
if extension == '.kobo':
|
||||||
|
from calibre.devices.errors import UserFeedback
|
||||||
|
raise UserFeedback(_("Not Implemented"),
|
||||||
|
_('".kobo" files do not exist on the device as books '
|
||||||
|
'instead, they are rows in the sqlite database. '
|
||||||
|
'Currently they cannot be exported or viewed.'),
|
||||||
|
UserFeedback.WARN)
|
||||||
|
|
||||||
|
return USBMS.get_file(self, path, *args, **kwargs)
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ class Metadata(object):
|
|||||||
def print_all_attributes(self):
|
def print_all_attributes(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def smart_update(self, other):
|
def smart_update(self, other, replace_metadata=False):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def format_series_index(self):
|
def format_series_index(self):
|
||||||
|
@ -5,7 +5,7 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
|||||||
|
|
||||||
'''Read meta information from epub files'''
|
'''Read meta information from epub files'''
|
||||||
|
|
||||||
import os, re
|
import os, re, posixpath
|
||||||
from cStringIO import StringIO
|
from cStringIO import StringIO
|
||||||
from contextlib import closing
|
from contextlib import closing
|
||||||
|
|
||||||
@ -126,7 +126,6 @@ class OCFDirReader(OCFReader):
|
|||||||
return open(os.path.join(self.root, path), *args, **kwargs)
|
return open(os.path.join(self.root, path), *args, **kwargs)
|
||||||
|
|
||||||
def get_cover(opf, opf_path, stream, reader=None):
|
def get_cover(opf, opf_path, stream, reader=None):
|
||||||
import posixpath
|
|
||||||
from calibre.ebooks import render_html_svg_workaround
|
from calibre.ebooks import render_html_svg_workaround
|
||||||
from calibre.utils.logging import default_log
|
from calibre.utils.logging import default_log
|
||||||
raster_cover = opf.raster_cover
|
raster_cover = opf.raster_cover
|
||||||
@ -185,7 +184,37 @@ def get_quick_metadata(stream):
|
|||||||
def set_metadata(stream, mi, apply_null=False, update_timestamp=False):
|
def set_metadata(stream, mi, apply_null=False, update_timestamp=False):
|
||||||
stream.seek(0)
|
stream.seek(0)
|
||||||
reader = OCFZipReader(stream, root=os.getcwdu())
|
reader = OCFZipReader(stream, root=os.getcwdu())
|
||||||
|
raster_cover = reader.opf.raster_cover
|
||||||
mi = MetaInformation(mi)
|
mi = MetaInformation(mi)
|
||||||
|
new_cdata = None
|
||||||
|
replacements = {}
|
||||||
|
try:
|
||||||
|
new_cdata = mi.cover_data[1]
|
||||||
|
if not new_cdata:
|
||||||
|
raise Exception('no cover')
|
||||||
|
except:
|
||||||
|
try:
|
||||||
|
new_cdata = open(mi.cover, 'rb').read()
|
||||||
|
except:
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
if new_cdata and raster_cover:
|
||||||
|
try:
|
||||||
|
cpath = posixpath.join(posixpath.dirname(reader.opf_path),
|
||||||
|
raster_cover)
|
||||||
|
cover_replacable = not reader.encryption_meta.is_encrypted(cpath) and \
|
||||||
|
os.path.splitext(cpath)[1].lower() in ('.png', '.jpg', '.jpeg')
|
||||||
|
if cover_replacable:
|
||||||
|
from calibre.ptempfile import PersistentTemporaryFile
|
||||||
|
from calibre.utils.magick_draw import save_cover_data_to
|
||||||
|
new_cover = PersistentTemporaryFile(suffix=os.path.splitext(cpath)[1])
|
||||||
|
new_cover.close()
|
||||||
|
save_cover_data_to(new_cdata, new_cover.name)
|
||||||
|
replacements[cpath] = open(new_cover.name, 'rb')
|
||||||
|
except:
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
for x in ('guide', 'toc', 'manifest', 'spine'):
|
for x in ('guide', 'toc', 'manifest', 'spine'):
|
||||||
setattr(mi, x, None)
|
setattr(mi, x, None)
|
||||||
reader.opf.smart_update(mi)
|
reader.opf.smart_update(mi)
|
||||||
@ -200,5 +229,6 @@ def set_metadata(stream, mi, apply_null=False, update_timestamp=False):
|
|||||||
reader.opf.timestamp = mi.timestamp
|
reader.opf.timestamp = mi.timestamp
|
||||||
|
|
||||||
newopf = StringIO(reader.opf.render())
|
newopf = StringIO(reader.opf.render())
|
||||||
safe_replace(stream, reader.container[OPF.MIMETYPE], newopf)
|
safe_replace(stream, reader.container[OPF.MIMETYPE], newopf,
|
||||||
|
extra_replacements=replacements)
|
||||||
|
|
||||||
|
@ -916,7 +916,7 @@ class OPF(object):
|
|||||||
raw = '<?xml version="1.0" encoding="%s"?>\n'%encoding.upper()+raw
|
raw = '<?xml version="1.0" encoding="%s"?>\n'%encoding.upper()+raw
|
||||||
return raw
|
return raw
|
||||||
|
|
||||||
def smart_update(self, mi):
|
def smart_update(self, mi, replace_metadata=False):
|
||||||
for attr in ('title', 'authors', 'author_sort', 'title_sort',
|
for attr in ('title', 'authors', 'author_sort', 'title_sort',
|
||||||
'publisher', 'series', 'series_index', 'rating',
|
'publisher', 'series', 'series_index', 'rating',
|
||||||
'isbn', 'language', 'tags', 'category', 'comments',
|
'isbn', 'language', 'tags', 'category', 'comments',
|
||||||
|
@ -59,6 +59,29 @@ class PDFOutput(OutputFormatPlugin):
|
|||||||
self.metadata = oeb_book.metadata
|
self.metadata = oeb_book.metadata
|
||||||
self.cover_data = None
|
self.cover_data = None
|
||||||
|
|
||||||
|
# Remove page-break-before on <body> element as it causes
|
||||||
|
# blank pages in PDF Output
|
||||||
|
from calibre.ebooks.oeb.base import OEB_STYLES, XPath
|
||||||
|
stylesheet = None
|
||||||
|
for item in self.oeb.manifest:
|
||||||
|
if item.media_type.lower() in OEB_STYLES:
|
||||||
|
stylesheet = item
|
||||||
|
break
|
||||||
|
if stylesheet is not None:
|
||||||
|
from cssutils.css import CSSRule
|
||||||
|
classes = set(['.calibre'])
|
||||||
|
for x in self.oeb.spine:
|
||||||
|
root = x.data
|
||||||
|
body = XPath('//h:body[@class]')(root)
|
||||||
|
if body:
|
||||||
|
classes.add('.'+body[0].get('class'))
|
||||||
|
|
||||||
|
for rule in stylesheet.data.cssRules.rulesOfType(CSSRule.STYLE_RULE):
|
||||||
|
if rule.selectorList.selectorText in classes:
|
||||||
|
rule.style.removeProperty('page-break-before')
|
||||||
|
rule.style.removeProperty('page-break-after')
|
||||||
|
|
||||||
|
|
||||||
if input_plugin.is_image_collection:
|
if input_plugin.is_image_collection:
|
||||||
log.debug('Converting input as an image collection...')
|
log.debug('Converting input as an image collection...')
|
||||||
self.convert_images(input_plugin.get_images())
|
self.convert_images(input_plugin.get_images())
|
||||||
|
@ -677,6 +677,10 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
|
|||||||
|
|
||||||
|
|
||||||
def accept(self):
|
def accept(self):
|
||||||
|
cf = getattr(self, 'cover_fetcher', None)
|
||||||
|
if cf is not None and hasattr(cf, 'terminate'):
|
||||||
|
cf.terminate()
|
||||||
|
cf.wait()
|
||||||
try:
|
try:
|
||||||
if self.formats_changed:
|
if self.formats_changed:
|
||||||
self.sync_formats()
|
self.sync_formats()
|
||||||
|
@ -302,6 +302,7 @@ class ToolBar(QToolBar): # {{{
|
|||||||
text = _('%d books')%new_count
|
text = _('%d books')%new_count
|
||||||
a = self.choose_action
|
a = self.choose_action
|
||||||
a.setText(text)
|
a.setText(text)
|
||||||
|
a.setToolTip(_('Choose calibre library to work with') + '\n\n' + text)
|
||||||
|
|
||||||
def resizeEvent(self, ev):
|
def resizeEvent(self, ev):
|
||||||
QToolBar.resizeEvent(self, ev)
|
QToolBar.resizeEvent(self, ev)
|
||||||
@ -328,6 +329,7 @@ class ShareConnMenu(QMenu): # {{{
|
|||||||
connect_to_folder = pyqtSignal()
|
connect_to_folder = pyqtSignal()
|
||||||
connect_to_itunes = pyqtSignal()
|
connect_to_itunes = pyqtSignal()
|
||||||
config_email = pyqtSignal()
|
config_email = pyqtSignal()
|
||||||
|
toggle_server = pyqtSignal()
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
QMenu.__init__(self, parent)
|
QMenu.__init__(self, parent)
|
||||||
@ -335,15 +337,27 @@ class ShareConnMenu(QMenu): # {{{
|
|||||||
mitem.setEnabled(True)
|
mitem.setEnabled(True)
|
||||||
mitem.triggered.connect(lambda x : self.connect_to_folder.emit())
|
mitem.triggered.connect(lambda x : self.connect_to_folder.emit())
|
||||||
self.connect_to_folder_action = mitem
|
self.connect_to_folder_action = mitem
|
||||||
|
|
||||||
mitem = self.addAction(QIcon(I('devices/itunes.png')),
|
mitem = self.addAction(QIcon(I('devices/itunes.png')),
|
||||||
_('Connect to iTunes'))
|
_('Connect to iTunes'))
|
||||||
mitem.setEnabled(True)
|
mitem.setEnabled(True)
|
||||||
mitem.triggered.connect(lambda x : self.connect_to_itunes.emit())
|
mitem.triggered.connect(lambda x : self.connect_to_itunes.emit())
|
||||||
self.connect_to_itunes_action = mitem
|
self.connect_to_itunes_action = mitem
|
||||||
self.addSeparator()
|
self.addSeparator()
|
||||||
|
self.toggle_server_action = \
|
||||||
|
self.addAction(QIcon(I('network-server.svg')),
|
||||||
|
_('Start Content Server'))
|
||||||
|
self.toggle_server_action.triggered.connect(lambda x:
|
||||||
|
self.toggle_server.emit())
|
||||||
|
self.addSeparator()
|
||||||
|
|
||||||
self.email_actions = []
|
self.email_actions = []
|
||||||
|
|
||||||
|
def server_state_changed(self, running):
|
||||||
|
text = _('Start Content Server')
|
||||||
|
if running:
|
||||||
|
text = _('Stop Content Server')
|
||||||
|
self.toggle_server_action.setText(text)
|
||||||
|
|
||||||
def build_email_entries(self, sync_menu):
|
def build_email_entries(self, sync_menu):
|
||||||
from calibre.gui2.device import DeviceAction
|
from calibre.gui2.device import DeviceAction
|
||||||
for ac in self.email_actions:
|
for ac in self.email_actions:
|
||||||
@ -477,6 +491,7 @@ class MainWindowMixin(object):
|
|||||||
self.action_news.triggered.connect(
|
self.action_news.triggered.connect(
|
||||||
self.scheduler.show_dialog)
|
self.scheduler.show_dialog)
|
||||||
self.share_conn_menu = ShareConnMenu(self)
|
self.share_conn_menu = ShareConnMenu(self)
|
||||||
|
self.share_conn_menu.toggle_server.connect(self.toggle_content_server)
|
||||||
self.share_conn_menu.config_email.connect(partial(self.do_config,
|
self.share_conn_menu.config_email.connect(partial(self.do_config,
|
||||||
initial_category='email'))
|
initial_category='email'))
|
||||||
self.action_conn_share.setMenu(self.share_conn_menu)
|
self.action_conn_share.setMenu(self.share_conn_menu)
|
||||||
@ -613,4 +628,12 @@ class MainWindowMixin(object):
|
|||||||
def show_help(self, *args):
|
def show_help(self, *args):
|
||||||
open_url(QUrl('http://calibre-ebook.com/user_manual'))
|
open_url(QUrl('http://calibre-ebook.com/user_manual'))
|
||||||
|
|
||||||
|
def content_server_state_changed(self, running):
|
||||||
|
self.share_conn_menu.server_state_changed(running)
|
||||||
|
|
||||||
|
def toggle_content_server(self):
|
||||||
|
if self.content_server is None:
|
||||||
|
self.start_content_server()
|
||||||
|
else:
|
||||||
|
self.content_server.exit()
|
||||||
|
self.content_server = None
|
||||||
|
@ -221,7 +221,7 @@ def fetch_scheduled_recipe(arg):
|
|||||||
if lf.get('base_font_size', 0.0) != 0.0:
|
if lf.get('base_font_size', 0.0) != 0.0:
|
||||||
recs.append(('base_font_size', lf['base_font_size'],
|
recs.append(('base_font_size', lf['base_font_size'],
|
||||||
OptionRecommendation.HIGH))
|
OptionRecommendation.HIGH))
|
||||||
recs.append(('keep_ligatures', lf['keep_ligatures'],
|
recs.append(('keep_ligatures', lf.get('keep_ligatures', False),
|
||||||
OptionRecommendation.HIGH))
|
OptionRecommendation.HIGH))
|
||||||
|
|
||||||
lr = load_defaults('lrf_output')
|
lr = load_defaults('lrf_output')
|
||||||
|
@ -24,7 +24,7 @@ from calibre.ptempfile import PersistentTemporaryFile
|
|||||||
from calibre.utils.config import prefs, dynamic
|
from calibre.utils.config import prefs, dynamic
|
||||||
from calibre.utils.ipc.server import Server
|
from calibre.utils.ipc.server import Server
|
||||||
from calibre.gui2 import error_dialog, GetMetadata, open_local_file, \
|
from calibre.gui2 import error_dialog, GetMetadata, open_local_file, \
|
||||||
gprefs, max_available_height, config, info_dialog
|
gprefs, max_available_height, config, info_dialog, Dispatcher
|
||||||
from calibre.gui2.cover_flow import CoverFlowMixin
|
from calibre.gui2.cover_flow import CoverFlowMixin
|
||||||
from calibre.gui2.widgets import ProgressIndicator
|
from calibre.gui2.widgets import ProgressIndicator
|
||||||
from calibre.gui2.update import UpdateMixin
|
from calibre.gui2.update import UpdateMixin
|
||||||
@ -106,6 +106,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{
|
|||||||
opts = self.opts
|
opts = self.opts
|
||||||
self.preferences_action, self.quit_action = actions
|
self.preferences_action, self.quit_action = actions
|
||||||
self.library_path = library_path
|
self.library_path = library_path
|
||||||
|
self.content_server = None
|
||||||
self.spare_servers = []
|
self.spare_servers = []
|
||||||
self.must_restart_before_config = False
|
self.must_restart_before_config = False
|
||||||
# Initialize fontconfig in a separate thread as this can be a lengthy
|
# Initialize fontconfig in a separate thread as this can be a lengthy
|
||||||
@ -146,7 +147,6 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{
|
|||||||
self.default_thumbnail = None
|
self.default_thumbnail = None
|
||||||
self.tb_wrapper = textwrap.TextWrapper(width=40)
|
self.tb_wrapper = textwrap.TextWrapper(width=40)
|
||||||
self.viewers = collections.deque()
|
self.viewers = collections.deque()
|
||||||
self.content_server = None
|
|
||||||
self.system_tray_icon = SystemTrayIcon(QIcon(I('library.png')), self)
|
self.system_tray_icon = SystemTrayIcon(QIcon(I('library.png')), self)
|
||||||
self.system_tray_icon.setToolTip('calibre')
|
self.system_tray_icon.setToolTip('calibre')
|
||||||
self.system_tray_icon.tooltip_requested.connect(
|
self.system_tray_icon.tooltip_requested.connect(
|
||||||
@ -246,11 +246,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{
|
|||||||
|
|
||||||
|
|
||||||
if config['autolaunch_server']:
|
if config['autolaunch_server']:
|
||||||
from calibre.library.server.main import start_threaded_server
|
self.start_content_server()
|
||||||
from calibre.library.server import server_config
|
|
||||||
self.content_server = start_threaded_server(
|
|
||||||
db, server_config().parse())
|
|
||||||
self.test_server_timer = QTimer.singleShot(10000, self.test_server)
|
|
||||||
|
|
||||||
self.keyboard_interrupt.connect(self.quit, type=Qt.QueuedConnection)
|
self.keyboard_interrupt.connect(self.quit, type=Qt.QueuedConnection)
|
||||||
AddAction.__init__(self)
|
AddAction.__init__(self)
|
||||||
@ -263,6 +259,15 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{
|
|||||||
self.library_view.model().delete_books_by_id,
|
self.library_view.model().delete_books_by_id,
|
||||||
type=Qt.QueuedConnection)
|
type=Qt.QueuedConnection)
|
||||||
|
|
||||||
|
def start_content_server(self):
|
||||||
|
from calibre.library.server.main import start_threaded_server
|
||||||
|
from calibre.library.server import server_config
|
||||||
|
self.content_server = start_threaded_server(
|
||||||
|
self.library_view.model().db, server_config().parse())
|
||||||
|
self.content_server.state_callback = Dispatcher(self.content_server_state_changed)
|
||||||
|
self.content_server.state_callback(True)
|
||||||
|
self.test_server_timer = QTimer.singleShot(10000, self.test_server)
|
||||||
|
|
||||||
|
|
||||||
def resizeEvent(self, ev):
|
def resizeEvent(self, ev):
|
||||||
MainWindow.resizeEvent(self, ev)
|
MainWindow.resizeEvent(self, ev)
|
||||||
@ -308,7 +313,8 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{
|
|||||||
setattr(window, '__systray_minimized', False)
|
setattr(window, '__systray_minimized', False)
|
||||||
|
|
||||||
def test_server(self, *args):
|
def test_server(self, *args):
|
||||||
if self.content_server.exception is not None:
|
if self.content_server is not None and \
|
||||||
|
self.content_server.exception is not None:
|
||||||
error_dialog(self, _('Failed to start content server'),
|
error_dialog(self, _('Failed to start content server'),
|
||||||
unicode(self.content_server.exception)).exec_()
|
unicode(self.content_server.exception)).exec_()
|
||||||
|
|
||||||
@ -367,6 +373,11 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{
|
|||||||
|
|
||||||
d.exec_()
|
d.exec_()
|
||||||
self.content_server = d.server
|
self.content_server = d.server
|
||||||
|
if self.content_server is not None:
|
||||||
|
self.content_server.state_callback = \
|
||||||
|
Dispatcher(self.content_server_state_changed)
|
||||||
|
self.content_server.state_callback(self.content_server.is_running)
|
||||||
|
|
||||||
if d.result() == d.Accepted:
|
if d.result() == d.Accepted:
|
||||||
self.read_toolbar_settings()
|
self.read_toolbar_settings()
|
||||||
self.search.search_as_you_type(config['search_as_you_type'])
|
self.search.search_as_you_type(config['search_as_you_type'])
|
||||||
@ -583,7 +594,9 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{
|
|||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
if self.content_server is not None:
|
if self.content_server is not None:
|
||||||
self.content_server.exit()
|
s = self.content_server
|
||||||
|
self.content_server = None
|
||||||
|
s.exit()
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
|
@ -31,7 +31,7 @@ class CheckForUpdates(QThread):
|
|||||||
'win' if iswindows else 'osx' if isosx else 'oth')
|
'win' if iswindows else 'osx' if isosx else 'oth')
|
||||||
req.add_header('CALIBRE_INSTALL_UUID', prefs['installation_uuid'])
|
req.add_header('CALIBRE_INSTALL_UUID', prefs['installation_uuid'])
|
||||||
version = br.open(req).read().strip()
|
version = br.open(req).read().strip()
|
||||||
if version and version != __version__:
|
if version and version != __version__ and len(version) < 10:
|
||||||
self.update_found.emit(version)
|
self.update_found.emit(version)
|
||||||
except:
|
except:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
@ -64,6 +64,7 @@ class LibraryServer(ContentServer, MobileServer, XMLServer, OPDSServer, Cache):
|
|||||||
break
|
break
|
||||||
self.opts = opts
|
self.opts = opts
|
||||||
self.embedded = embedded
|
self.embedded = embedded
|
||||||
|
self.state_callback = None
|
||||||
self.max_cover_width, self.max_cover_height = \
|
self.max_cover_width, self.max_cover_height = \
|
||||||
map(int, self.opts.max_cover.split('x'))
|
map(int, self.opts.max_cover.split('x'))
|
||||||
path = P('content_server')
|
path = P('content_server')
|
||||||
@ -159,11 +160,22 @@ class LibraryServer(ContentServer, MobileServer, XMLServer, OPDSServer, Cache):
|
|||||||
import traceback
|
import traceback
|
||||||
cherrypy.log.error('Failed to stop BonJour:')
|
cherrypy.log.error('Failed to stop BonJour:')
|
||||||
cherrypy.log.error(traceback.format_exc())
|
cherrypy.log.error(traceback.format_exc())
|
||||||
|
try:
|
||||||
|
if callable(self.state_callback):
|
||||||
|
self.state_callback(self.is_running)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
def exit(self):
|
def exit(self):
|
||||||
try:
|
try:
|
||||||
cherrypy.engine.exit()
|
cherrypy.engine.exit()
|
||||||
finally:
|
finally:
|
||||||
cherrypy.server.httpserver = None
|
cherrypy.server.httpserver = None
|
||||||
|
self.is_running = False
|
||||||
|
try:
|
||||||
|
if callable(self.state_callback):
|
||||||
|
self.state_callback(self.is_running)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@ -183,7 +183,8 @@ class ContentServer(object):
|
|||||||
fmt = TemporaryFile()
|
fmt = TemporaryFile()
|
||||||
fmt.write(raw)
|
fmt.write(raw)
|
||||||
fmt.seek(0)
|
fmt.seek(0)
|
||||||
set_metadata(fmt, self.db.get_metadata(id, index_is_id=True),
|
set_metadata(fmt, self.db.get_metadata(id, index_is_id=True,
|
||||||
|
get_cover=True),
|
||||||
'epub')
|
'epub')
|
||||||
fmt.seek(0)
|
fmt.seek(0)
|
||||||
mt = guess_type('dummy.'+format.lower())[0]
|
mt = guess_type('dummy.'+format.lower())[0]
|
||||||
|
@ -341,7 +341,7 @@ class OPDSServer(object):
|
|||||||
items = items[offsets.offset:offsets.offset+max_items]
|
items = items[offsets.offset:offsets.offset+max_items]
|
||||||
updated = self.db.last_modified()
|
updated = self.db.last_modified()
|
||||||
cherrypy.response.headers['Last-Modified'] = self.last_modified(updated)
|
cherrypy.response.headers['Last-Modified'] = self.last_modified(updated)
|
||||||
cherrypy.response.headers['Content-Type'] = 'text/xml'
|
cherrypy.response.headers['Content-Type'] = 'application/atom+xml;profile=opds-catalog'
|
||||||
return str(AcquisitionFeed(updated, id_, items, offsets,
|
return str(AcquisitionFeed(updated, id_, items, offsets,
|
||||||
page_url, up_url, version, self.db.FIELD_MAP))
|
page_url, up_url, version, self.db.FIELD_MAP))
|
||||||
|
|
||||||
@ -413,7 +413,7 @@ class OPDSServer(object):
|
|||||||
items = list(items)[offsets.offset:offsets.offset+max_items]
|
items = list(items)[offsets.offset:offsets.offset+max_items]
|
||||||
|
|
||||||
cherrypy.response.headers['Last-Modified'] = self.last_modified(updated)
|
cherrypy.response.headers['Last-Modified'] = self.last_modified(updated)
|
||||||
cherrypy.response.headers['Content-Type'] = 'text/xml'
|
cherrypy.response.headers['Content-Type'] = 'application/atom+xml'
|
||||||
|
|
||||||
return str(CategoryFeed(items, category, id_, updated, version, offsets,
|
return str(CategoryFeed(items, category, id_, updated, version, offsets,
|
||||||
page_url, up_url))
|
page_url, up_url))
|
||||||
@ -478,7 +478,7 @@ class OPDSServer(object):
|
|||||||
page_url, up_url)
|
page_url, up_url)
|
||||||
|
|
||||||
cherrypy.response.headers['Last-Modified'] = self.last_modified(updated)
|
cherrypy.response.headers['Last-Modified'] = self.last_modified(updated)
|
||||||
cherrypy.response.headers['Content-Type'] = 'text/xml'
|
cherrypy.response.headers['Content-Type'] = 'application/atom+xml'
|
||||||
|
|
||||||
return str(ans)
|
return str(ans)
|
||||||
|
|
||||||
@ -552,7 +552,7 @@ class OPDSServer(object):
|
|||||||
updated = self.db.last_modified()
|
updated = self.db.last_modified()
|
||||||
|
|
||||||
cherrypy.response.headers['Last-Modified'] = self.last_modified(updated)
|
cherrypy.response.headers['Last-Modified'] = self.last_modified(updated)
|
||||||
cherrypy.response.headers['Content-Type'] = 'text/xml'
|
cherrypy.response.headers['Content-Type'] = 'application/atom+xml'
|
||||||
|
|
||||||
feed = TopLevel(updated, cats, version)
|
feed = TopLevel(updated, cats, version)
|
||||||
|
|
||||||
|
@ -376,6 +376,12 @@ How do I use purchased EPUB books with |app|?
|
|||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
Most purchased EPUB books have `DRM <http://wiki.mobileread.com/wiki/DRM>`_. This prevents |app| from opening them. You can still use |app| to store and transfer them to your e-book reader. First, you must authorize your reader on a windows machine with Adobe Digital Editions. Once this is done, EPUB books transferred with |app| will work fine on your reader. When you purchase an epub book from a website, you will get an ".acsm" file. This file should be opened with Adobe Digital Editions, which will then download the actual ".epub" e-book. The e-book file will be stored in the folder "My Digital Editions", from where you can add it to |app|.
|
Most purchased EPUB books have `DRM <http://wiki.mobileread.com/wiki/DRM>`_. This prevents |app| from opening them. You can still use |app| to store and transfer them to your e-book reader. First, you must authorize your reader on a windows machine with Adobe Digital Editions. Once this is done, EPUB books transferred with |app| will work fine on your reader. When you purchase an epub book from a website, you will get an ".acsm" file. This file should be opened with Adobe Digital Editions, which will then download the actual ".epub" e-book. The e-book file will be stored in the folder "My Digital Editions", from where you can add it to |app|.
|
||||||
|
|
||||||
|
Can I have the comment metadata show up on my reader?
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Most readers do not support this. You should complain to the manufacturer about it and hopefully if enough people complain, things will change. In the meantime, you can insert the metadata, including comments into a "Jacket page" at the start of the ebook, by using the option to "Insert metadata as page at start of book" during conversion. The option is found in the :guilabel:`Structure Detection` section of the conversion settings. Note that for this to have effect you have to *convert* the book. If your book is already in a format that does not need conversion, you can convert from that format to the same format.
|
||||||
|
|
||||||
|
Another alternative is to create a catalog in ebook form containing a listing of all the books in your calibre library, with their metadata. Click the arrow next to the convert button to access the catalog creation tool. And before you ask, no you cannot have the catalog "link directly to" books on your reader.
|
||||||
|
|
||||||
I want some feature added to |app|. What can I do?
|
I want some feature added to |app|. What can I do?
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
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
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
@ -144,6 +144,8 @@ def compose_image(canvas, image, left, top):
|
|||||||
int(top))
|
int(top))
|
||||||
|
|
||||||
def load_image(path):
|
def load_image(path):
|
||||||
|
if isinstance(path, unicode):
|
||||||
|
path = path.encode(filesystem_encoding)
|
||||||
img = alloc_wand('NewMagickWand')
|
img = alloc_wand('NewMagickWand')
|
||||||
if not p.MagickReadImage(img, path):
|
if not p.MagickReadImage(img, path):
|
||||||
severity = p.ExceptionType(0)
|
severity = p.ExceptionType(0)
|
||||||
@ -221,7 +223,7 @@ def create_cover_page(top_lines, logo_path, width=590, height=750,
|
|||||||
def save_cover_data_to(data, path, bgcolor='white'):
|
def save_cover_data_to(data, path, bgcolor='white'):
|
||||||
'''
|
'''
|
||||||
Saves image in data to path, in the format specified by the path
|
Saves image in data to path, in the format specified by the path
|
||||||
extension. Composes the image onto a blank cancas so as to
|
extension. Composes the image onto a blank canvas so as to
|
||||||
properly convert transparent images.
|
properly convert transparent images.
|
||||||
'''
|
'''
|
||||||
with open(path, 'wb') as f:
|
with open(path, 'wb') as f:
|
||||||
|
@ -1362,7 +1362,7 @@ class ZipFile:
|
|||||||
self.fp.close()
|
self.fp.close()
|
||||||
self.fp = None
|
self.fp = None
|
||||||
|
|
||||||
def safe_replace(zipstream, name, datastream):
|
def safe_replace(zipstream, name, datastream, extra_replacements={}):
|
||||||
'''
|
'''
|
||||||
Replace a file in a zip file in a safe manner. This proceeds by extracting
|
Replace a file in a zip file in a safe manner. This proceeds by extracting
|
||||||
and re-creating the zipfile. This is necessary because :method:`ZipFile.replace`
|
and re-creating the zipfile. This is necessary because :method:`ZipFile.replace`
|
||||||
@ -1371,13 +1371,19 @@ def safe_replace(zipstream, name, datastream):
|
|||||||
:param zipstream: Stream from a zip file
|
:param zipstream: Stream from a zip file
|
||||||
:param name: The name of the file to replace
|
:param name: The name of the file to replace
|
||||||
:param datastream: The data to replace the file with.
|
:param datastream: The data to replace the file with.
|
||||||
|
:param extra_replacements: Extra replacements. Mapping of name to file-like
|
||||||
|
objects
|
||||||
|
|
||||||
'''
|
'''
|
||||||
z = ZipFile(zipstream, 'r')
|
z = ZipFile(zipstream, 'r')
|
||||||
|
replacements = {name:datastream}
|
||||||
|
replacements.update(extra_replacements)
|
||||||
|
names = frozenset(replacements.keys())
|
||||||
with SpooledTemporaryFile(max_size=100*1024*1024) as temp:
|
with SpooledTemporaryFile(max_size=100*1024*1024) as temp:
|
||||||
ztemp = ZipFile(temp, 'w')
|
ztemp = ZipFile(temp, 'w')
|
||||||
for obj in z.infolist():
|
for obj in z.infolist():
|
||||||
if obj.filename == name:
|
if obj.filename in names:
|
||||||
ztemp.writestr(obj, datastream.read())
|
ztemp.writestr(obj, replacements[obj.filename].read())
|
||||||
else:
|
else:
|
||||||
ztemp.writestr(obj, z.read_raw(obj), raw_bytes=True)
|
ztemp.writestr(obj, z.read_raw(obj), raw_bytes=True)
|
||||||
ztemp.close()
|
ztemp.close()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user