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. # 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.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 - version: 0.7.19
date: 2010-09-17 date: 2010-09-17
@ -61,6 +154,7 @@
- title: "PDB Input: Fix bug in conversion of TOC in some PML files" - title: "PDB Input: Fix bug in conversion of TOC in some PML files"
new recipes: new recipes:
- title: "taz.de RSS" - title: "taz.de RSS"
author: Alexander Schremmer author: Alexander Schremmer
@ -272,7 +366,7 @@
new features: new features:
- title: "Multiple library support: Various improvements to make using multiple calibre libraries easier." - title: "Multiple library support: Various improvements to make using multiple calibre libraries easier."
type: major 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 "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, 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. 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." - title: "Content server: Allow setting a restriction so that the server shares only some of the books in the library."
type: major 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 "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. 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(","); formats = book.attr("formats").split(",");
if (formats.length > 0) { if (formats.length > 0) {
for (i=0; i < formats.length; i++) { 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 = title.slice(0, title.length-2);
title += '&nbsp;({0}&nbsp;MB)&nbsp;'.format(size); 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 = [ preprocess_regexps = [
(re.compile(u'\u0110'), lambda match: u'\u00D0') (re.compile(u'\u0110'), lambda match: u'\u00D0')
,(re.compile(u'\u201c'), lambda match: '"') ,(re.compile(u'\u2018'), lambda match: '&lsquo;') # left single quotation mark
,(re.compile(u'\u201e'), lambda match: '"') ,(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'})] 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'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'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'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): 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.web.feeds.news import BasicNewsRecipe
from calibre.ebooks.BeautifulSoup import BeautifulSoup, re
class AdvancedUserRecipe1282101454(BasicNewsRecipe): class AdvancedUserRecipe1282101454(BasicNewsRecipe):
title = 'Popular Science' title = 'Popular Science'
@ -12,13 +12,11 @@ class AdvancedUserRecipe1282101454(BasicNewsRecipe):
max_articles_per_feed = 100 max_articles_per_feed = 100
no_stylesheets = True no_stylesheets = True
remove_javascript = True remove_javascript = True
use_embedded_content = True
masthead_url = 'http://www.raytheon.com/newsroom/rtnwcm/groups/Public/documents/masthead/rtn08_popscidec_masthead.jpg' 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 = [ feeds = [
('Gadgets', 'http://www.popsci.com/full-feed/gadgets'), ('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 #!/usr/bin/env python
__license__ = 'GPL v3' __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en'
'''
sciam.com
'''
import re import re
from calibre.web.feeds.news import BasicNewsRecipe from calibre.web.feeds.news import BasicNewsRecipe
class ScientificAmerican(BasicNewsRecipe): class ScientificAmerican(BasicNewsRecipe):
title = u'Scientific American' title = u'Scientific American'
description = u'Popular science. Monthly magazine.' description = u'Popular Science. Monthly magazine.'
__author__ = 'Kovid Goyal and Sujata Raman' category = 'science'
__author__ = 'Starson17'
no_stylesheets = True
use_embedded_content = False
language = 'en' language = 'en'
publisher = 'Nature Publishing Group'
remove_empty_feeds = True
remove_javascript = True remove_javascript = True
oldest_article = 30 oldest_article = 30
max_articles_per_feed = 100 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']) conversion_options = {'linearize_tables' : True
remove_tags = [ , 'comment' : description
dict(id=['sharetools', 'reddit']), , 'tags' : category
#dict(name='script'), , 'publisher' : publisher
{'class':['float_left', 'atools']}, , 'language' : language
{"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"]), keep_only_tags = [
dict(name='div', attrs={'class':['commentbox']}), dict(name='h2', attrs={'class':'articleTitle'})
dict(name='h2', attrs={'class':['discuss_h2']}), ,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'] remove_tags = [dict(name='a', attrs={'class':'tinyCommentCount'})]
recursions = 1
match_regexps = [r'article.cfm.id=\S+page=(2|3|4|5|6|7|8|9|10|11|12|13|14|15)']
def parse_index(self): def parse_index(self):
soup = self.index_to_soup('http://www.scientificamerican.com/sciammag/') soup = self.index_to_soup('http://www.scientificamerican.com/sciammag/')
monthtag = soup.find('div',attrs={'id':'magazine-main_col2'}) issuetag = soup.find('p',attrs={'id':'articleDek'})
month = self.tag_to_string(monthtag.contents[1]) self.timefmt = ' [%s]'%(self.tag_to_string(issuetag))
self.timefmt = ' [%s]'%(self.tag_to_string(month))
img = soup.find('img', alt='Scientific American Magazine', src=True) img = soup.find('img', alt='Scientific American Magazine', src=True)
if img is not None: if img is not None:
self.cover_url = img['src'] self.cover_url = img['src']
features, feeds = [], [] features, feeds = [], []
for p in soup.find(id='magazine-main_col2').findAll('p') : for a in soup.find(attrs={'class':'primaryCol'}).findAll('a',attrs={'title':'Feature'}):
a = p.find('a', href=True)
if a is None: continue if a is None: continue
desc = '' desc = ''
s = p.find('span', attrs={'class':"sub"}) s = a.parent.parent.find(attrs={'class':'dek'})
desc = self.tag_to_string(s) desc = self.tag_to_string(s)
article = { article = {
'url' : a['href'], 'url' : a['href'],
'title' : self.tag_to_string(a), 'title' : self.tag_to_string(a),
@ -71,51 +56,36 @@ class ScientificAmerican(BasicNewsRecipe):
} }
features.append(article) features.append(article)
feeds.append(('Features', features)) feeds.append(('Features', features))
department = []
section = []
title = None title = None
for li in soup.find(attrs={'class':'secondaryCol'}).findAll('li'):
for x in soup.find(id='magazine-main_col1').findAll(['div', 'a']): if 'department.cfm' in li.a['href']:
if department:
if x.name == 'div': feeds.append((title, department))
title = self.tag_to_string(li.a)
if section: department = []
feeds.append((title, section)) if 'article.cfm' in li.h3.a['href']:
title = self.tag_to_string(x)
section = []
else:
if 'article.cfm' in x['href']:
article = { article = {
'url' : x['href'], 'url' : li.h3.a['href'],
'title' : self.tag_to_string(x), 'title' : self.tag_to_string(li.h3.a),
'date': '', 'date': '',
'description': '', 'description': self.tag_to_string(li.p),
} }
department.append(article)
section.append(article) if department:
feeds.append((title, department))
if section:
feeds.append((title, section))
return feeds return feeds
def postprocess_html(self, soup, first_fetch): def postprocess_html(self, soup, first_fetch):
if soup is not None: for item in soup.findAll('a'):
for span in soup.findAll('span', attrs={'class':'pagination'}): if 'topic.cfm' in item['href']:
span.extract() item.replaceWith(item.string)
if not first_fetch:
div = soup.find('div', attrs={'class':'headline'})
if div:
div.extract()
return soup return soup
preprocess_regexps = [ extra_css = '''
(re.compile(r'Already a Digital subscriber.*Now</a>', re.DOTALL|re.IGNORECASE), lambda match: ''), p{font-weight: normal; font-size:small}
(re.compile(r'If your institution has site license access, enter.*here</a>.', re.DOTALL|re.IGNORECASE), lambda match: ''), li{font-weight: normal; font-size:small}
(re.compile(r'to subscribe to our.*;.*\}', re.DOTALL|re.IGNORECASE), lambda match: ''), .headline p{font-size:x-small; font-family:Arial,Helvetica,sans-serif;}
(re.compile(r'\)\(jQuery\);.*-->', re.DOTALL|re.IGNORECASE), lambda match: ''), 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' __copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
__appname__ = 'calibre' __appname__ = 'calibre'
__version__ = '0.7.19' __version__ = '0.7.20'
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>" __author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
import re import re

View File

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

View File

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

View File

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

View File

@ -389,6 +389,7 @@ class HTMLPreProcessor(object):
if is_pdftohtml: 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),) 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: if getattr(self.extra_opts, 'unwrap_factor', 0.0) > 0.01:
length = line_length('pdf', html, getattr(self.extra_opts, 'unwrap_factor')) length = line_length('pdf', html, getattr(self.extra_opts, 'unwrap_factor'))
if length: if length:
@ -425,7 +426,7 @@ class HTMLPreProcessor(object):
for rule in rules + end_rules: for rule in rules + end_rules:
html = rule[0].sub(rule[1], html) html = rule[0].sub(rule[1], html)
if is_pdftohtml: if is_pdftohtml and length > -1:
# Dehyphenate # Dehyphenate
dehyphenator = Dehyphenator() dehyphenator = Dehyphenator()
html = dehyphenator(html,'pdf', length) html = dehyphenator(html,'pdf', length)

View File

@ -1574,6 +1574,7 @@ class MobiWriter(object):
id = unicode(oeb.metadata.cover[0]) id = unicode(oeb.metadata.cover[0])
item = oeb.manifest.ids[id] item = oeb.manifest.ids[id]
href = item.href href = item.href
if href in self._images:
index = self._images[href] - 1 index = self._images[href] - 1
exth.write(pack('>III', 0xc9, 0x0c, index)) exth.write(pack('>III', 0xc9, 0x0c, index))
exth.write(pack('>III', 0xcb, 0x0c, 0)) 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.ebooks.metadata.opf2 import OPF
from calibre.ptempfile import TemporaryDirectory from calibre.ptempfile import TemporaryDirectory
from calibre.ebooks.chardet import xml_to_unicode 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.config import DynamicConfig
from calibre.utils.logging import Log from calibre.utils.logging import Log
from calibre import guess_type, prints from calibre import guess_type, prints
@ -294,12 +294,8 @@ class EbookIterator(object):
zf = open(self.pathtoebook, 'r+b') zf = open(self.pathtoebook, 'r+b')
except IOError: except IOError:
return return
zipf = ZipFile(zf, mode='a') safe_replace(zf, 'META-INF/calibre_bookmarks.txt', StringIO(dat),
for name in zipf.namelist(): add_missing=True)
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)
else: else:
self.config['bookmarks_'+self.pathtoebook] = dat self.config['bookmarks_'+self.pathtoebook] = dat

View File

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

View File

@ -603,18 +603,6 @@ class Application(QApplication):
self._file_open_paths = [] self._file_open_paths = []
self._file_open_lock = RLock() 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): def _send_file_open_events(self):
with self._file_open_lock: with self._file_open_lock:
if self._file_open_paths: if self._file_open_paths:

View File

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

View File

@ -28,12 +28,12 @@ class DBUSNotifier(Notifier):
ICON = I('notify.png') ICON = I('notify.png')
def __init__(self, server, path): def __init__(self, server, path, interface):
self.ok, self.err = True, None self.ok, self.err = True, None
try: try:
import dbus import dbus
self.dbus = 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: except Exception, err:
self.ok = False self.ok = False
self.err = str(err) self.err = str(err)
@ -43,7 +43,7 @@ class KDENotifier(DBUSNotifier):
def __init__(self): def __init__(self):
DBUSNotifier.__init__(self, 'org.kde.VisualNotifications', DBUSNotifier.__init__(self, 'org.kde.VisualNotifications',
'/VisualNotifications') '/VisualNotifications', 'org.kde.VisualNotifications')
def __call__(self, body, summary=None, replaces_id=None, timeout=0): def __call__(self, body, summary=None, replaces_id=None, timeout=0):
if replaces_id is None: if replaces_id is None:
@ -62,7 +62,7 @@ class FDONotifier(DBUSNotifier):
def __init__(self): def __init__(self):
DBUSNotifier.__init__(self, 'org.freedesktop.Notifications', 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): def __call__(self, body, summary=None, replaces_id=None, timeout=0):
if replaces_id is None: 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) 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.' '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)) 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) os.makedirs(path)
return path return path
@ -443,6 +443,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
self.add_format(id, format, stream, index_is_id=True, self.add_format(id, format, stream, index_is_id=True,
path=tpath, notify=False) path=tpath, notify=False)
self.conn.execute('UPDATE books SET path=? WHERE id=?', (path, id)) 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) self.data.set(id, self.FIELD_MAP['path'], path, row_is_id=True)
# Delete not needed directories # Delete not needed directories
if current_path and os.path.exists(spath): if current_path and os.path.exists(spath):
@ -451,6 +452,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
parent = os.path.dirname(spath) parent = os.path.dirname(spath)
if len(os.listdir(parent)) == 0: if len(os.listdir(parent)) == 0:
self.rmtree(parent, permanent=True) self.rmtree(parent, permanent=True)
curpath = self.library_path curpath = self.library_path
c1, c2 = current_path.split('/'), path.split('/') c1, c2 = current_path.split('/'), path.split('/')
if not self.is_case_sensitive and len(c1) == len(c2): 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. # the directories, so no need to do them here.
for oldseg, newseg in zip(c1, c2): for oldseg, newseg in zip(c1, c2):
if oldseg.lower() == newseg.lower() and oldseg != newseg: if oldseg.lower() == newseg.lower() and oldseg != newseg:
while True: try:
# need a temp name in the current segment for renames os.rename(os.path.join(curpath, oldseg), os.path.join(curpath, newseg))
tempname = os.path.join(curpath, 'TEMP.%f'%time.time()) except:
if not os.path.exists(tempname): break # Fail silently since nothing catastrophic has happened
break
os.rename(os.path.join(curpath, oldseg), tempname)
os.rename(tempname, os.path.join(curpath, newseg))
curpath = os.path.join(curpath, newseg) curpath = os.path.join(curpath, newseg)
def add_listener(self, listener): def add_listener(self, listener):
@ -599,7 +598,8 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
def has_cover(self, index, index_is_id=False): def has_cover(self, index, index_is_id=False):
id = index if index_is_id else self.id(index) id = index if index_is_id else self.id(index)
try: 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: except:
# Can happen if path has not yet been set # Can happen if path has not yet been set
return False return False
@ -721,7 +721,13 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
path = self.format_abspath(index, format, index_is_id=index_is_id) path = self.format_abspath(index, format, index_is_id=index_is_id)
if path is not None: if path is not None:
f = open(path, mode) f = open(path, mode)
try:
ret = f if as_file else f.read() 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: if not as_file:
f.close() f.close()
return ret return ret

View File

@ -123,8 +123,6 @@ class ContentServer(object):
return self.static('index.html') return self.static('index.html')
# Actually get content from the database {{{ # Actually get content from the database {{{
def get_cover(self, id, thumbnail=False): def get_cover(self, id, thumbnail=False):
cover = self.db.cover(id, index_is_id=True, as_file=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.constants import __appname__
from calibre import human_readable from calibre import human_readable
from calibre.utils.date import utcfromtimestamp, format_date 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 def CLASS(*args, **kwargs): # class is a reserved word in Python
kwargs['class'] = ' '.join(args) kwargs['class'] = ' '.join(args)
@ -110,11 +111,13 @@ def build_index(books, num, search, sort, order, start, total, url_base, CKEYS):
data = TD() data = TD()
last = None last = None
for fmt in book['formats'].split(','): for fmt in book['formats'].split(','):
a = ascii_filename(book['authors'])
t = ascii_filename(book['title'])
s = SPAN( s = SPAN(
A( A(
fmt.lower(), fmt.lower(),
href='/get/%s/%s-%s_%d.%s' % (fmt, book['authors'], href='/get/%s/%s-%s_%d.%s' % (fmt, a, t,
book['title'], book['id'], fmt) book['id'], fmt)
), ),
CLASS('button')) CLASS('button'))
s.tail = u'\u202f' # &nbsp; 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.constants import preferred_encoding
from calibre import isbytestring from calibre import isbytestring
from calibre.utils.date import format_date from calibre.utils.date import format_date
from calibre.utils.filenames import ascii_filename
E = ElementMaker() E = ElementMaker()
@ -88,6 +89,8 @@ class XMLServer(object):
y = format_tag_string(y, ',') y = format_tag_string(y, ',')
kwargs[x] = serialize(y) if y else '' kwargs[x] = serialize(y) if y else ''
kwargs['safe_title'] = ascii_filename(kwargs['title'])
c = kwargs.pop('comments') c = kwargs.pop('comments')
CFM = self.db.field_metadata 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.utils.ipc.worker import PARALLEL_FUNCS
from calibre import detect_ncpus as cpu_count from calibre import detect_ncpus as cpu_count
from calibre.constants import iswindows from calibre.constants import iswindows
from calibre.ptempfile import base_dir
_counter = 0 _counter = 0
@ -114,8 +115,9 @@ class Server(Thread):
with self._worker_launch_lock: with self._worker_launch_lock:
self.launched_worker_count += 1 self.launched_worker_count += 1
id = self.launched_worker_count id = self.launched_worker_count
rfile = os.path.join(tempfile.gettempdir(), fd, rfile = tempfile.mkstemp(prefix='ipc_result_%d_%d_'%(self.id, id),
'calibre_ipc_result_%d_%d.pickle'%(self.id, id)) dir=base_dir(), suffix='.pickle')
os.close(fd)
if redirect_output is None: if redirect_output is None:
redirect_output = not gui redirect_output = not gui
@ -189,8 +191,11 @@ class Server(Thread):
job.failed = True job.failed = True
job.returncode = worker.returncode job.returncode = worker.returncode
elif os.path.exists(worker.rfile): elif os.path.exists(worker.rfile):
try:
job.result = cPickle.load(open(worker.rfile, 'rb')) job.result = cPickle.load(open(worker.rfile, 'rb'))
os.remove(worker.rfile) os.remove(worker.rfile)
except:
pass
job.duration = time.time() - job.start_time job.duration = time.time() - job.start_time
self.changed_jobs_queue.put(job) self.changed_jobs_queue.put(job)

View File

@ -281,7 +281,7 @@ class ZipInfo (object):
'file_offset', '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 self.orig_filename = filename # Original file name in archive
# Terminate the file name at the first null byte. Null bytes in file # Terminate the file name at the first null byte. Null bytes in file
@ -1362,30 +1362,42 @@ class ZipFile:
self.fp.close() self.fp.close()
self.fp = None 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 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`
sometimes created corrupted zip files. sometimes created corrupted zip files.
: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 :param extra_replacements: Extra replacements. Mapping of name to file-like
objects 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') z = ZipFile(zipstream, 'r')
replacements = {name:datastream} replacements = {name:datastream}
replacements.update(extra_replacements) replacements.update(extra_replacements)
names = frozenset(replacements.keys()) names = frozenset(replacements.keys())
found = set([])
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 isinstance(obj.filename, unicode):
obj.flag_bits |= 0x16 # Set isUTF-8 bit
if obj.filename in names: if obj.filename in names:
ztemp.writestr(obj, replacements[obj.filename].read()) ztemp.writestr(obj, replacements[obj.filename].read())
found.add(obj.filename)
else: else:
ztemp.writestr(obj, z.read_raw(obj), raw_bytes=True) 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() ztemp.close()
z.close() z.close()
temp.seek(0) temp.seek(0)