KG updates 0.7.20

This commit is contained in:
GRiker 2010-09-26 09:06:12 -07:00
commit 9b356fb6b6
65 changed files with 32518 additions and 25241 deletions

View File

@ -4,6 +4,99 @@
# for important features/bug fixes.
# Also, each release can have new and improved recipes.
- version: 0.7.20
date: 2010-09-24
new features:
- title: "Tweak epub feature."
type: major
description: >
"Now you can conveniently browse the contents of an epub, tweak them and rebuild the epub within your calibre library
by right clicking on the book and selecting Tweak ePub. See http://www.mobileread.com/forums/showthread.php?t=99875
for details."
- title: "Add button to Edit metadata dialog to trim borders from the cover"
- title: "Kobo driver: Add support for setting the ReadStatus to Read and correctly deal with empty collections"
- title: "Improved algorithm for removal of hyphens during pre-processing"
- title: "EPUB metadata: Don't read timestamp value from epubs as I am sick of closing bugs about adding books and having the Date not be today."
- title: "After bulk edit metadata, reselect previously selected books."
bug fixes:
- title: "Fix regression in 0.7.19 that broke the By Author and By Title category listing in Stanza/Aldiko feeds."
- title: "MOBI Output: Fix regression that broke sections list in downloaded periodicals on Kindle for non-english news sources"
- title: "News download: Rationalize cover processing."
tickets: [6852]
- title: "Cover cache: load images only in the GUI thread to prevent stale files being leftover by set_path due to Windows file locking"
- title: "Database: Make renaming of folders on case change more robust"
tickets: [6914]
- title: "When adding/replacing files to/in EPUB files, set the GPF bit for all files in the archive, to prevent unzip from complaining in linux"
tickets: [6363]
- title: "Plugin loading: Handle encoding declarations in .py files correctly"
- title: "MOBI input: Another corner case"
tickets: [6909]
- title: "IPC: Store results file in the calibre temp dir and also dont die if for some reason removing result file fails. Should make adding/saving more robust"
- title: "Database: Fix regression that caused has_cover to create empty directories unneccessarily"
- title: "Detection of Alex on unix"
tickets: [5900]
- title: "News download: Don't add inline table of contents when downloading news for the Kindle"
- title: "Add prologue and epilogue to default chapter detection regex"
- title: "Kobo driver: Fix issue where books that are read were getting their status reset to Unread"
- title: "Device drivers: Fix occassional false positive when matching books on device with books in the calibre library"
- title: "Content server: Making serving of large files more efficient."
- title: "GUI device detection: Handle case when user yanks connected device before device connection handler is called."
tickets: [6864]
- title: "Strip leading/trailing whitespace when setting metadata using the edit metadata dialog"
tickets: [6854]
- title: "KOBO: Editing the Im_Reading list with SD Card installed fixed"
tickets: [6850]
new recipes:
- title: "Neal's Nuze and Popular Science"
author: Tony Stegall
- title: "Rmf24.pl"
author: "Tomasz Dlugosz"
- title: "Gazeta Pomorska"
author: "Richard"
- title: "Le Journal de Montreal and superesportes"
author: "Luciano Furtado"
- title: "The Marker"
author: Marbs
- title: "Tagesanzeiger"
author: noxxx
improved recipes:
- Danas
- Harvard Business Review
- version: 0.7.19
date: 2010-09-17
@ -61,6 +154,7 @@
- title: "PDB Input: Fix bug in conversion of TOC in some PML files"
new recipes:
- title: "taz.de RSS"
author: Alexander Schremmer
@ -272,7 +366,7 @@
new features:
- title: "Multiple library support: Various improvements to make using multiple calibre libraries easier."
type: major
desc: >
description: >
"Now, when you switch libraries using the Choose Library button on the toolbar, entries are created in the menu of that button to easily switch to that library in the
future. Also, you can now right click on a book in the calibre library and use the 'Copy to library' action to copy the book to another library,
that you have switched to at least once. The name of the current library is shown in the titlebar.
@ -280,7 +374,7 @@
- title: "Content server: Allow setting a restriction so that the server shares only some of the books in the library."
type: major
desc: >
description: >
"You can now use a Saved Search as a restiction for the content server, via Preferences->Content Server. This will cause the
server to share only those books that match the saved search.
"

View File

@ -54,7 +54,7 @@ function render_book(book) {
formats = book.attr("formats").split(",");
if (formats.length > 0) {
for (i=0; i < formats.length; i++) {
title += '<a title="Download in '+formats[i]+' format" class="format" href="'+format_url(formats[i], id, book.attr("title"))+'">'+formats[i]+'</a>, ';
title += '<a title="Download in '+formats[i]+' format" class="format" href="'+format_url(formats[i], id, book.attr("safe_title"))+'">'+formats[i]+'</a>, ';
}
title = title.slice(0, title.length-2);
title += '&nbsp;({0}&nbsp;MB)&nbsp;'.format(size);

Binary file not shown.

After

Width:  |  Height:  |  Size: 652 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 646 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 323 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 922 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 334 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 634 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1017 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 722 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 722 B

View File

@ -0,0 +1,44 @@
from calibre.web.feeds.news import BasicNewsRecipe
from calibre.ebooks.BeautifulSoup import BeautifulSoup, re
class AdvancedUserRecipe1282101454(BasicNewsRecipe):
title = 'Nealz Nuze'
language = 'en'
__author__ = 'TonytheBookworm'
description = 'Neal Boortz Show Radio Notes'
publisher = 'Neal Boortz'
category = 'news, politics, USA, talkshow'
oldest_article = 2
max_articles_per_feed = 100
linearize_tables = True
no_stylesheets = True
remove_javascript = True
masthead_url = 'http://boortz.com/images/nuze_logo.gif'
keep_only_tags = [
dict(name='td', attrs={'id':['contentWellCell']})
]
remove_tags = [
dict(name='a', attrs={'class':['blogPermalink']}),
dict(name='span', attrs={'class':['blogBylineSeparator']}),
dict(name='td', attrs={'id':['nealztitle']}),
]
remove_tags_after = [dict(name='div', attrs={'class':'blogEntryBody'}),]
feeds = [
('NUZE', 'http://boortz.com/nealz_nuze_rss/rss.xml')
]

View File

@ -51,8 +51,14 @@ class Danas(BasicNewsRecipe):
preprocess_regexps = [
(re.compile(u'\u0110'), lambda match: u'\u00D0')
,(re.compile(u'\u201c'), lambda match: '"')
,(re.compile(u'\u201e'), lambda match: '"')
,(re.compile(u'\u2018'), lambda match: '&lsquo;') # left single quotation mark
,(re.compile(u'\u2019'), lambda match: '&rsquo;') # right single quotation mark
,(re.compile(u'\u201a'), lambda match: '&lsquo;') # single low-9 quotation mark
,(re.compile(u'\u201b'), lambda match: '&rsquo;') # single high-reversed-9 quotation mark
,(re.compile(u'\u201c'), lambda match: '&ldquo;') # left double quotation mark
,(re.compile(u'\u201d'), lambda match: '&rdquo;') # right double quotation mark
,(re.compile(u'\u201e'), lambda match: '&ldquo;') # double low-9 quotation mark
,(re.compile(u'\u201f'), lambda match: '&rdquo;') # double high-reversed-9 quotation mark
]
keep_only_tags = [dict(name='div', attrs={'id':'left'})]
@ -90,6 +96,8 @@ class Danas(BasicNewsRecipe):
,(u'Vostani Serbie' , u'http://www.danas.rs/rss/rss.asp?column_id=57')
,(u'Med&Jad-a' , u'http://www.danas.rs/rss/rss.asp?column_id=58')
,(u'Svetlosti pozornice' , u'http://www.danas.rs/rss/rss.asp?column_id=59')
,(u'Dva cvancika' , u'http://www.danas.rs/rss/rss.asp?column_id=65')
,(u'Iz kornera' , u'http://www.danas.rs/rss/rss.asp?column_id=64')
]
def preprocess_html(self, soup):

View File

@ -0,0 +1,104 @@
#!/usr/bin/env python
# # Przed uzyciem przeczytaj komentarz w sekcji "feeds"
__license__ = 'GPL v3'
__copyright__ = u'2010, Richard z forum.eksiazki.org'
'''pomorska.pl'''
import re
from calibre.web.feeds.news import BasicNewsRecipe
class GazetaPomorska(BasicNewsRecipe):
title = u'Gazeta Pomorska'
publisher = u'Gazeta Pomorska'
description = u'Kujawy i Pomorze - wiadomo\u015bci'
language = 'pl'
__author__ = u'Richard z forum.eksiazki.org'
# # (dziekuje t3d z forum.eksiazki.org za testy)
oldest_article = 2
max_articles_per_feed = 20
no_stylesheets = True
remove_javascript = True
preprocess_regexps = [
(re.compile(r'<a href="http://maps.google[^>]*>[^<]*</a>\.*', re.DOTALL|re.IGNORECASE), lambda m: ''),
(re.compile(r'[<Bb >]*Poznaj opinie[^<]*[</Bb >]*[^<]*<a href[^>]*>[^<]*</a>\.*', re.DOTALL|re.IGNORECASE), lambda m: ''),
(re.compile(r'[<Bb >]*Przeczytaj[^<]*[</Bb >]*[^<]*<a href[^>]*>[^<]*</a>\.*', re.DOTALL|re.IGNORECASE), lambda m: ''),
(re.compile(r'[<Bb >]*Wi.cej informacji[^<]*[</Bb >]*[^<]*<a href[^>]*>[^<]*</a>\.*', re.DOTALL|re.IGNORECASE), lambda m: ''),
(re.compile(r'<a href[^>]*>[<Bb >]*Wideo[^<]*[</Bb >]*[^<]*</a>\.*', re.DOTALL|re.IGNORECASE), lambda m: ''),
(re.compile(r'<a href[^>]*>[<Bb >]*KLIKNIJ TUTAJ[^<]*[</Bb >]*[^<]*</a>\.*', re.DOTALL|re.IGNORECASE), lambda m: '')
]
feeds = [
# # Tutaj jest wymieniona lista kategorii jakie mozemy otrzymywac z Gazety
# # Pomorskiej, po jednej kategorii w wierszu. Jesli na poczatku danego wiersza
# # znajduje sie jeden znak "#", oznacza to ze kategoria jest zakomentowana
# # i nie bedziemy jej otrzymywac. Jesli chcemy ja otrzymywac nalezy usunac
# # znak # z jej wiersza.
# # Jesli subskrybujemy wiecej niz jedna kategorie, na koncu wiersza z kazda
# # kategoria musi sie znajdowac niezakomentowany przecinek, z wyjatkiem
# # ostatniego wiersza - ma byc bez przecinka na koncu.
# # Rekomendowane opcje wyboru kategorii:
# # 1. PomorskaRSS - wiadomosci kazdego typu, lub
# # 2. Region + wybrane miasta, lub
# # 3. Wiadomosci tematyczne.
# # Lista kategorii:
# # PomorskaRSS - wiadomosci kazdego typu, zakomentuj znakiem "#"
# # przed odkomentowaniem wiadomosci wybranego typu:
(u'PomorskaRSS', u'http://www.pomorska.pl/rss.xml')
# # wiadomosci z regionu nie przypisane do okreslonego miasta:
# (u'Region', u'http://www.pomorska.pl/region.xml'),
# # wiadomosci przypisane do miast:
# (u'Bydgoszcz', u'http://www.pomorska.pl/bydgoszcz.xml'),
# (u'Nak\u0142o', u'http://www.pomorska.pl/naklo.xml'),
# (u'Koronowo', u'http://www.pomorska.pl/koronowo.xml'),
# (u'Solec Kujawski', u'http://www.pomorska.pl/soleckujawski.xml'),
# (u'Grudzi\u0105dz', u'http://www.pomorska.pl/grudziadz.xml'),
# (u'Inowroc\u0142aw', u'http://www.pomorska.pl/inowroclaw.xml'),
# (u'Toru\u0144', u'http://www.pomorska.pl/torun.xml'),
# (u'W\u0142oc\u0142awek', u'http://www.pomorska.pl/wloclawek.xml'),
# (u'Aleksandr\u00f3w Kujawski', u'http://www.pomorska.pl/aleksandrow.xml'),
# (u'Brodnica', u'http://www.pomorska.pl/brodnica.xml'),
# (u'Che\u0142mno', u'http://www.pomorska.pl/chelmno.xml'),
# (u'Chojnice', u'http://www.pomorska.pl/chojnice.xml'),
# (u'Ciechocinek', u'http://www.pomorska.pl/ciechocinek.xml'),
# (u'Golub Dobrzy\u0144', u'http://www.pomorska.pl/golubdobrzyn.xml'),
# (u'Mogilno', u'http://www.pomorska.pl/mogilno.xml'),
# (u'Radziej\u00f3w', u'http://www.pomorska.pl/radziejow.xml'),
# (u'Rypin', u'http://www.pomorska.pl/rypin.xml'),
# (u'S\u0119p\u00f3lno', u'http://www.pomorska.pl/sepolno.xml'),
# (u'\u015awiecie', u'http://www.pomorska.pl/swiecie.xml'),
# (u'Tuchola', u'http://www.pomorska.pl/tuchola.xml'),
# (u'\u017bnin', u'http://www.pomorska.pl/znin.xml')
# # wiadomosci tematyczne (redundancja z region/miasta):
# (u'Sport', u'http://www.pomorska.pl/sport.xml'),
# (u'Zdrowie', u'http://www.pomorska.pl/zdrowie.xml'),
# (u'Auto', u'http://www.pomorska.pl/moto.xml'),
# (u'Dom', u'http://www.pomorska.pl/dom.xml'),
# (u'Reporta\u017c', u'http://www.pomorska.pl/reportaz.xml'),
# (u'Gospodarka', u'http://www.pomorska.pl/gospodarka.xml')
]
keep_only_tags = [dict(name='div', attrs={'id':'article'})]
remove_tags = [
dict(name='p', attrs={'id':'articleTags'}),
dict(name='div', attrs={'id':'articleEpaper'}),
dict(name='div', attrs={'id':'articleConnections'}),
dict(name='div', attrs={'class':'articleFacts'}),
dict(name='div', attrs={'id':'articleExternalLink'}),
dict(name='div', attrs={'id':'articleMultimedia'}),
dict(name='div', attrs={'id':'articleGalleries'}),
dict(name='div', attrs={'id':'articleAlarm'}),
dict(name='div', attrs={'id':'adholder_srodek1'}),
dict(name='div', attrs={'id':'articleVideo'}),
dict(name='a', attrs={'name':'fb_share'})]
extra_css = '''h1 { font-size: 1.4em; }
h2 { font-size: 1.0em; }'''

View File

@ -0,0 +1,40 @@
from calibre.web.feeds.news import BasicNewsRecipe
class AdvancedUserRecipe1282101454(BasicNewsRecipe):
title = 'How To Geek'
language = 'en'
__author__ = 'TonytheBookworm'
description = 'Daily Computer Tips and Tricks'
publisher = 'Howtogeek'
category = 'PC,tips,tricks'
oldest_article = 2
max_articles_per_feed = 100
linearize_tables = True
no_stylesheets = True
remove_javascript = True
masthead_url = 'http://blog.stackoverflow.com/wp-content/uploads/how-to-geek-logo.png'
remove_tags =[dict(name='a', attrs={'target':['_blank']}),
dict(name='table', attrs={'id':['articleTable']}),
dict(name='div', attrs={'class':['feedflare']}),
]
feeds = [
('Tips', 'http://feeds.howtogeek.com/howtogeek')
]

View File

@ -0,0 +1,57 @@
from calibre.web.feeds.news import BasicNewsRecipe
class JerusalemPost(BasicNewsRecipe):
title = 'Jerusalem post'
language = 'fr'
__author__ = 'TonytheBookworm'
description = 'The Jerusalem Post (in French)'
publisher = 'jpost'
category = 'news'
oldest_article = 30
max_articles_per_feed = 100
linearize_tables = True
no_stylesheets = True
remove_javascript = True
masthead_url = 'http://static.jpost.com/JPSITES/images/JFrench/2008/site/jplogo.JFrench.gif'
remove_tags = [
dict(name='a', attrs={'href':['javascript:window.print()']}),
dict(name='div', attrs={'class':['bot']}),
]
feeds = [
('NEWS', 'http://fr.jpost.com/servlet/Satellite?collId=1216805762036&pagename=JFrench%2FPage%2FRSS'),
('JFrench En route vers la paix', 'http://fr.jpost.com/servlet/Satellite?collId=1216805762201&pagename=JFrench%2FPage%2FRSS'),
('JFrench Politique', 'http://fr.jpost.com/servlet/Satellite?collId=1215356737334&pagename=JFrench%2FPage%2FRSS'),
('JFrench Securite', 'http://fr.jpost.com/servlet/Satellite?collId=1215356737338&pagename=JFrench%2FPage%2FRSS'),
('JFrench Moyen Orient', 'http://fr.jpost.com/servlet/Satellite?collId=1215356737342&pagename=JFrench%2FPage%2FRSS'),
('JFrench Diplomatie / Monde', 'http://fr.jpost.com/servlet/Satellite?collId=1215356737346&pagename=JFrench%2FPage%2FRSS'),
('JFrench Economie / Sciences', 'http://fr.jpost.com/servlet/Satellite?collId=1215356737358&pagename=JFrench%2FPage%2FRSS'),
('JFrench Societe', 'http://fr.jpost.com/servlet/Satellite?collId=1215356737354&pagename=JFrench%2FPage%2FRSS'),
('JFrench Opinions', 'http://fr.jpost.com/servlet/Satellite?collId=1215356737350&pagename=JFrench%2FPage%2FRSS'),
('JFrench Monde juif', 'http://fr.jpost.com/servlet/Satellite?collId=1215356737366&pagename=JFrench%2FPage%2FRSS'),
('JFrench Culture / Sport', 'http://fr.jpost.com/servlet/Satellite?collId=1215356737362&pagename=JFrench%2FPage%2FRSS')
]
def print_version(self, url):
split1 = url.split("cid=")
#for testing only -------
#print 'SPLIT IS: ', split1
#print 'ORG URL IS: ', url
#---------------------------
idnum = split1[1] # get the actual value of the id article
#for testing only --------------------
#print 'the idnum is: ', idnum
#--------------------------------------
print_url = 'http://fr.jpost.com/servlet/Satellite?cid=' + idnum + '&pagename=JFrench%2FJPArticle%2FPrinter'
#for testing only -------------------------
#print 'PRINT URL IS: ', print_url
#------------------------------------------
return print_url
#example of how links should be formated
#--------------------------------------------------------------------------------------------------------------
#org version = http://fr.jpost.com/servlet/Satellite?pagename=JFrench/JPArticle/ShowFull&cid=1282804806075
#print version = http://fr.jpost.com/servlet/Satellite?cid=1282804806075&pagename=JFrench%2FJPArticle%2FPrinter
#------------------------------------------------------------------------------------------------------------------

View File

@ -1,35 +0,0 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2010, Tomasz Dlugosz <tomek3d@gmail.com>'
'''
nczas.com
'''
from calibre.web.feeds.news import BasicNewsRecipe
#
class NCzas(BasicNewsRecipe):
title = u'Najwy\u017cszy Czas!'
description = u'Najwy\u017cszy Czas!\nwydanie internetowe'
__author__ = u'Tomasz D\u0142ugosz'
language = 'pl'
oldest_article = 7
max_articles_per_feed = 100
no_stylesheets = True
cover_url = 'http://nczas.com/wp-content/themes/default/grafika/logo.png'
keep_only_tags = [dict(name='div', attrs={'class':'trescartykulu'})]
feeds = [(u'Najwy\u017cszy Czas!', u'http://nczas.com/feed/')]
def postprocess_html(self, soup, first):
for tag in soup.findAll(name= 'img', alt=""):
tag.extract()
for item in soup.findAll(align = "right"):
del item['align']
return soup

View File

@ -1,5 +1,5 @@
import re
from calibre.web.feeds.news import BasicNewsRecipe
from calibre.ebooks.BeautifulSoup import BeautifulSoup, re
class AdvancedUserRecipe1282101454(BasicNewsRecipe):
title = 'Popular Science'
@ -12,13 +12,11 @@ class AdvancedUserRecipe1282101454(BasicNewsRecipe):
max_articles_per_feed = 100
no_stylesheets = True
remove_javascript = True
use_embedded_content = True
masthead_url = 'http://www.raytheon.com/newsroom/rtnwcm/groups/Public/documents/masthead/rtn08_popscidec_masthead.jpg'
remove_tags = [dict(name='div', attrs={'id':['toolbar','main_supplements']}),
dict(name='span', attrs={'class':['comments']}),
dict(name='div', attrs={'class':['relatedinfo related-right','node_navigation','content2']}),
dict(name='ul', attrs={'class':['item-list clear-block']})]
feeds = [
('Gadgets', 'http://www.popsci.com/full-feed/gadgets'),

View File

@ -0,0 +1,46 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = u'2010, Tomasz Dlugosz <tomek3d@gmail.com>'
'''
rmf24.pl
'''
import re
from calibre.web.feeds.news import BasicNewsRecipe
class RMF24_ESKN(BasicNewsRecipe):
title = u'Rmf24.pl - Ekonomia Sport Kultura Nauka'
description = u'Ekonomia, sport, kultura i nauka ze strony rmf24.pl'
language = 'pl'
oldest_article = 7
max_articles_per_feed = 100
__author__ = u'Tomasz D\u0142ugosz'
no_stylesheets = True
remove_javascript = True
feeds = [(u'Ekonomia', u'http://www.rmf24.pl/ekonomia/feed'),
(u'Sport', u'http://www.rmf24.pl/sport/feed'),
(u'Kultura', u'http://www.rmf24.pl/kultura/feed'),
(u'Nauka', u'http://www.rmf24.pl/nauka/feed')]
keep_only_tags = [dict(name='div', attrs={'class':'box articleSingle print'})]
remove_tags = [
dict(name='div', attrs={'class':'toTop'}),
dict(name='div', attrs={'class':'category'}),
dict(name='div', attrs={'class':'REMOVE'}),
dict(name='div', attrs={'class':'embed embedAd'})]
extra_css = '''
h1 { font-size: 1.2em; }
'''
preprocess_regexps = [
(re.compile(i[0], re.IGNORECASE | re.DOTALL), i[1]) for i in
[
(r'<h2>Zdj.cie</h2>', lambda match: ''),
(r'embed embed(Left|Right|Center) articleEmbed(Audio|Wideo articleEmbedVideo|ArticleFull|ArticleTitle|ArticleListTitle|AlbumHorizontal)">', lambda match: 'REMOVE">'),
(r'<a href="http://www.facebook.com/pages/RMF24pl/.*?>RMF24.pl</a> on Facebook</div>', lambda match: '</div>')
]
]

View File

@ -0,0 +1,44 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = u'2010, Tomasz Dlugosz <tomek3d@gmail.com>'
'''
rmf24.pl
'''
import re
from calibre.web.feeds.news import BasicNewsRecipe
class RMF24(BasicNewsRecipe):
title = u'Rmf24.pl - Fakty'
description = u'Fakty ze strony rmf24.pl'
language = 'pl'
oldest_article = 7
max_articles_per_feed = 100
__author__ = u'Tomasz D\u0142ugosz'
no_stylesheets = True
remove_javascript = True
feeds = [(u'Kraj', u'http://www.rmf24.pl/fakty/polska/feed'),
(u'\u015awiat', u'http://www.rmf24.pl/fakty/swiat/feed')]
keep_only_tags = [dict(name='div', attrs={'class':'box articleSingle print'})]
remove_tags = [
dict(name='div', attrs={'id':'adBox625'}),
dict(name='div', attrs={'class':'toTop'}),
dict(name='div', attrs={'class':'category'}),
dict(name='div', attrs={'class':'REMOVE'}),
dict(name='div', attrs={'class':'embed embedAd'})]
extra_css = '''
h1 { font-size: 1.2em; }
'''
preprocess_regexps = [
(re.compile(i[0], re.IGNORECASE | re.DOTALL), i[1]) for i in
[
(r'<h2>Zdj.cie</h2>', lambda match: ''),
(r'embed embed(Left|Right|Center) articleEmbed(Audio|Wideo articleEmbedVideo|ArticleFull|ArticleTitle|ArticleListTitle|AlbumHorizontal)">', lambda match: 'REMOVE">'),
(r'<a href="http://www.facebook.com/pages/RMF24pl/.*?>RMF24.pl</a> on Facebook</div>', lambda match: '</div>')
]
]

View File

@ -1,68 +1,53 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en'
'''
sciam.com
'''
import re
from calibre.web.feeds.news import BasicNewsRecipe
class ScientificAmerican(BasicNewsRecipe):
title = u'Scientific American'
description = u'Popular science. Monthly magazine.'
__author__ = 'Kovid Goyal and Sujata Raman'
description = u'Popular Science. Monthly magazine.'
category = 'science'
__author__ = 'Starson17'
no_stylesheets = True
use_embedded_content = False
language = 'en'
publisher = 'Nature Publishing Group'
remove_empty_feeds = True
remove_javascript = True
oldest_article = 30
max_articles_per_feed = 100
no_stylesheets = True
use_embedded_content = False
extra_css = '''
p{font-weight: normal; font-size:small}
li{font-weight: normal; font-size:small}
.headline p{font-size:x-small; font-family:Arial,Helvetica,sans-serif;}
h2{font-size:x-small;}
h3{font-size:x-small;font-family:Arial,Helvetica,sans-serif;}
'''
remove_tags_before = dict(name='div', attrs={'class':'headline'})
remove_tags_after = dict(id=['article'])
remove_tags = [
dict(id=['sharetools', 'reddit']),
#dict(name='script'),
{'class':['float_left', 'atools']},
{"class": re.compile(r'also-in-this')},
dict(name='a',title = ["Get the Rest of the Article","Subscribe","Buy this Issue"]),
dict(name = 'img',alt = ["Graphic - Get the Rest of the Article"]),
dict(name='div', attrs={'class':['commentbox']}),
dict(name='h2', attrs={'class':['discuss_h2']}),
conversion_options = {'linearize_tables' : True
, 'comment' : description
, 'tags' : category
, 'publisher' : publisher
, 'language' : language
}
keep_only_tags = [
dict(name='h2', attrs={'class':'articleTitle'})
,dict(name='p', attrs={'id':'articleDek'})
,dict(name='p', attrs={'class':'articleInfo'})
,dict(name='div', attrs={'id':['articleContent']})
,dict(name='img', attrs={'src':re.compile(r'/media/inline/blog/Image/', re.DOTALL|re.IGNORECASE)})
]
html2lrf_options = ['--base-font-size', '8']
recursions = 1
match_regexps = [r'article.cfm.id=\S+page=(2|3|4|5|6|7|8|9|10|11|12|13|14|15)']
remove_tags = [dict(name='a', attrs={'class':'tinyCommentCount'})]
def parse_index(self):
soup = self.index_to_soup('http://www.scientificamerican.com/sciammag/')
monthtag = soup.find('div',attrs={'id':'magazine-main_col2'})
month = self.tag_to_string(monthtag.contents[1])
self.timefmt = ' [%s]'%(self.tag_to_string(month))
issuetag = soup.find('p',attrs={'id':'articleDek'})
self.timefmt = ' [%s]'%(self.tag_to_string(issuetag))
img = soup.find('img', alt='Scientific American Magazine', src=True)
if img is not None:
self.cover_url = img['src']
features, feeds = [], []
for p in soup.find(id='magazine-main_col2').findAll('p') :
a = p.find('a', href=True)
for a in soup.find(attrs={'class':'primaryCol'}).findAll('a',attrs={'title':'Feature'}):
if a is None: continue
desc = ''
s = p.find('span', attrs={'class':"sub"})
s = a.parent.parent.find(attrs={'class':'dek'})
desc = self.tag_to_string(s)
article = {
'url' : a['href'],
'title' : self.tag_to_string(a),
@ -71,51 +56,36 @@ class ScientificAmerican(BasicNewsRecipe):
}
features.append(article)
feeds.append(('Features', features))
section = []
department = []
title = None
for x in soup.find(id='magazine-main_col1').findAll(['div', 'a']):
if x.name == 'div':
if section:
feeds.append((title, section))
title = self.tag_to_string(x)
section = []
else:
if 'article.cfm' in x['href']:
for li in soup.find(attrs={'class':'secondaryCol'}).findAll('li'):
if 'department.cfm' in li.a['href']:
if department:
feeds.append((title, department))
title = self.tag_to_string(li.a)
department = []
if 'article.cfm' in li.h3.a['href']:
article = {
'url' : x['href'],
'title' : self.tag_to_string(x),
'url' : li.h3.a['href'],
'title' : self.tag_to_string(li.h3.a),
'date': '',
'description': '',
'description': self.tag_to_string(li.p),
}
section.append(article)
if section:
feeds.append((title, section))
department.append(article)
if department:
feeds.append((title, department))
return feeds
def postprocess_html(self, soup, first_fetch):
if soup is not None:
for span in soup.findAll('span', attrs={'class':'pagination'}):
span.extract()
if not first_fetch:
div = soup.find('div', attrs={'class':'headline'})
if div:
div.extract()
for item in soup.findAll('a'):
if 'topic.cfm' in item['href']:
item.replaceWith(item.string)
return soup
preprocess_regexps = [
(re.compile(r'Already a Digital subscriber.*Now</a>', re.DOTALL|re.IGNORECASE), lambda match: ''),
(re.compile(r'If your institution has site license access, enter.*here</a>.', re.DOTALL|re.IGNORECASE), lambda match: ''),
(re.compile(r'to subscribe to our.*;.*\}', re.DOTALL|re.IGNORECASE), lambda match: ''),
(re.compile(r'\)\(jQuery\);.*-->', re.DOTALL|re.IGNORECASE), lambda match: ''),
]
extra_css = '''
p{font-weight: normal; font-size:small}
li{font-weight: normal; font-size:small}
.headline p{font-size:x-small; font-family:Arial,Helvetica,sans-serif;}
h2{font-size:large; font-family:Arial,Helvetica,sans-serif;}
h3{font-size:x-small;font-family:Arial,Helvetica,sans-serif;}
'''

View File

@ -0,0 +1,79 @@
__license__ = 'GPL v3'
__copyright__ = '2010, Luciano Furtado <lrfurtado at yahoo.com.br>'
'''
www.superesportes.com.br
'''
from calibre.web.feeds.news import BasicNewsRecipe
class SuperEsportesRecipe(BasicNewsRecipe):
title = u'www.superesportes.com.br'
description = u'Superesportes - Notícias do esporte no Brasil e no mundo'
__author__ = 'Luciano Furtado'
language = 'pt'
category = 'esportes, Brasil'
no_stylesheets = True
oldest_article = 7
use_embedded_content=0
max_articles_per_feed = 10
cover_url = 'http://imgs.mg.superesportes.com.br/superesportes_logo.png'
extra_css = 'div.info_noticias h1 { font-size: 100% }'
remove_tags = [
dict(name='div',attrs={'class':'topo'}),
dict(name='div',attrs={'class':'rodape'}),
dict(name='div',attrs={'class':'navegacao'}),
dict(name='div',attrs={'class':'lateral2'}),
dict(name='div',attrs={'class':'leia_mais'}),
dict(name='div',attrs={'id':'comentar'}),
dict(name='div',attrs={'id':'vrumelc_noticia'}),
dict(name='div',attrs={'class':'compartilhe'}),
dict(name='div',attrs={'class':'linha_noticias'}),
dict(name='div',attrs={'class':'botoes_noticias'}),
dict(name='div',attrs={'class':'barra_time bg_time'}),
]
def parse_index(self):
feeds = []
sections = [
(u'Atletico', 'http://www.df.superesportes.com.br/futebol/atletico-mg/capa_atletico_mg/index.shtml'),
(u'Botafogo', 'http://www.df.superesportes.com.br/futebol/botafogo/capa_botafogo/index.shtml'),
(u'Corinthinas', 'http://www.df.superesportes.com.br/futebol/corinthians/capa_corinthians/index.shtml'),
(u'Cruzeiro', 'http://www.df.superesportes.com.br/futebol/cruzeiro/capa_cruzeiro/index.shtml'),
(u'Flamengo', 'http://www.df.superesportes.com.br/futebol/flamengo/capa_flamengo/index.shtml'),
(u'Fluminense', 'http://www.df.superesportes.com.br/futebol/fluminense/capa_fluminense/index.shtml'),
(u'Palmeiras', 'http://www.df.superesportes.com.br/futebol/palmeiras/capa_palmeiras/index.shtml'),
(u'Santos', 'http://www.df.superesportes.com.br/futebol/santos/capa_santos/index.shtml'),
(u'S√£o Paulo', 'http://www.df.superesportes.com.br/futebol/sao-paulo/capa_sao_paulo/index.shtml'),
(u'Vasco', 'http://www.df.superesportes.com.br/futebol/vasco/capa_vasco/index.shtml'),
]
for section, url in sections:
current_articles = []
soup = self.index_to_soup(url)
latestNews = soup.find(name='ul',attrs={'class': 'lista_ultimas_noticias'})
for li_tag in latestNews.findAll(name='li'):
a_tag = li_tag.find('a', href= True)
if a_tag is None:
continue
title = self.tag_to_string(a_tag)
url = a_tag.get('href', False)
self.log("\n\nFound title: " + title + "\nUrl: " + url + "\nSection: " + section)
current_articles.append({'title': title, 'url': url, 'description': title, 'date':''})
if current_articles:
feeds.append((section, current_articles))
return feeds

View File

@ -2,7 +2,7 @@ __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en'
__appname__ = 'calibre'
__version__ = '0.7.19'
__version__ = '0.7.20'
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
import re

View File

@ -67,10 +67,17 @@ def load_plugin(path_to_zip_file): # {{{
if name.lower().endswith('plugin.py'):
locals = {}
raw = zf.read(name)
match = re.search(r'coding[:=]\s*([-\w.]+)', raw[:300])
encoding = 'utf-8'
lines, encoding = raw.splitlines(), 'utf-8'
cr = re.compile(r'coding[:=]\s*([-\w.]+)')
raw = []
for l in lines[:2]:
match = cr.search(l)
if match is not None:
encoding = match.group(1)
else:
raw.append(l)
raw += lines[2:]
raw = '\n'.join(raw)
raw = raw.decode(encoding)
raw = re.sub('\r\n', '\n', raw)
exec raw in locals

View File

@ -34,7 +34,7 @@ class ANDROID(USBMS):
0x227]},
# Samsung
0x04e8 : { 0x681d : [0x0222, 0x0400],
0x04e8 : { 0x681d : [0x0222, 0x0224, 0x0400],
0x681c : [0x0222, 0x0224, 0x0400],
0x6640 : [0x0100],
},

View File

@ -41,6 +41,10 @@ class Book(MetaInformation):
self.authors = ['']
else:
self.authors = [authors]
if not title:
self.title = _('Unknown')
self.mime = mime
self.size = size # will be set later if None

View File

@ -389,6 +389,7 @@ class HTMLPreProcessor(object):
if is_pdftohtml:
end_rules.append((re.compile(r'<p>\s*(?P<chap>(<[ibu]>){0,2}\s*([A-Z \'"!]{3,})\s*([\dA-Z:]+\s){0,4}\s*(</[ibu]>){0,2})\s*<p>\s*(?P<title>(<[ibu]>){0,2}(\s*\w+){1,4}\s*(</[ibu]>){0,2}\s*<p>)?'), chap_head),)
length = -1
if getattr(self.extra_opts, 'unwrap_factor', 0.0) > 0.01:
length = line_length('pdf', html, getattr(self.extra_opts, 'unwrap_factor'))
if length:
@ -425,7 +426,7 @@ class HTMLPreProcessor(object):
for rule in rules + end_rules:
html = rule[0].sub(rule[1], html)
if is_pdftohtml:
if is_pdftohtml and length > -1:
# Dehyphenate
dehyphenator = Dehyphenator()
html = dehyphenator(html,'pdf', length)

View File

@ -1574,6 +1574,7 @@ class MobiWriter(object):
id = unicode(oeb.metadata.cover[0])
item = oeb.manifest.ids[id]
href = item.href
if href in self._images:
index = self._images[href] - 1
exth.write(pack('>III', 0xc9, 0x0c, index))
exth.write(pack('>III', 0xcb, 0x0c, 0))

View File

@ -15,7 +15,7 @@ from calibre.customize.ui import available_input_formats
from calibre.ebooks.metadata.opf2 import OPF
from calibre.ptempfile import TemporaryDirectory
from calibre.ebooks.chardet import xml_to_unicode
from calibre.utils.zipfile import safe_replace, ZipFile
from calibre.utils.zipfile import safe_replace
from calibre.utils.config import DynamicConfig
from calibre.utils.logging import Log
from calibre import guess_type, prints
@ -294,12 +294,8 @@ class EbookIterator(object):
zf = open(self.pathtoebook, 'r+b')
except IOError:
return
zipf = ZipFile(zf, mode='a')
for name in zipf.namelist():
if name == 'META-INF/calibre_bookmarks.txt':
safe_replace(zf, 'META-INF/calibre_bookmarks.txt', StringIO(dat))
return
zipf.writestr('META-INF/calibre_bookmarks.txt', dat)
safe_replace(zf, 'META-INF/calibre_bookmarks.txt', StringIO(dat),
add_missing=True)
else:
self.config['bookmarks_'+self.pathtoebook] = dat

View File

@ -219,7 +219,10 @@ class CSSFlattener(object):
fnums = self.context.source.fnums
if size[0] in ('+', '-'):
# Oh, the warcrimes
try:
esize = 3 + force_int(size)
except:
esize = 3
if esize < 1:
esize = 1
if esize > 7:

View File

@ -603,18 +603,6 @@ class Application(QApplication):
self._file_open_paths = []
self._file_open_lock = RLock()
if islinux:
self.setStyleSheet('''
QToolTip {
border: 2px solid black;
padding: 5px;
border-radius: 10px;
opacity: 200;
background-color: #e1e1ff;
color: black;
}
''')
def _send_file_open_events(self):
with self._file_open_lock:
if self._file_open_paths:

View File

@ -819,7 +819,8 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
fname = err.filename if err.filename else 'file'
return error_dialog(self, _('Permission denied'),
_('Could not open %s. Is it being used by another'
' program?')%fname, show=True)
' program?')%fname, det_msg=traceback.format_exc(),
show=True)
raise
self.save_state()
QDialog.accept(self)

View File

@ -28,12 +28,12 @@ class DBUSNotifier(Notifier):
ICON = I('notify.png')
def __init__(self, server, path):
def __init__(self, server, path, interface):
self.ok, self.err = True, None
try:
import dbus
self.dbus = dbus
self._notify = dbus.SessionBus().get_object(server, path)
self._notify = dbus.Interface(dbus.SessionBus().get_object(server, path), interface)
except Exception, err:
self.ok = False
self.err = str(err)
@ -43,7 +43,7 @@ class KDENotifier(DBUSNotifier):
def __init__(self):
DBUSNotifier.__init__(self, 'org.kde.VisualNotifications',
'/VisualNotifications')
'/VisualNotifications', 'org.kde.VisualNotifications')
def __call__(self, body, summary=None, replaces_id=None, timeout=0):
if replaces_id is None:
@ -62,7 +62,7 @@ class FDONotifier(DBUSNotifier):
def __init__(self):
DBUSNotifier.__init__(self, 'org.freedesktop.Notifications',
'/org/freedesktop/Notifications')
'/org/freedesktop/Notifications', 'org.freedesktop.Notifications')
def __call__(self, body, summary=None, replaces_id=None, timeout=0):
if replaces_id is None:

View File

@ -358,10 +358,10 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
return row[self.FIELD_MAP['path']].replace('/', os.sep)
def abspath(self, index, index_is_id=False):
def abspath(self, index, index_is_id=False, create_dirs=True):
'Return the absolute path to the directory containing this books files as a unicode string.'
path = os.path.join(self.library_path, self.path(index, index_is_id=index_is_id))
if not os.path.exists(path):
if create_dirs and not os.path.exists(path):
os.makedirs(path)
return path
@ -443,6 +443,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
self.add_format(id, format, stream, index_is_id=True,
path=tpath, notify=False)
self.conn.execute('UPDATE books SET path=? WHERE id=?', (path, id))
self.conn.commit()
self.data.set(id, self.FIELD_MAP['path'], path, row_is_id=True)
# Delete not needed directories
if current_path and os.path.exists(spath):
@ -451,6 +452,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
parent = os.path.dirname(spath)
if len(os.listdir(parent)) == 0:
self.rmtree(parent, permanent=True)
curpath = self.library_path
c1, c2 = current_path.split('/'), path.split('/')
if not self.is_case_sensitive and len(c1) == len(c2):
@ -465,13 +467,10 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
# the directories, so no need to do them here.
for oldseg, newseg in zip(c1, c2):
if oldseg.lower() == newseg.lower() and oldseg != newseg:
while True:
# need a temp name in the current segment for renames
tempname = os.path.join(curpath, 'TEMP.%f'%time.time())
if not os.path.exists(tempname):
break
os.rename(os.path.join(curpath, oldseg), tempname)
os.rename(tempname, os.path.join(curpath, newseg))
try:
os.rename(os.path.join(curpath, oldseg), os.path.join(curpath, newseg))
except:
break # Fail silently since nothing catastrophic has happened
curpath = os.path.join(curpath, newseg)
def add_listener(self, listener):
@ -599,7 +598,8 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
def has_cover(self, index, index_is_id=False):
id = index if index_is_id else self.id(index)
try:
path = os.path.join(self.abspath(id, index_is_id=True), 'cover.jpg')
path = os.path.join(self.abspath(id, index_is_id=True,
create_dirs=False), 'cover.jpg')
except:
# Can happen if path has not yet been set
return False
@ -721,7 +721,13 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
path = self.format_abspath(index, format, index_is_id=index_is_id)
if path is not None:
f = open(path, mode)
try:
ret = f if as_file else f.read()
except IOError:
f.seek(0)
out = cStringIO.StringIO()
shutil.copyfileobj(f, out)
ret = out.getvalue()
if not as_file:
f.close()
return ret

View File

@ -123,8 +123,6 @@ class ContentServer(object):
return self.static('index.html')
# Actually get content from the database {{{
def get_cover(self, id, thumbnail=False):
cover = self.db.cover(id, index_is_id=True, as_file=False)

View File

@ -18,6 +18,7 @@ from calibre.ebooks.metadata import fmt_sidx
from calibre.constants import __appname__
from calibre import human_readable
from calibre.utils.date import utcfromtimestamp, format_date
from calibre.utils.filenames import ascii_filename
def CLASS(*args, **kwargs): # class is a reserved word in Python
kwargs['class'] = ' '.join(args)
@ -110,11 +111,13 @@ def build_index(books, num, search, sort, order, start, total, url_base, CKEYS):
data = TD()
last = None
for fmt in book['formats'].split(','):
a = ascii_filename(book['authors'])
t = ascii_filename(book['title'])
s = SPAN(
A(
fmt.lower(),
href='/get/%s/%s-%s_%d.%s' % (fmt, book['authors'],
book['title'], book['id'], fmt)
href='/get/%s/%s-%s_%d.%s' % (fmt, a, t,
book['id'], fmt)
),
CLASS('button'))
s.tail = u'\u202f' # &nbsp;

View File

@ -16,6 +16,7 @@ from calibre.ebooks.metadata import fmt_sidx
from calibre.constants import preferred_encoding
from calibre import isbytestring
from calibre.utils.date import format_date
from calibre.utils.filenames import ascii_filename
E = ElementMaker()
@ -88,6 +89,8 @@ class XMLServer(object):
y = format_tag_string(y, ',')
kwargs[x] = serialize(y) if y else ''
kwargs['safe_title'] = ascii_filename(kwargs['title'])
c = kwargs.pop('comments')
CFM = self.db.field_metadata

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

View File

@ -18,6 +18,7 @@ from calibre.utils.ipc.launch import Worker
from calibre.utils.ipc.worker import PARALLEL_FUNCS
from calibre import detect_ncpus as cpu_count
from calibre.constants import iswindows
from calibre.ptempfile import base_dir
_counter = 0
@ -114,8 +115,9 @@ class Server(Thread):
with self._worker_launch_lock:
self.launched_worker_count += 1
id = self.launched_worker_count
rfile = os.path.join(tempfile.gettempdir(),
'calibre_ipc_result_%d_%d.pickle'%(self.id, id))
fd, rfile = tempfile.mkstemp(prefix='ipc_result_%d_%d_'%(self.id, id),
dir=base_dir(), suffix='.pickle')
os.close(fd)
if redirect_output is None:
redirect_output = not gui
@ -189,8 +191,11 @@ class Server(Thread):
job.failed = True
job.returncode = worker.returncode
elif os.path.exists(worker.rfile):
try:
job.result = cPickle.load(open(worker.rfile, 'rb'))
os.remove(worker.rfile)
except:
pass
job.duration = time.time() - job.start_time
self.changed_jobs_queue.put(job)

View File

@ -281,7 +281,7 @@ class ZipInfo (object):
'file_offset',
)
def __init__(self, filename="NoName", date_time=(1980,1,1,0,0,0)):
def __init__(self, filename=u"NoName", date_time=(1980,1,1,0,0,0)):
self.orig_filename = filename # Original file name in archive
# Terminate the file name at the first null byte. Null bytes in file
@ -1362,30 +1362,42 @@ class ZipFile:
self.fp.close()
self.fp = None
def safe_replace(zipstream, name, datastream, extra_replacements={}):
def safe_replace(zipstream, name, datastream, extra_replacements={},
add_missing=False):
'''
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`
sometimes created corrupted zip files.
:param zipstream: Stream from a zip file
:param name: The name of the file to replace
:param datastream: The data to replace the file with.
:param extra_replacements: Extra replacements. Mapping of name to file-like
objects
:param add_missing: If a replacement does not exist in the zip file, it is
added. Use with care as currently parent directories
are not created.
'''
z = ZipFile(zipstream, 'r')
replacements = {name:datastream}
replacements.update(extra_replacements)
names = frozenset(replacements.keys())
found = set([])
with SpooledTemporaryFile(max_size=100*1024*1024) as temp:
ztemp = ZipFile(temp, 'w')
for obj in z.infolist():
if isinstance(obj.filename, unicode):
obj.flag_bits |= 0x16 # Set isUTF-8 bit
if obj.filename in names:
ztemp.writestr(obj, replacements[obj.filename].read())
found.add(obj.filename)
else:
ztemp.writestr(obj, z.read_raw(obj), raw_bytes=True)
if add_missing:
for name in names - found:
ztemp.writestr(name, replacements[name].read())
ztemp.close()
z.close()
temp.seek(0)