Sync with trunk.

This commit is contained in:
John Schember 2012-11-18 13:10:28 -05:00
commit b0b1b95860
190 changed files with 35456 additions and 27284 deletions

View File

@ -35,3 +35,49 @@ nbproject/
.settings/
*.DS_Store
calibre_plugins/
recipes/.git
recipes/.gitignore
recipes/README
recipes/katalog_egazeciarz.recipe
recipes/tv_axnscifi.recipe
recipes/tv_comedycentral.recipe
recipes/tv_discoveryscience.recipe
recipes/tv_foxlife.recipe
recipes/tv_fox.recipe
recipes/tv_hbo.recipe
recipes/tv_kinopolska.recipe
recipes/tv_nationalgeographic.recipe
recipes/tv_polsat2.recipe
recipes/tv_polsat.recipe
recipes/tv_tv4.recipe
recipes/tv_tvn7.recipe
recipes/tv_tvn.recipe
recipes/tv_tvp1.recipe
recipes/tv_tvp2.recipe
recipes/tv_tvphd.recipe
recipes/tv_tvphistoria.recipe
recipes/tv_tvpkultura.recipe
recipes/tv_tvppolonia.recipe
recipes/tv_tvpuls.recipe
recipes/tv_viasathistory.recipe
recipes/icons/tv_axnscifi.png
recipes/icons/tv_comedycentral.png
recipes/icons/tv_discoveryscience.png
recipes/icons/tv_foxlife.png
recipes/icons/tv_fox.png
recipes/icons/tv_hbo.png
recipes/icons/tv_kinopolska.png
recipes/icons/tv_nationalgeographic.png
recipes/icons/tv_polsat2.png
recipes/icons/tv_polsat.png
recipes/icons/tv_tv4.png
recipes/icons/tv_tvn7.png
recipes/icons/tv_tvn.png
recipes/icons/tv_tvp1.png
recipes/icons/tv_tvp2.png
recipes/icons/tv_tvphd.png
recipes/icons/tv_tvphistoria.png
recipes/icons/tv_tvpkultura.png
recipes/icons/tv_tvppolonia.png
recipes/icons/tv_tvpuls.png
recipes/icons/tv_viasathistory.png

View File

@ -19,6 +19,61 @@
# new recipes:
# - title:
- version: 0.9.6
date: 2012-11-10
new features:
- title: "Experimental support for subsetting fonts"
description: "Subsetting a font means reducing the font to contain only the glyphs for the text actually present in the book. This can easily halve the size of the font. calibre can now do this for all embedded fonts during a conversion. Turn it on via the 'Subset all embedded fonts' option under the Look & Feel section of the conversion dialog. calibre can subset both TrueType and OpenType fonts. Note that this code is very new and likely has bugs, so please check the output if you turn on subsetting. The conversion log will have info about the subsetting operations."
type: major
- title: "EPUB Input: Try to workaround EPUBs that have missing or damaged ZIP central directories. calibre should now be able to read/convert such an EPUB file, provided it does not suffer from further corruption."
- title: "Allow using identifiers in save to disk templates."
tickets: [1074623]
- title: "calibredb: Add an option to not notify the GUI"
- title: "Catalogs: Fix long tags causing catalog generation to fail on windows. Add the ability to cross-reference authors, i.e. to relist the authors for a book with multiple authors separately."
tickets: [1074931]
- title: "Edit metadata dialog: Add a clear tags button to remove all tags with a single click"
- title: "Add search to the font family chooser dialog"
bug fixes:
- title: "Windows: Fix a long standing bug in the device eject code that for some reason only manifested in 0.9.5."
tickets: [1075782]
- title: "Get Books: Fix Amazon stores, Google Books store and libri.de"
- title: "Kobo driver: More fixes for on device book matching, and list books as being on device even if the Kobo has not yet indexed them. Also some performance improvements."
tickets: [1069617]
- title: "EPUB Output: Remove duplicate id and name attributes to eliminate pointless noise from the various epub check utilities"
- title: "Ask for confirmation before removing plugins"
- title: "Fix bulk convert queueing dialog becoming very long if any of the books have a very long title."
tickets: [1076191]
- title: "Fix deleting custom column tags like data from the Tag browser not updating the last modified timestamp for affected books"
tickets: [1075476]
- title: "When updating a previously broken plugin, do not show an error message because the previous version of the plugin cannot be loaded"
- title: "Fix regression that broke the Template Editor"
improved recipes:
- Various updated Polish recipes
- London Review of Books
- Yemen Times
new recipes:
- title: "Various Polish news sources"
author: Artur Stachecki
- version: 0.9.5
date: 2012-11-02

View File

@ -327,9 +327,8 @@ You can browse your |app| collection on your Android device is by using the
calibre content server, which makes your collection available over the net.
First perform the following steps in |app|
* Set the :guilabel:`Preferred Output Format` in |app| to EPUB (The output format can be set under :guilabel:`Preferences->Interface->Behavior`)
* Set the output profile to Tablet (this will work for phones as well), under :guilabel:`Preferences->Conversion->Common Options->Page Setup`
* Convert the books you want to read on your device to EPUB format by selecting them and clicking the Convert button.
* Set the :guilabel:`Preferred Output Format` in |app| to EPUB for normal Android devices or MOBI for Kindles (The output format can be set under :guilabel:`Preferences->Interface->Behavior`)
* Convert the books you want to read on your device to EPUB/MOBI format by selecting them and clicking the Convert button.
* Turn on the Content Server in |app|'s preferences and leave |app| running.
Now on your Android device, open the browser and browse to
@ -650,20 +649,24 @@ If it still wont launch, start a command prompt (press the windows key and R; th
Post any output you see in a help message on the `Forum <http://www.mobileread.com/forums/forumdisplay.php?f=166>`_.
|app| freezes when I click on anything?
|app| freezes/crashes occasionally?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
There are three possible things I know of, that can cause this:
* You recently connected an external monitor or TV to your computer. In this case, whenever |app| opens a new window like the edit metadata window or the conversion dialog, it appears on the second monitor where you dont notice it and so you think |app| has frozen. Disconnect your second monitor and restart calibre.
* You recently connected an external monitor or TV to your computer. In
this case, whenever |app| opens a new window like the edit metadata
window or the conversion dialog, it appears on the second monitor where
you dont notice it and so you think |app| has frozen. Disconnect your
second monitor and restart calibre.
* You are using a Wacom branded mouse. There is an incompatibility between Wacom mice and the graphics toolkit |app| uses. Try using a non-Wacom mouse.
* You are using a Wacom branded mouse. There is an incompatibility between
Wacom mice and the graphics toolkit |app| uses. Try using a non-Wacom
mouse.
* If you use RoboForm, it is known to cause |app| to crash. Add |app| to
the blacklist of programs inside RoboForm to fix this.
* Sometimes if some software has installed lots of new files in your fonts folder, |app| can crash until it finishes indexing them. Just start |app|, then leave it alone for about 20 minutes, without clicking on anything. After that you should be able to use |app| as normal.
the blacklist of programs inside RoboForm to fix this. Or uninstall
RoboForm.
|app| is not starting on OS X?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -722,8 +725,8 @@ You can switch |app| to using a backed up library folder by simply clicking the
If you want to backup the |app| configuration/plugins, you have to backup the config directory. You can find this config directory via :guilabel:`Preferences->Miscellaneous`. Note that restoring configuration directories is not officially supported, but should work in most cases. Just copy the contents of the backup directory into the current configuration directory to restore.
How do I use purchased EPUB books with |app|?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
How do I use purchased EPUB books with |app| (or what do I do with .acsm files)?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Most purchased EPUB books have `DRM <http://drmfree.calibre-ebook.com/about#drm>`_. This prevents |app| from opening them. You can still use |app| to store and transfer them to your ebook reader. First, you must authorize your reader on a windows machine with Adobe Digital Editions. Once this is done, EPUB books transferred with |app| will work fine on your reader. When you purchase an epub book from a website, you will get an ".acsm" file. This file should be opened with Adobe Digital Editions, which will then download the actual ".epub" ebook. The ebook file will be stored in the folder "My Digital Editions", from where you can add it to |app|.
I am getting a "Permission Denied" error?

48
recipes/antyweb.recipe Normal file
View File

@ -0,0 +1,48 @@
from calibre.web.feeds.news import BasicNewsRecipe
class AntywebRecipe(BasicNewsRecipe):
encoding = 'utf-8'
__license__ = 'GPL v3'
__author__ = u'Artur Stachecki <artur.stachecki@gmail.com>'
language = 'pl'
version = 1
title = u'Antyweb'
category = u'News'
description = u'Blog o internecie i nowych technologiach'
cover_url=''
remove_empty_feeds= True
auto_cleanup = False
no_stylesheets=True
use_embedded_content = False
oldest_article = 1
max_articles_per_feed = 100
remove_javascript = True
simultaneous_downloads = 3
keep_only_tags =[]
keep_only_tags.append(dict(name = 'h1', attrs = { 'class' : 'mm-article-title'}))
keep_only_tags.append(dict(name = 'div', attrs = {'class' : 'mm-article-content'}))
remove_tags =[]
remove_tags.append(dict(name = 'h2', attrs = {'class' : 'widgettitle'}))
remove_tags.append(dict(name = 'img', attrs = {'class' : 'alignleft'}))
remove_tags.append(dict(name = 'div', attrs = {'class' : 'float: right;margin-left:1em;margin-bottom: 0.5em;padding-bottom: 3px; width: 72px;'}))
remove_tags.append(dict(name = 'img', attrs = {'src' : 'http://antyweb.pl/wp-content/uploads/2011/09/HOSTERSI_testy_pasek600x30.gif'}))
remove_tags.append(dict(name = 'div', attrs = {'class' : 'podwpisowe'}))
extra_css = '''
body {font-family: verdana, arial, helvetica, geneva, sans-serif ;}
'''
feeds = [
(u'Artykuly', u'feed://feeds.feedburner.com/Antyweb?format=xml'),
]
def preprocess_html(self, soup):
for alink in soup.findAll('a'):
if alink.string is not None:
tstr = alink.string
alink.replaceWith(tstr)
return soup

30
recipes/autosport.recipe Normal file
View File

@ -0,0 +1,30 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__author__ = 'MrStefan <mrstefaan@gmail.com>'
'''
www.autosport.com
'''
from calibre.web.feeds.news import BasicNewsRecipe
class autosport(BasicNewsRecipe):
title = u'Autosport'
__author__ = 'MrStefan <mrstefaan@gmail.com>'
language = 'en_GB'
description =u'Daily Formula 1 and motorsport news from the leading weekly motor racing magazine. The authority on Formula 1, F1, MotoGP, GP2, Champ Car, Le Mans...'
masthead_url='http://cdn.images.autosport.com/asdotcom.gif'
remove_empty_feeds= True
oldest_article = 1
max_articles_per_feed = 100
remove_javascript=True
no_stylesheets=True
keep_only_tags =[]
keep_only_tags.append(dict(name = 'h1', attrs = {'class' : 'news_headline'}))
keep_only_tags.append(dict(name = 'td', attrs = {'class' : 'news_article_author'}))
keep_only_tags.append(dict(name = 'td', attrs = {'class' : 'news_article_date'}))
keep_only_tags.append(dict(name = 'p'))
feeds = [(u'ALL NEWS', u'http://www.autosport.com/rss/allnews.xml')]

50
recipes/bankier_pl.recipe Normal file
View File

@ -0,0 +1,50 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__author__ = 'teepel <teepel44@gmail.com>'
'''
bankier.pl
'''
from calibre.web.feeds.news import BasicNewsRecipe
class bankier(BasicNewsRecipe):
title = u'Bankier.pl'
__author__ = 'teepel <teepel44@gmail.com>'
language = 'pl'
description ='Polski portal finansowy. Informacje o: gospodarka, inwestowanie, finanse osobiste, prowadzenie firmy, kursy walut, notowania akcji, fundusze.'
masthead_url='http://www.bankier.pl/gfx/hd-mid-02.gif'
INDEX='http://bankier.pl/'
remove_empty_feeds= True
oldest_article = 1
max_articles_per_feed = 100
remove_javascript=True
no_stylesheets=True
simultaneous_downloads = 5
keep_only_tags =[]
keep_only_tags.append(dict(name = 'div', attrs = {'align' : 'left'}))
remove_tags =[]
remove_tags.append(dict(name = 'table', attrs = {'cellspacing' : '2'}))
remove_tags.append(dict(name = 'div', attrs = {'align' : 'center'}))
remove_tags.append(dict(name = 'img', attrs = {'src' : '/gfx/hd-mid-02.gif'}))
#remove_tags.append(dict(name = 'a', attrs = {'target' : '_blank'}))
#remove_tags.append(dict(name = 'br', attrs = {'clear' : 'all'}))
feeds = [
(u'Wiadomości dnia', u'http://feeds.feedburner.com/bankier-wiadomosci-dnia'),
(u'Finanse osobiste', u'http://feeds.feedburner.com/bankier-finanse-osobiste'),
(u'Firma', u'http://feeds.feedburner.com/bankier-firma'),
(u'Giełda', u'http://feeds.feedburner.com/bankier-gielda'),
(u'Rynek walutowy', u'http://feeds.feedburner.com/bankier-rynek-walutowy'),
(u'Komunikaty ze spółek', u'http://feeds.feedburner.com/bankier-espi'),
]
def print_version(self, url):
segment = url.split('.')
urlPart = segment[2]
segments = urlPart.split('-')
urlPart2 = segments[-1]
return 'http://www.bankier.pl/wiadomosci/print.html?article_id=' + urlPart2

28
recipes/blognexto.recipe Normal file
View File

@ -0,0 +1,28 @@
from calibre.web.feeds.news import BasicNewsRecipe
class blognexto(BasicNewsRecipe):
title = 'BLOG.NEXTO.pl'
__author__ = 'MrStefan <mrstefaan@gmail.com>'
language = 'pl'
description ='o e-publikacjach prawie wszystko'
masthead_url='http://blog.nexto.pl/wp-content/uploads/2012/04/logo-blog-nexto.pl_.jpg'
remove_empty_feeds= True
oldest_article = 7
max_articles_per_feed = 100
remove_javascript=True
no_stylesheets=True
keep_only_tags =[]
keep_only_tags.append(dict(name = 'div', attrs = {'id' : 'content'}))
remove_tags =[]
remove_tags.append(dict(name = 'div', attrs = {'class' : 'comment-cloud'}))
remove_tags.append(dict(name = 'p', attrs = {'class' : 'post-date1'}))
remove_tags.append(dict(name = 'div', attrs = {'class' : 'fb-like'}))
remove_tags.append(dict(name = 'div', attrs = {'class' : 'tags'}))
remove_tags.append(dict(name = 'div', attrs = {'class' : 'postnavi'}))
remove_tags.append(dict(name = 'div', attrs = {'class' : 'commments-box'}))
remove_tags.append(dict(name = 'div', attrs = {'id' : 'respond'}))
feeds = [('Artykuly', 'http://feeds.feedburner.com/blognexto')]

140
recipes/brewiarz.recipe Normal file
View File

@ -0,0 +1,140 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
from calibre.web.feeds.news import BasicNewsRecipe
import datetime, re
class brewiarz(BasicNewsRecipe):
title = u'Brewiarz'
__author__ = 'Artur Stachecki <artur.stachecki@gmail.com>'
language = 'pl'
description = u'Serwis poświęcony Liturgii Godzin (brewiarzowi) - formie codziennej modlitwy Kościoła katolickiego.'
masthead_url = 'http://brewiarz.pl/images/logo2.gif'
max_articles_per_feed = 100
remove_javascript = True
no_stylesheets = True
publication_type = 'newspaper'
next_days = 1
def parse_index(self):
dec2rom_dict = {"01": "i", "02": "ii", "03": "iii", "04": "iv",
"05": "v", "06": "vi", "07": "vii", "08": "viii",
"09": "ix", "10": "x", "11": "xi", "12": "xii"}
weekday_dict = {"Sunday": "Niedziela", "Monday": "Poniedziałek", "Tuesday": "Wtorek",
"Wednesday": "Środa", "Thursday": "Czwartek", "Friday": "Piątek", "Saturday": "Sobota"}
now = datetime.datetime.now()
feeds = []
for i in range(0, self.next_days):
url_date = now + datetime.timedelta(days=i)
url_date_month = url_date.strftime("%m")
url_date_month_roman = dec2rom_dict[url_date_month]
url_date_day = url_date.strftime("%d")
url_date_year = url_date.strftime("%Y")[2:]
url_date_weekday = url_date.strftime("%A")
url_date_weekday_pl = weekday_dict[url_date_weekday]
url = "http://brewiarz.pl/" + url_date_month_roman + "_" + url_date_year + "/" + url_date_day + url_date_month + "/index.php3"
articles = self.parse_pages(url)
if articles:
title = url_date_weekday_pl + " " + url_date_day + "." + url_date_month + "." + url_date_year
feeds.append((title, articles))
else:
sectors = self.get_sectors(url)
for subpage in sectors:
title = url_date_weekday_pl + " " + url_date_day + "." + url_date_month + "." + url_date_year + " - " + subpage.string
url = "http://brewiarz.pl/" + url_date_month_roman + "_" + url_date_year + "/" + url_date_day + url_date_month + "/" + subpage['href']
print(url)
articles = self.parse_pages(url)
if articles:
feeds.append((title, articles))
return feeds
def get_sectors(self, url):
sectors = []
soup = self.index_to_soup(url)
sectors_table = soup.find(name='table', attrs={'width': '490'})
sector_links = sectors_table.findAll(name='a')
for sector_links_modified in sector_links:
link_parent_text = sector_links_modified.findParent(name='div').text
if link_parent_text:
sector_links_modified.text = link_parent_text.text
sectors.append(sector_links_modified)
return sectors
def parse_pages(self, url):
current_articles = []
soup = self.index_to_soup(url)
www = soup.find(attrs={'class': 'www'})
if www:
box_title = www.find(text='Teksty LG')
article_box_parent = box_title.findParent('ul')
article_box_sibling = article_box_parent.findNextSibling('ul')
for li in article_box_sibling.findAll('li'):
link = li.find(name='a')
ol = link.findNextSibling(name='ol')
if ol:
sublinks = ol.findAll(name='a')
for sublink in sublinks:
link_title = self.tag_to_string(link) + " - " + self.tag_to_string(sublink)
link_url_print = re.sub('php3', 'php3?kr=_druk&wr=lg&', sublink['href'])
link_url = url[:-10] + link_url_print
current_articles.append({'title': link_title,
'url': link_url, 'description': '', 'date': ''})
else:
if link.findParent(name = 'ol'):
continue
else:
link_title = self.tag_to_string(link)
link_url_print = re.sub('php3', 'php3?kr=_druk&wr=lg&', link['href'])
link_url = url[:-10] + link_url_print
current_articles.append({'title': link_title,
'url': link_url, 'description': '', 'date': ''})
return current_articles
else:
return None
def preprocess_html(self, soup):
footer = soup.find(name='a', attrs={'href': 'http://brewiarz.pl'})
footer_parent = footer.findParent('div')
footer_parent.extract()
header = soup.find(text='http://brewiarz.pl')
header_parent = header.findParent('div')
header_parent.extract()
subheader = soup.find(text='Kolor szat:').findParent('div')
subheader.extract()
color = soup.find('b')
color.extract()
cleaned = self.strip_tags(soup)
div = cleaned.findAll(name='div')
div[1].extract()
div[2].extract()
div[3].extract()
return cleaned
def strip_tags(self, soup_dirty):
VALID_TAGS = ['p', 'div', 'br', 'b', 'a', 'title', 'head', 'html', 'body']
for tag in soup_dirty.findAll(True):
if tag.name not in VALID_TAGS:
for i, x in enumerate(tag.parent.contents):
if x == tag:
break
else:
print "Can't find", tag, "in", tag.parent
continue
for r in reversed(tag.contents):
tag.parent.insert(i, r)
tag.extract()
return soup_dirty

45
recipes/buchreport.recipe Normal file
View File

@ -0,0 +1,45 @@
from calibre.web.feeds.recipes import BasicNewsRecipe
'''Calibre recipe to convert the RSS feeds of the Buchreport to an ebook.'''
class Buchreport(BasicNewsRecipe) :
__author__ = 'a.peter'
__copyright__ = 'a.peter'
__license__ = 'GPL v3'
description = 'Buchreport'
version = 4
title = u'Buchreport'
timefmt = ' [%d.%m.%Y]'
encoding = 'cp1252'
language = 'de_DE'
extra_css = 'body { margin-left: 0.00em; margin-right: 0.00em; } \
article, articledate, articledescription { text-align: left; } \
h1 { text-align: left; font-size: 140%; font-weight: bold; } \
h2 { text-align: left; font-size: 100%; font-weight: bold; font-style: italic; } \
h3 { text-align: left; font-size: 100%; font-weight: regular; font-style: italic; } \
h4, h5, h6 { text-align: left; font-size: 100%; font-weight: bold; }'
oldest_article = 7.0
no_stylesheets = True
remove_javascript = True
use_embedded_content = False
publication_type = 'newspaper'
remove_tags_before = dict(name='h2')
remove_tags_after = [
dict(name='div', attrs={'style':["padding-top:10px;clear:both"]})
]
remove_tags = [
dict(name='div', attrs={'style':["padding-top:10px;clear:both"]}),
dict(name='iframe'),
dict(name='img')
]
feeds = [
(u'Buchreport', u'http://www.buchreport.de/index.php?id=5&type=100')
]
def get_masthead_url(self):
return 'http://www.buchreport.de/fileadmin/template/img/buchreport_logo.jpg'

View File

@ -1,5 +1,5 @@
__license__ = 'GPL v3'
__copyright__ = '2009-2010, Darko Miletic <darko.miletic at gmail.com>'
__copyright__ = '2009-2012, Darko Miletic <darko.miletic at gmail.com>'
'''
www.business-standard.com
'''
@ -14,10 +14,12 @@ class BusinessStandard(BasicNewsRecipe):
max_articles_per_feed = 100
no_stylesheets = True
use_embedded_content = False
auto_cleanup = False
encoding = 'cp1252'
publisher = 'Business Standard Limited'
category = 'news, business, money, india, world'
language = 'en_IN'
masthead_url = 'http://feeds.business-standard.com/images/logo_08.jpg'
conversion_options = {
'comments' : description
@ -26,7 +28,7 @@ class BusinessStandard(BasicNewsRecipe):
,'publisher' : publisher
,'linearize_tables': True
}
keep_only_tags=[dict(attrs={'class':'TableClas'})]
#keep_only_tags=[dict(name='td', attrs={'class':'TableClas'})]
remove_tags = [
dict(name=['object','link','script','iframe','base','meta'])
,dict(attrs={'class':'rightDiv2'})
@ -45,3 +47,8 @@ class BusinessStandard(BasicNewsRecipe):
,(u'Management & Mktg' , u'http://feeds.business-standard.com/rss/7_0.xml' )
,(u'Opinion' , u'http://feeds.business-standard.com/rss/5_0.xml' )
]
def print_version(self, url):
l, s, tp = url.rpartition('/')
t, k, autono = l.rpartition('/')
return 'http://www.business-standard.com/india/printpage.php?autono=' + autono + '&tp=' + tp

View File

@ -6,7 +6,6 @@ class Dobreprogramy_pl(BasicNewsRecipe):
__author__ = 'fenuks'
__licence__ ='GPL v3'
category = 'IT'
language = 'pl'
masthead_url='http://static.dpcdn.pl/css/Black/Images/header_logo_napis_fullVersion.png'
cover_url = 'http://userlogos.org/files/logos/Karmody/dobreprogramy_01.png'
description = u'Aktualności i blogi z dobreprogramy.pl'
@ -29,4 +28,4 @@ class Dobreprogramy_pl(BasicNewsRecipe):
for a in soup('a'):
if a.has_key('href') and 'http://' not in a['href'] and 'https://' not in a['href']:
a['href']=self.index + a['href']
return soup
return soup

35
recipes/f1_ultra.recipe Normal file
View File

@ -0,0 +1,35 @@
from calibre.web.feeds.news import BasicNewsRecipe
import re
class f1ultra(BasicNewsRecipe):
title = u'Formuła 1 - F1 ultra'
__license__ = 'GPL v3'
__author__ = 'MrStefan <mrstefaan@gmail.com>, Artur Stachecki <artur.stachecki@gmail.com>'
language = 'pl'
description =u'Formuła 1, Robert Kubica, F3, GP2 oraz inne serie wyścigowe.'
masthead_url='http://www.f1ultra.pl/templates/f1ultra/images/logo.gif'
remove_empty_feeds= True
oldest_article = 1
max_articles_per_feed = 100
remove_javascript=True
no_stylesheets=True
keep_only_tags =[(dict(name = 'div', attrs = {'id' : 'main'}))]
remove_tags_after =[dict(attrs = {'style' : 'margin-top:5px;margin-bottom:5px;display: inline;'})]
remove_tags =[(dict(attrs = {'class' : ['buttonheading', 'avPlayerContainer', 'createdate']}))]
remove_tags.append(dict(attrs = {'title' : ['PDF', 'Drukuj', 'Email']}))
remove_tags.append(dict(name = 'form', attrs = {'method' : 'post'}))
remove_tags.append(dict(name = 'hr', attrs = {'size' : '2'}))
preprocess_regexps = [(re.compile(r'align="left"'), lambda match: ''),
(re.compile(r'align="right"'), lambda match: ''),
(re.compile(r'width=\"*\"'), lambda match: ''),
(re.compile(r'\<table .*?\>'), lambda match: '')]
extra_css = '''.contentheading { font-size: 1.4em; font-weight: bold; }
img { display: block; clear: both;}
'''
remove_attributes = ['width','height','position','float','padding-left','padding-right','padding','text-align']
feeds = [(u'F1 Ultra', u'http://www.f1ultra.pl/index.php?option=com_rd_rss&id=1&Itemid=245')]

View File

@ -2,7 +2,9 @@ import re
from calibre.web.feeds.news import BasicNewsRecipe
class FocusRecipe(BasicNewsRecipe):
__license__ = 'GPL v3'
__author__ = u'intromatyk <intromatyk@gmail.com>'
language = 'pl'
@ -12,10 +14,10 @@ class FocusRecipe(BasicNewsRecipe):
publisher = u'Gruner + Jahr Polska'
category = u'News'
description = u'Newspaper'
category='magazine'
cover_url=''
remove_empty_feeds= True
no_stylesheets=True
category = 'magazine'
cover_url = ''
remove_empty_feeds = True
no_stylesheets = True
oldest_article = 7
max_articles_per_feed = 100000
recursions = 0
@ -27,15 +29,15 @@ class FocusRecipe(BasicNewsRecipe):
simultaneous_downloads = 5
r = re.compile('.*(?P<url>http:\/\/(www.focus.pl)|(rss.feedsportal.com\/c)\/.*\.html?).*')
keep_only_tags =[]
keep_only_tags.append(dict(name = 'div', attrs = {'id' : 'cll'}))
remove_tags =[]
remove_tags.append(dict(name = 'div', attrs = {'class' : 'ulm noprint'}))
remove_tags.append(dict(name = 'div', attrs = {'class' : 'txb'}))
remove_tags.append(dict(name = 'div', attrs = {'class' : 'h2'}))
remove_tags.append(dict(name = 'ul', attrs = {'class' : 'txu'}))
remove_tags.append(dict(name = 'div', attrs = {'class' : 'ulc'}))
keep_only_tags = []
keep_only_tags.append(dict(name='div', attrs={'id': 'cll'}))
remove_tags = []
remove_tags.append(dict(name='div', attrs={'class': 'ulm noprint'}))
remove_tags.append(dict(name='div', attrs={'class': 'txb'}))
remove_tags.append(dict(name='div', attrs={'class': 'h2'}))
remove_tags.append(dict(name='ul', attrs={'class': 'txu'}))
remove_tags.append(dict(name='div', attrs={'class': 'ulc'}))
extra_css = '''
body {font-family: verdana, arial, helvetica, geneva, sans-serif ;}
@ -44,18 +46,17 @@ class FocusRecipe(BasicNewsRecipe):
p.lead {font-weight: bold; text-align: left;}
.authordate {font-size: small; color: #696969;}
.fot{font-size: x-small; color: #666666;}
'''
'''
feeds = [
('Nauka', 'http://focus.pl.feedsportal.com/c/32992/f/532693/index.rss'),
('Historia', 'http://focus.pl.feedsportal.com/c/32992/f/532694/index.rss'),
('Cywilizacja', 'http://focus.pl.feedsportal.com/c/32992/f/532695/index.rss'),
('Sport', 'http://focus.pl.feedsportal.com/c/32992/f/532696/index.rss'),
('Technika', 'http://focus.pl.feedsportal.com/c/32992/f/532697/index.rss'),
('Przyroda', 'http://focus.pl.feedsportal.com/c/32992/f/532698/index.rss'),
('Technologie', 'http://focus.pl.feedsportal.com/c/32992/f/532699/index.rss'),
]
feeds = [
('Nauka', 'http://www.focus.pl/nauka/rss/'),
('Historia', 'http://www.focus.pl/historia/rss/'),
('Cywilizacja', 'http://www.focus.pl/cywilizacja/rss/'),
('Sport', 'http://www.focus.pl/sport/rss/'),
('Technika', 'http://www.focus.pl/technika/rss/'),
('Przyroda', 'http://www.focus.pl/przyroda/rss/'),
('Technologie', 'http://www.focus.pl/gadzety/rss/')
]
def skip_ad_pages(self, soup):
if ('advertisement' in soup.find('title').string.lower()):
@ -65,20 +66,20 @@ class FocusRecipe(BasicNewsRecipe):
return None
def get_cover_url(self):
soup=self.index_to_soup('http://www.focus.pl/magazyn/')
tag=soup.find(name='div', attrs={'class':'clr fl'})
soup = self.index_to_soup('http://www.focus.pl/magazyn/')
tag = soup.find(name='div', attrs={'class': 'clr fl'})
if tag:
self.cover_url='http://www.focus.pl/' + tag.a['href']
self.cover_url = 'http://www.focus.pl/' + tag.a['href']
return getattr(self, 'cover_url', self.cover_url)
def print_version(self, url):
if url.count ('focus.pl.feedsportal.com'):
if url.count('focus.pl.feedsportal.com'):
u = url.find('focus0Bpl')
u = 'http://www.focus.pl/' + url[u + 11:]
u = u.replace('0C', '/')
u = u.replace('A', '')
u = u.replace ('0E','-')
u = u.replace('0E', '-')
u = u.replace('/nc/1//story01.htm', '/do-druku/1')
else:
u = url.replace('/nc/1','/do-druku/1')
return u
else:
u = url.replace('/nc/1', '/do-druku/1')
return u

View File

@ -0,0 +1,102 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = 'teepel <teepel44@gmail.com> based on GW from fenuks'
'''
krakow.gazeta.pl
'''
from calibre.web.feeds.news import BasicNewsRecipe
class gw_krakow(BasicNewsRecipe):
title = u'Gazeta.pl Kraków'
__author__ = 'teepel <teepel44@gmail.com> based on GW from fenuks'
language = 'pl'
description =u'Wiadomości z Krakowa na portalu Gazeta.pl.'
category='newspaper'
publication_type = 'newspaper'
masthead_url='http://bi.gazeta.pl/im/5/8528/m8528105.gif'
INDEX='http://krakow.gazeta.pl/'
remove_empty_feeds= True
oldest_article = 1
max_articles_per_feed = 100
remove_javascript=True
no_stylesheets=True
keep_only_tags =[]
keep_only_tags.append(dict(name = 'div', attrs = {'id' : 'gazeta_article'}))
remove_tags =[]
remove_tags.append(dict(name = 'div', attrs = {'id' : 'gazeta_article_likes'}))
remove_tags.append(dict(name = 'div', attrs = {'id' : 'gazeta_article_tools'}))
remove_tags.append(dict(name = 'div', attrs = {'id' : 'rel'}))
remove_tags.append(dict(name = 'div', attrs = {'id' : 'gazeta_article_share'}))
remove_tags.append(dict(name = 'u1', attrs = {'id' : 'articleToolbar'}))
remove_tags.append(dict(name = 'li', attrs = {'class' : 'atComments'}))
remove_tags.append(dict(name = 'li', attrs = {'class' : 'atLicense'}))
remove_tags.append(dict(name = 'div', attrs = {'id' : 'banP4'}))
remove_tags.append(dict(name = 'div', attrs = {'id' : 'article_toolbar'}))
remove_tags.append(dict(name = 'div', attrs = {'id' : 'gazeta_article_tags'}))
remove_tags.append(dict(name = 'p', attrs = {'class' : 'txt_upl'}))
remove_tags.append(dict(name = 'div', attrs = {'class' : 'gazeta_article_related_new'}))
remove_tags.append(dict(name = 'div', attrs = {'class' : 'gazetaVideoPlayer'}))
remove_tags.append(dict(name = 'div', attrs = {'id' : 'gazeta_article_miniatures'}))
remove_tags.append(dict(name = 'div', attrs = {'id' : 'gazeta_article_buttons'}))
remove_tags_after = [dict(name = 'div', attrs = {'id' : 'gazeta_article_share'})]
feeds = [(u'Wiadomości', u'http://rss.gazeta.pl/pub/rss/krakow.xml')]
def skip_ad_pages(self, soup):
tag=soup.find(name='a', attrs={'class':'btn'})
if tag:
new_soup=self.index_to_soup(tag['href'], raw=True)
return new_soup
def append_page(self, soup, appendtag):
loop=False
tag = soup.find('div', attrs={'id':'Str'})
if appendtag.find('div', attrs={'id':'Str'}):
nexturl=tag.findAll('a')
appendtag.find('div', attrs={'id':'Str'}).extract()
loop=True
if appendtag.find(id='source'):
appendtag.find(id='source').extract()
while loop:
loop=False
for link in nexturl:
if u'następne' in link.string:
url= self.INDEX + link['href']
soup2 = self.index_to_soup(url)
pagetext = soup2.find(id='artykul')
pos = len(appendtag.contents)
appendtag.insert(pos, pagetext)
tag = soup2.find('div', attrs={'id':'Str'})
nexturl=tag.findAll('a')
loop=True
def gallery_article(self, appendtag):
tag=appendtag.find(id='container_gal')
if tag:
nexturl=appendtag.find(id='gal_btn_next').a['href']
appendtag.find(id='gal_navi').extract()
while nexturl:
soup2=self.index_to_soup(nexturl)
pagetext=soup2.find(id='container_gal')
nexturl=pagetext.find(id='gal_btn_next')
if nexturl:
nexturl=nexturl.a['href']
pos = len(appendtag.contents)
appendtag.insert(pos, pagetext)
rem=appendtag.find(id='gal_navi')
if rem:
rem.extract()
def preprocess_html(self, soup):
self.append_page(soup, soup.body)
if soup.find(id='container_gal'):
self.gallery_article(soup.body)
return soup

View File

@ -0,0 +1,99 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__author__ = 'teepel <teepel44@gmail.com> based on GW from fenuks'
'''
warszawa.gazeta.pl
'''
from calibre.web.feeds.news import BasicNewsRecipe
class gw_wawa(BasicNewsRecipe):
title = u'Gazeta.pl Warszawa'
__author__ = 'teepel <teepel44@gmail.com> based on GW from fenuks'
language = 'pl'
description ='Wiadomości z Warszawy na portalu Gazeta.pl.'
category='newspaper'
publication_type = 'newspaper'
masthead_url='http://bi.gazeta.pl/im/3/4089/m4089863.gif'
INDEX='http://warszawa.gazeta.pl/'
remove_empty_feeds= True
oldest_article = 1
max_articles_per_feed = 100
remove_javascript=True
no_stylesheets=True
keep_only_tags =[]
keep_only_tags.append(dict(name = 'div', attrs = {'id' : 'gazeta_article'}))
remove_tags =[]
remove_tags.append(dict(name = 'div', attrs = {'id' : 'gazeta_article_likes'}))
remove_tags.append(dict(name = 'div', attrs = {'id' : 'gazeta_article_tools'}))
remove_tags.append(dict(name = 'div', attrs = {'id' : 'rel'}))
remove_tags.append(dict(name = 'div', attrs = {'id' : 'gazeta_article_share'}))
remove_tags.append(dict(name = 'u1', attrs = {'id' : 'articleToolbar'}))
remove_tags.append(dict(name = 'li', attrs = {'class' : 'atComments'}))
remove_tags.append(dict(name = 'li', attrs = {'class' : 'atLicense'}))
remove_tags.append(dict(name = 'div', attrs = {'id' : 'banP4'}))
remove_tags.append(dict(name = 'div', attrs = {'id' : 'article_toolbar'}))
remove_tags.append(dict(name = 'div', attrs = {'id' : 'gazeta_article_tags'}))
remove_tags.append(dict(name = 'p', attrs = {'class' : 'txt_upl'}))
remove_tags.append(dict(name = 'div', attrs = {'class' : 'gazeta_article_related_new'}))
remove_tags.append(dict(name = 'div', attrs = {'class' : 'gazetaVideoPlayer'}))
remove_tags.append(dict(name = 'div', attrs = {'id' : 'gazeta_article_miniatures'}))
feeds = [(u'Wiadomości', u'http://rss.gazeta.pl/pub/rss/warszawa.xml')]
def skip_ad_pages(self, soup):
tag=soup.find(name='a', attrs={'class':'btn'})
if tag:
new_soup=self.index_to_soup(tag['href'], raw=True)
return new_soup
def append_page(self, soup, appendtag):
loop=False
tag = soup.find('div', attrs={'id':'Str'})
if appendtag.find('div', attrs={'id':'Str'}):
nexturl=tag.findAll('a')
appendtag.find('div', attrs={'id':'Str'}).extract()
loop=True
if appendtag.find(id='source'):
appendtag.find(id='source').extract()
while loop:
loop=False
for link in nexturl:
if u'następne' in link.string:
url= self.INDEX + link['href']
soup2 = self.index_to_soup(url)
pagetext = soup2.find(id='artykul')
pos = len(appendtag.contents)
appendtag.insert(pos, pagetext)
tag = soup2.find('div', attrs={'id':'Str'})
nexturl=tag.findAll('a')
loop=True
def gallery_article(self, appendtag):
tag=appendtag.find(id='container_gal')
if tag:
nexturl=appendtag.find(id='gal_btn_next').a['href']
appendtag.find(id='gal_navi').extract()
while nexturl:
soup2=self.index_to_soup(nexturl)
pagetext=soup2.find(id='container_gal')
nexturl=pagetext.find(id='gal_btn_next')
if nexturl:
nexturl=nexturl.a['href']
pos = len(appendtag.contents)
appendtag.insert(pos, pagetext)
rem=appendtag.find(id='gal_navi')
if rem:
rem.extract()
def preprocess_html(self, soup):
self.append_page(soup, soup.body)
if soup.find(id='container_gal'):
self.gallery_article(soup.body)
return soup

View File

@ -1,104 +1,107 @@
# -*- coding: utf-8 -*-
from calibre.web.feeds.news import BasicNewsRecipe
class Gazeta_Wyborcza(BasicNewsRecipe):
title = u'Gazeta Wyborcza'
__author__ = 'fenuks'
language = 'pl'
description ='news from gazeta.pl'
category='newspaper'
title = u'Gazeta.pl'
__author__ = 'fenuks, Artur Stachecki'
language = 'pl'
description = 'news from gazeta.pl'
category = 'newspaper'
publication_type = 'newspaper'
masthead_url='http://bi.gazeta.pl/im/5/10285/z10285445AA.jpg'
INDEX='http://wyborcza.pl'
remove_empty_feeds= True
masthead_url = 'http://bi.gazeta.pl/im/5/10285/z10285445AA.jpg'
INDEX = 'http://wyborcza.pl'
remove_empty_feeds = True
oldest_article = 3
max_articles_per_feed = 100
remove_javascript=True
no_stylesheets=True
ignore_duplicate_articles = {'title', 'url'}
keep_only_tags = dict(id=['gazeta_article', 'article'])
remove_tags_after = dict(id='gazeta_article_share')
remove_tags = [dict(attrs={'class':['artReadMore', 'gazeta_article_related_new', 'txt_upl']}), dict(id=['gazeta_article_likes', 'gazeta_article_tools', 'rel', 'gazeta_article_tags', 'gazeta_article_share', 'gazeta_article_brand', 'gazeta_article_miniatures'])]
feeds = [(u'Kraj', u'http://rss.feedsportal.com/c/32739/f/530266/index.rss'), (u'\u015awiat', u'http://rss.feedsportal.com/c/32739/f/530270/index.rss'),
(u'Wyborcza.biz', u'http://wyborcza.biz/pub/rss/wyborcza_biz_wiadomosci.htm'),
(u'Komentarze', u'http://rss.feedsportal.com/c/32739/f/530312/index.rss'),
(u'Kultura', u'http://rss.gazeta.pl/pub/rss/gazetawyborcza_kultura.xml'),
(u'Nauka', u'http://rss.feedsportal.com/c/32739/f/530269/index.rss'),
(u'Opinie', u'http://rss.gazeta.pl/pub/rss/opinie.xml'),
(u'Gazeta \u015awi\u0105teczna', u'http://rss.feedsportal.com/c/32739/f/530431/index.rss'),
#(u'Du\u017cy Format', u'http://rss.feedsportal.com/c/32739/f/530265/index.rss'),
(u'Witamy w Polsce', u'http://rss.feedsportal.com/c/32739/f/530476/index.rss'),
(u'M\u0119ska Muzyka', u'http://rss.feedsportal.com/c/32739/f/530337/index.rss'),
(u'Lata Lec\u0105', u'http://rss.feedsportal.com/c/32739/f/530326/index.rss'),
(u'Solidarni z Tybetem', u'http://rss.feedsportal.com/c/32739/f/530461/index.rss'),
(u'W pon. - \u017bakowski', u'http://rss.feedsportal.com/c/32739/f/530491/index.rss'),
(u'We wt. - Kolenda-Zalewska', u'http://rss.feedsportal.com/c/32739/f/530310/index.rss'),
(u'\u015aroda w \u015brod\u0119', u'http://rss.feedsportal.com/c/32739/f/530428/index.rss'),
(u'W pi\u0105tek - Olejnik', u'http://rss.feedsportal.com/c/32739/f/530364/index.rss')
]
remove_javascript = True
no_stylesheets = True
remove_tags_before = dict(id='k0')
remove_tags_after = dict(id='banP4')
remove_tags = [dict(name='div', attrs={'class':'rel_box'}), dict(attrs={'class':['date', 'zdjP', 'zdjM', 'pollCont', 'rel_video', 'brand', 'txt_upl']}), dict(name='div', attrs={'id':'footer'})]
feeds = [(u'Kraj', u'http://rss.feedsportal.com/c/32739/f/530266/index.rss'), (u'\u015awiat', u'http://rss.feedsportal.com/c/32739/f/530270/index.rss'),
(u'Wyborcza.biz', u'http://wyborcza.biz/pub/rss/wyborcza_biz_wiadomosci.htm'),
(u'Komentarze', u'http://rss.feedsportal.com/c/32739/f/530312/index.rss'),
(u'Kultura', u'http://rss.gazeta.pl/pub/rss/gazetawyborcza_kultura.xml'),
(u'Nauka', u'http://rss.feedsportal.com/c/32739/f/530269/index.rss'), (u'Opinie', u'http://rss.gazeta.pl/pub/rss/opinie.xml'), (u'Gazeta \u015awi\u0105teczna', u'http://rss.feedsportal.com/c/32739/f/530431/index.rss'), (u'Du\u017cy Format', u'http://rss.feedsportal.com/c/32739/f/530265/index.rss'), (u'Witamy w Polsce', u'http://rss.feedsportal.com/c/32739/f/530476/index.rss'), (u'M\u0119ska Muzyka', u'http://rss.feedsportal.com/c/32739/f/530337/index.rss'), (u'Lata Lec\u0105', u'http://rss.feedsportal.com/c/32739/f/530326/index.rss'), (u'Solidarni z Tybetem', u'http://rss.feedsportal.com/c/32739/f/530461/index.rss'), (u'W pon. - \u017bakowski', u'http://rss.feedsportal.com/c/32739/f/530491/index.rss'), (u'We wt. - Kolenda-Zalewska', u'http://rss.feedsportal.com/c/32739/f/530310/index.rss'), (u'\u015aroda w \u015brod\u0119', u'http://rss.feedsportal.com/c/32739/f/530428/index.rss'), (u'W pi\u0105tek - Olejnik', u'http://rss.feedsportal.com/c/32739/f/530364/index.rss'), (u'Nekrologi', u'http://rss.feedsportal.com/c/32739/f/530358/index.rss')
]
def skip_ad_pages(self, soup):
tag=soup.find(name='a', attrs={'class':'btn'})
if tag:
new_soup=self.index_to_soup(tag['href'], raw=True)
tag = soup.find(name='a', attrs={'class': 'btn'})
if tag:
new_soup = self.index_to_soup(tag['href'], raw=True)
return new_soup
def append_page(self, soup, appendtag):
loop=False
tag = soup.find('div', attrs={'id':'Str'})
if appendtag.find('div', attrs={'id':'Str'}):
nexturl=tag.findAll('a')
appendtag.find('div', attrs={'id':'Str'}).extract()
loop=True
loop = False
tag = soup.find('div', attrs={'id': 'Str'})
if appendtag.find('div', attrs={'id': 'Str'}):
nexturl = tag.findAll('a')
appendtag.find('div', attrs={'id': 'Str'}).extract()
loop = True
if appendtag.find(id='source'):
appendtag.find(id='source').extract()
while loop:
loop=False
loop = False
for link in nexturl:
if u'następne' in link.string:
url= self.INDEX + link['href']
url = self.INDEX + link['href']
soup2 = self.index_to_soup(url)
pagetext = soup2.find(id='artykul')
pos = len(appendtag.contents)
appendtag.insert(pos, pagetext)
tag = soup2.find('div', attrs={'id':'Str'})
nexturl=tag.findAll('a')
loop=True
tag = soup2.find('div', attrs={'id': 'Str'})
nexturl = tag.findAll('a')
loop = True
def gallery_article(self, appendtag):
tag=appendtag.find(id='container_gal')
tag = appendtag.find(id='container_gal')
if tag:
nexturl=appendtag.find(id='gal_btn_next').a['href']
nexturl = appendtag.find(id='gal_btn_next').a['href']
appendtag.find(id='gal_navi').extract()
while nexturl:
soup2=self.index_to_soup(nexturl)
pagetext=soup2.find(id='container_gal')
nexturl=pagetext.find(id='gal_btn_next')
soup2 = self.index_to_soup(nexturl)
pagetext = soup2.find(id='container_gal')
nexturl = pagetext.find(id='gal_btn_next')
if nexturl:
nexturl=nexturl.a['href']
nexturl = nexturl.a['href']
pos = len(appendtag.contents)
appendtag.insert(pos, pagetext)
rem=appendtag.find(id='gal_navi')
rem = appendtag.find(id='gal_navi')
if rem:
rem.extract()
def preprocess_html(self, soup):
self.append_page(soup, soup.body)
if soup.find(id='container_gal'):
self.gallery_article(soup.body)
return soup
if soup.find(attrs={'class': 'piano_btn_1'}):
return None
else:
self.append_page(soup, soup.body)
if soup.find(id='container_gal'):
self.gallery_article(soup.body)
return soup
def print_version(self, url):
if 'http://wyborcza.biz/biznes/' not in url:
return url
if url.count('rss.feedsportal.com'):
u = url.find('wyborcza0Bpl')
u = 'http://www.wyborcza.pl/' + url[u + 11:]
u = u.replace('0C', '/')
u = u.replace('A', '')
u = u.replace('0E', '-')
u = u.replace('0H', ',')
u = u.replace('0I', '_')
u = u.replace('0B', '.')
u = u.replace('/1,', '/2029020,')
u = u.replace('/story01.htm', '')
print(u)
return u
elif 'http://wyborcza.pl/1' in url:
return url.replace('http://wyborcza.pl/1', 'http://wyborcza.pl/2029020')
else:
return url.replace('http://wyborcza.biz/biznes/1', 'http://wyborcza.biz/biznes/2029020')
return url.replace('http://wyborcza.biz/biznes/1', 'http://wyborcza.biz/biznes/2029020')
def get_cover_url(self):
soup = self.index_to_soup('http://wyborcza.pl/0,76762,3751429.html')
cover=soup.find(id='GWmini2')
soup = self.index_to_soup('http://wyborcza.pl/'+ cover.contents[3].a['href'])
self.cover_url='http://wyborcza.pl' + soup.img['src']
cover = soup.find(id='GWmini2')
soup = self.index_to_soup('http://wyborcza.pl/' + cover.contents[3].a['href'])
self.cover_url = 'http://wyborcza.pl' + soup.img['src']
return getattr(self, 'cover_url', self.cover_url)

BIN
recipes/icons/antyweb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 668 B

BIN
recipes/icons/autosport.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 B

BIN
recipes/icons/blognexto.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 699 B

BIN
recipes/icons/brewiarz.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 290 B

After

Width:  |  Height:  |  Size: 786 B

BIN
recipes/icons/f1_ultra.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 490 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 802 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 802 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 802 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 221 B

After

Width:  |  Height:  |  Size: 802 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 698 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 965 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 820 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 330 B

BIN
recipes/icons/satkurier.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
recipes/icons/wprost.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

34
recipes/kerrang.recipe Normal file
View File

@ -0,0 +1,34 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
from calibre.web.feeds.news import BasicNewsRecipe
class kerrang(BasicNewsRecipe):
title = u'Kerrang!'
__author__ = 'Artur Stachecki <artur.stachecki@gmail.com>'
language = 'en_GB'
description = u'UK-based magazine devoted to rock music published by Bauer Media Group'
oldest_article = 7
masthead_url = 'http://images.kerrang.com/design/kerrang/kerrangsite/logo.gif'
max_articles_per_feed = 100
simultaneous_downloads = 5
remove_javascript = True
no_stylesheets = True
use_embedded_content = False
recursions = 0
keep_only_tags = []
keep_only_tags.append(dict(attrs = {'class' : ['headz', 'blktxt']}))
extra_css = ''' img { display: block; margin-right: auto;}
h1 {text-align: left; font-size: 22px;}'''
feeds = [(u'News', u'http://www.kerrang.com/blog/rss.xml')]
def preprocess_html(self, soup):
for alink in soup.findAll('a'):
if alink.string is not None:
tstr = alink.string
alink.replaceWith(tstr)
return soup

45
recipes/lequipe.recipe Normal file
View File

@ -0,0 +1,45 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
from calibre.web.feeds.news import BasicNewsRecipe
class leequipe(BasicNewsRecipe):
title = u'l\'equipe'
__author__ = 'Artur Stachecki <artur.stachecki@gmail.com>'
language = 'fr'
description = u'Retrouvez tout le sport en direct sur le site de L\'EQUIPE et suivez l\'actualité du football, rugby, basket, cyclisme, f1, volley, hand, tous les résultats sportifs'
oldest_article = 1
masthead_url = 'http://static.lequipe.fr/v6/img/logo-lequipe.png'
max_articles_per_feed = 100
simultaneous_downloads = 5
remove_javascript = True
no_stylesheets = True
use_embedded_content = False
recursions = 0
keep_only_tags = []
keep_only_tags.append(dict(attrs={'id': ['article']}))
remove_tags = []
remove_tags.append(dict(attrs={'id': ['partage', 'ensavoirplus', 'bloc_bas_breve', 'commentaires', 'tools']}))
remove_tags.append(dict(attrs={'class': ['partage_bis', 'date']}))
feeds = [(u'Football', u'http://www.lequipe.fr/rss/actu_rss_Football.xml'),
(u'Auto-Moto', u'http://www.lequipe.fr/rss/actu_rss_Auto-Moto.xml'),
(u'Tennis', u'http://www.lequipe.fr/rss/actu_rss_Tennis.xml'),
(u'Golf', u'http://www.lequipe.fr/rss/actu_rss_Golf.xml'),
(u'Rugby', u'http://www.lequipe.fr/rss/actu_rss_Rugby.xml'),
(u'Basket', u'http://www.lequipe.fr/rss/actu_rss_Basket.xml'),
(u'Hand', u'http://www.lequipe.fr/rss/actu_rss_Hand.xml'),
(u'Cyclisme', u'http://www.lequipe.fr/rss/actu_rss_Cyclisme.xml'),
(u'Autres Sports', u'http://pipes.yahoo.com/pipes/pipe.run?_id=2039f7f4f350c70c5e4e8633aa1b37cd&_render=rss')
]
def preprocess_html(self, soup):
for alink in soup.findAll('a'):
if alink.string is not None:
tstr = alink.string
alink.replaceWith(tstr)
return soup

View File

@ -0,0 +1,36 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__author__ = 'teepel <teepel44@gmail.com>'
'''
http://www.mateusz.pl/czytania
'''
from calibre.web.feeds.news import BasicNewsRecipe
class czytania_mateusz(BasicNewsRecipe):
title = u'Czytania na ka\u017cdy dzie\u0144'
__author__ = 'teepel <teepel44@gmail.com>'
description = u'Codzienne czytania z jednego z najstarszych polskich serwisów katolickich.'
language = 'pl'
INDEX='http://www.mateusz.pl/czytania'
oldest_article = 1
remove_empty_feeds= True
no_stylesheets=True
auto_cleanup = True
remove_javascript = True
simultaneous_downloads = 2
max_articles_per_feed = 100
auto_cleanup = True
feeds = [(u'Czytania', u'http://mateusz.pl/rss/czytania/')]
remove_tags =[]
remove_tags.append(dict(name = 'p', attrs = {'class' : 'top'}))
#thanks t3d
def get_article_url(self, article):
link = article.get('link')
if 'kmt.pl' not in link:
return link

View File

@ -4,7 +4,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
class FocusRecipe(BasicNewsRecipe):
__license__ = 'GPL v3'
__author__ = u'intromatyk <intromatyk@gmail.com>'
__author__ = u'Artur Stachecki <artur.stachecki@gmail.com>'
language = 'pl'
version = 1

49
recipes/myapple_pl.recipe Normal file
View File

@ -0,0 +1,49 @@
from calibre.web.feeds.news import BasicNewsRecipe
class MyAppleRecipe(BasicNewsRecipe):
__license__ = 'GPL v3'
__author__ = u'Artur Stachecki <artur.stachecki@gmail.com>'
language = 'pl'
version = 1
title = u'MyApple.pl'
category = u'News'
description = u' Największy w Polsce serwis zajmujący się tematyką związaną z Apple i wszelkimi produktami tej firmy.'
cover_url=''
remove_empty_feeds= True
no_stylesheets=True
oldest_article = 7
max_articles_per_feed = 100000
recursions = 0
no_stylesheets = True
remove_javascript = True
simultaneous_downloads = 3
keep_only_tags =[]
keep_only_tags.append(dict(name = 'div', attrs = {'id' : 'article_content'}))
remove_tags =[]
remove_tags.append(dict(name = 'div', attrs = {'class' : 'article_author_date_comment_container'}))
remove_tags.append(dict(name = 'div', attrs = {'class' : 'fullwidth'}))
remove_tags.append(dict(name = 'div', attrs = {'class' : 'cmslinks'}))
remove_tags.append(dict(name = 'div', attrs = {'class' : 'googleads-468'}))
remove_tags.append(dict(name = 'div', attrs = {'id' : 'comments'}))
extra_css = '''
body {font-family: verdana, arial, helvetica, geneva, sans-serif ;}
td.contentheading{font-size: large; font-weight: bold;}
'''
feeds = [
('News', 'feed://myapple.pl/external.php?do=rss&type=newcontent&sectionid=1&days=120&count=10'),
]
def preprocess_html(self, soup):
for alink in soup.findAll('a'):
if alink.string is not None:
tstr = alink.string
alink.replaceWith(tstr)
return soup

View File

@ -0,0 +1,61 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
from calibre.web.feeds.news import BasicNewsRecipe
class naszdziennik(BasicNewsRecipe):
title = u'Nasz Dziennik'
__author__ = 'Artur Stachecki <artur.stachecki@gmail.com>'
language = 'pl'
description =u'Nasz Dziennik - Ogólnopolska gazeta codzienna. Podejmuje tematykę dotyczącą życia społecznego, kulturalnego, politycznego i religijnego. Propaguje wartości chrześcijańskie oraz tradycję i kulturę polską.'
masthead_url='http://www.naszdziennik.pl/images/logo-male.png'
max_articles_per_feed = 100
remove_javascript=True
no_stylesheets = True
keep_only_tags =[dict(attrs = {'id' : 'article'})]
#definiujemy nową funkcje; musi zwracać listę feedów wraz z artykułami
def parse_index(self):
#adres do parsowania artykułów
soup = self.index_to_soup('http://www.naszdziennik.pl/news')
#deklaracja pustej listy feedów
feeds = []
#deklaracja pustego słownika artykułów
articles = {}
#deklaracja pustej listy sekcji
sections = []
#deklaracja pierwszej sekcji jako pusty string
section = ''
#pętla for, która analizuje po kolei każdy tag "news-article"
for item in soup.findAll(attrs = {'class' : 'news-article'}) :
#w tagu "news-article szukamy pierwszego taga h4"
section = item.find('h4')
#zmiennej sekcja przypisujemy zawartość tekstową taga
section = self.tag_to_string(section)
#sprawdzamy czy w słowniku artykułów istnieje klucz dotyczący sekcji
#jeśli nie istnieje to :
if not articles.has_key(section) :
#do listy sekcji dodajemy nową sekcje
sections.append(section)
#deklarujemy nową sekcje w słowniku artykułów przypisując jej klucz odpowiadający nowej sekcji, którego wartością jest pusta lista
articles[section] = []
#przeszukujemy kolejny tag "title-datetime"
article_title_datetime = item.find(attrs = {'class' : 'title-datetime'})
#w tagu title-datetime znajdujemy pierwszy link
article_a = article_title_datetime.find('a')
#i tworzymy z niego link absolutny do właściwego artykułu
article_url = 'http://naszdziennik.pl' + article_a['href']
#jako tytuł użyty będzie tekst pomiędzy tagami <a>
article_title = self.tag_to_string(article_a)
#a data będzie tekstem z pierwszego taga h4 znalezionego w tagu title-datetime
article_date = self.tag_to_string(article_title_datetime.find('h4'))
#zebrane elementy dodajemy do listy zadeklarowanej w linijce 44
articles[section].append( { 'title' : article_title, 'url' : article_url, 'date' : article_date })
#po dodaniu wszystkich artykułów dodajemy sekcje do listy feedów, korzystając z list sekcji znajdujących się w słowniku
for section in sections:
feeds.append((section, articles[section]))
#zwracamy listę feedów, której parsowaniem zajmie się calibre
return feeds

View File

@ -22,9 +22,9 @@ class NewYorker(BasicNewsRecipe):
masthead_url = 'http://www.newyorker.com/css/i/hed/logo.gif'
extra_css = """
body {font-family: "Times New Roman",Times,serif}
.articleauthor{color: #9F9F9F;
.articleauthor{color: #9F9F9F;
font-family: Arial, sans-serif;
font-size: small;
font-size: small;
text-transform: uppercase}
.rubric,.dd,h6#credit{color: #CD0021;
font-family: Arial, sans-serif;
@ -63,11 +63,11 @@ class NewYorker(BasicNewsRecipe):
return url.strip()
def get_cover_url(self):
cover_url = None
soup = self.index_to_soup('http://www.newyorker.com/magazine/toc/')
cover_item = soup.find('img',attrs={'id':'inThisIssuePhoto'})
cover_url = "http://www.newyorker.com/images/covers/1925/1925_02_21_p233.jpg"
soup = self.index_to_soup('http://www.newyorker.com/magazine?intcid=magazine')
cover_item = soup.find('div',attrs={'id':'media-count-1'})
if cover_item:
cover_url = 'http://www.newyorker.com' + cover_item['src'].strip()
cover_url = 'http://www.newyorker.com' + cover_item.div.img['src'].strip()
return cover_url
def preprocess_html(self, soup):

View File

@ -0,0 +1,32 @@
import re
from calibre.web.feeds.news import BasicNewsRecipe
class RedVoltaireRecipe(BasicNewsRecipe):
title = u'Red Voltaire'
__author__ = 'atordo'
description = u'Red de prensa no alineada, especializada en el an\u00e1lisis de las relaciones internacionales'
oldest_article = 7
max_articles_per_feed = 30
auto_cleanup = False
no_stylesheets = True
language = 'es'
use_embedded_content = False
remove_javascript = True
cover_url = u'http://www.voltairenet.org/squelettes/elements/images/logo-voltairenet-org.png'
masthead_url = u'http://www.voltairenet.org/squelettes/elements/images/logo-voltairenet-org.png'
preprocess_regexps = [
(re.compile(r'<title>(?P<titulo>.+)</title>.+<span class="updated" title=".+"><time', re.IGNORECASE|re.DOTALL)
,lambda match:'</title></head><body><h1>'+match.group('titulo')+'</h1><time')
,(re.compile(r'<time datetime=.+pubdate>. (?P<fecha>.+)</time>.+<!------------------- COLONNE TEXTE ------------------->', re.IGNORECASE|re.DOTALL)
,lambda match:'<small>'+match.group('fecha')+'</small>')
,(re.compile(r'<aside>.+', re.IGNORECASE|re.DOTALL)
,lambda match:'</body></html>')
]
extra_css = '''
img{margin-bottom:0.4em; display:block; margin-left:auto; margin-right:auto}
'''
feeds = [u'http://www.voltairenet.org/spip.php?page=backend&id_secteur=1110&lang=es']

View File

@ -0,0 +1,28 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__author__ = 'MrStefan <mrstefaan@gmail.com>'
'''
www.rushisaband.com
'''
from calibre.web.feeds.news import BasicNewsRecipe
class rushisaband(BasicNewsRecipe):
title = u'Rushisaband'
__author__ = 'MrStefan <mrstefaan@gmail.com>'
language = 'en_GB'
description =u'A blog devoted to the band RUSH and its members, Neil Peart, Geddy Lee and Alex Lifeson'
remove_empty_feeds= True
oldest_article = 7
max_articles_per_feed = 100
remove_javascript=True
no_stylesheets=True
keep_only_tags =[]
keep_only_tags.append(dict(name = 'h4'))
keep_only_tags.append(dict(name = 'h5'))
keep_only_tags.append(dict(name = 'p'))
feeds = [(u'Rush is a Band', u'http://feeds2.feedburner.com/rushisaband/blog')]

View File

@ -0,0 +1,41 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__author__ = 'teepel <teepel44@gmail.com>'
'''
http://www.rynekinfrastruktury.pl
'''
from calibre.web.feeds.news import BasicNewsRecipe
class prawica_recipe(BasicNewsRecipe):
title = u'Rynek Infrastruktury'
__author__ = 'teepel <teepel44@gmail.com>'
language = 'pl'
description =u'Portal "Rynek Infrastruktury" to źródło informacji o kluczowych elementach polskiej gospodarki: drogach, kolei, lotniskach, portach, telekomunikacji, energetyce, prawie i polityce, wzmocnione eksperckimi komentarzami kluczowych analityków.'
remove_empty_feeds= True
oldest_article = 1
max_articles_per_feed = 100
remove_javascript=True
no_stylesheets=True
feeds = [
(u'Drogi', u'http://www.rynekinfrastruktury.pl/rss/41'),
(u'Lotniska', u'http://www.rynekinfrastruktury.pl/rss/42'),
(u'Kolej', u'http://www.rynekinfrastruktury.pl/rss/37'),
(u'Energetyka', u'http://www.rynekinfrastruktury.pl/rss/30'),
(u'Telekomunikacja', u'http://www.rynekinfrastruktury.pl/rss/31'),
(u'Porty', u'http://www.rynekinfrastruktury.pl/rss/32'),
(u'Prawo i polityka', u'http://www.rynekinfrastruktury.pl/rss/47'),
(u'Komentarze', u'http://www.rynekinfrastruktury.pl/rss/38'),
]
keep_only_tags =[]
keep_only_tags.append(dict(name = 'div', attrs = {'class' : 'articleContent'}))
remove_tags =[]
remove_tags.append(dict(name = 'span', attrs = {'class' : 'date'}))
def print_version(self, url):
return url.replace('http://www.rynekinfrastruktury.pl/artykul/', 'http://www.rynekinfrastruktury.pl/artykul/drukuj/')

View File

@ -0,0 +1,40 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__author__ = 'teepel <teepel44@gmail.com>'
'''
rynek-kolejowy.pl
'''
from calibre.web.feeds.news import BasicNewsRecipe
class rynek_kolejowy(BasicNewsRecipe):
title = u'Rynek Kolejowy'
__author__ = 'teepel <teepel44@gmail.com>'
language = 'pl'
description =u'Rynek Kolejowy - kalendarium wydarzeń branży kolejowej, konferencje, sympozja, targi kolejowe, krajowe i zagraniczne.'
masthead_url='http://p.wnp.pl/images/i/partners/rynek_kolejowy.gif'
remove_empty_feeds= True
oldest_article = 1
max_articles_per_feed = 100
remove_javascript=True
no_stylesheets=True
keep_only_tags =[]
keep_only_tags.append(dict(name = 'div', attrs = {'id' : 'mainContent'}))
remove_tags =[]
remove_tags.append(dict(name = 'div', attrs = {'class' : 'right no-print'}))
remove_tags.append(dict(name = 'div', attrs = {'id' : 'font-size'}))
remove_tags.append(dict(name = 'div', attrs = {'class' : 'no-print'}))
extra_css = '''.wiadomosc_title{ font-size: 1.4em; font-weight: bold; }'''
feeds = [(u'Wiadomości', u'http://www.rynek-kolejowy.pl/rss/rss.php')]
def print_version(self, url):
segment = url.split('/')
urlPart = segment[3]
return 'http://www.rynek-kolejowy.pl/drukuj.php?id=' + urlPart

View File

@ -34,16 +34,20 @@ class RzeczpospolitaRecipe(BasicNewsRecipe):
keep_only_tags.append(dict(name = 'div', attrs = {'id' : 'story'}))
remove_tags =[]
remove_tags.append(dict(name = 'div', attrs = {'class' : 'articleLeftBox'}))
remove_tags.append(dict(name = 'div', attrs = {'class' : 'socialNewTools'}))
remove_tags.append(dict(name = 'div', attrs = {'id' : 'socialTools'}))
remove_tags.append(dict(name = 'div', attrs = {'class' : 'articleToolBoxTop'}))
remove_tags.append(dict(name = 'div', attrs = {'class' : 'clr'}))
remove_tags.append(dict(name = 'div', attrs = {'id' : 'recommendations'}))
remove_tags.append(dict(name = 'div', attrs = {'id' : 'editorPicks'}))
remove_tags.append(dict(name = 'div', attrs = {'class' : 'editorPicks'}))
remove_tags.append(dict(name = 'div', attrs = {'class' : 'editorPicks editorPicksFirst'}))
remove_tags.append(dict(name = 'div', attrs = {'id' : 'articleCopyrightText'}))
remove_tags.append(dict(name = 'div', attrs = {'id' : 'articleCopyrightButton'}))
remove_tags.append(dict(name = 'div', attrs = {'class' : 'articleToolBoxBottom'}))
remove_tags.append(dict(name = 'div', attrs = {'class' : 'more'}))
remove_tags.append(dict(name = 'div', attrs = {'class' : 'addRecommendation'}))
remove_tags.append(dict(name = 'h3', attrs = {'id' : 'tags'}))
extra_css = '''
body {font-family: verdana, arial, helvetica, geneva, sans-serif ;}
@ -67,3 +71,4 @@ class RzeczpospolitaRecipe(BasicNewsRecipe):
return start + '/' + index + '?print=tak'

47
recipes/satkurier.recipe Normal file
View File

@ -0,0 +1,47 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
from calibre.web.feeds.news import BasicNewsRecipe
class SATKurier(BasicNewsRecipe):
title = u'SATKurier.pl'
__author__ = 'Artur Stachecki <artur.stachecki@gmail.com>'
language = 'pl'
description = u'Największy i najstarszy serwis poświęcony\
telewizji cyfrowej, przygotowywany przez wydawcę\
miesięcznika SAT Kurier. Bieżące wydarzenia\
z rynku mediów i nowych technologii.'
oldest_article = 7
masthead_url = 'http://satkurier.pl/img/header_sk_logo.gif'
max_articles_per_feed = 100
simultaneous_downloads = 5
remove_javascript = True
no_stylesheets = True
keep_only_tags = []
keep_only_tags.append(dict(name='div', attrs={'id': ['single_news', 'content']}))
remove_tags = []
remove_tags.append(dict(attrs={'id': ['news_info', 'comments']}))
remove_tags.append(dict(attrs={'href': '#czytaj'}))
remove_tags.append(dict(attrs={'align': 'center'}))
remove_tags.append(dict(attrs={'class': ['date', 'category', 'right mini-add-comment', 'socialLinks', 'commentlist']}))
remove_tags_after = [(dict(id='entry'))]
feeds = [(u'Najnowsze wiadomości', u'http://feeds.feedburner.com/satkurierpl?format=xml'),
(u'Sport w telewizji', u'http://feeds.feedburner.com/satkurier/sport?format=xml'),
(u'Blog', u'http://feeds.feedburner.com/satkurier/blog?format=xml')]
def preprocess_html(self, soup):
image = soup.find(attrs={'id': 'news_mini_photo'})
if image:
image.extract()
header = soup.find('h1')
header.replaceWith(header.prettify() + image.prettify())
for alink in soup.findAll('a'):
if alink.string is not None:
tstr = alink.string
alink.replaceWith(tstr)
return soup

View File

@ -0,0 +1,67 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
from calibre.web.feeds.news import BasicNewsRecipe
import re
class telepolis(BasicNewsRecipe):
title = u'Telepolis.pl'
__author__ = 'Artur Stachecki <artur.stachecki@gmail.com>'
language = 'pl'
description = u'Twój telekomunikacyjny serwis informacyjny.\
Codzienne informacje, testy i artykuły,\
promocje, baza telefonów oraz centrum rozrywki'
oldest_article = 7
masthead_url = 'http://telepolis.pl/i/telepolis-logo2.gif'
max_articles_per_feed = 100
simultaneous_downloads = 5
remove_javascript = True
no_stylesheets = True
use_embedded_content = False
remove_tags = []
remove_tags.append(dict(attrs={'alt': 'TELEPOLIS.pl'}))
preprocess_regexps = [(re.compile(r'<: .*? :>'),
lambda match: ''),
(re.compile(r'<b>Zobacz:</b>.*?</a>', re.DOTALL),
lambda match: ''),
(re.compile(r'<-ankieta.*?>'),
lambda match: ''),
(re.compile(r'\(Q\!\)'),
lambda match: ''),
(re.compile(r'\(plik.*?\)'),
lambda match: ''),
(re.compile(r'<br.*?><br.*?>', re.DOTALL),
lambda match: '')
]
extra_css = '''.tb { font-weight: bold; font-size: 20px;}'''
feeds = [
(u'Wiadomości', u'http://www.telepolis.pl/rss/news.php'),
(u'Artykuły', u'http://www.telepolis.pl/rss/artykuly.php')
]
def print_version(self, url):
if 'news.php' in url:
print_url = url.replace('news.php', 'news_print.php')
else:
print_url = url.replace('artykuly.php', 'art_print.php')
return print_url
def preprocess_html(self, soup):
for image in soup.findAll('img'):
if 'm.jpg' in image['src']:
image_big = image['src']
image_big = image_big.replace('m.jpg', '.jpg')
image['src'] = image_big
logo = soup.find('tr')
logo.extract()
for tag in soup.findAll('tr'):
for strings in ['Wiadomość wydrukowana', 'copyright']:
if strings in self.tag_to_string(tag):
tag.extract()
return self.adeify_images(soup)

View File

@ -1,34 +1,50 @@
from calibre.web.feeds.news import BasicNewsRecipe
from calibre.utils.magick import Image
class tvn24(BasicNewsRecipe):
title = u'TVN24'
oldest_article = 7
max_articles_per_feed = 100
__author__ = 'fenuks'
__author__ = 'fenuks, Artur Stachecki'
description = u'Sport, Biznes, Gospodarka, Informacje, Wiadomości Zawsze aktualne wiadomości z Polski i ze świata'
category = 'news'
language = 'pl'
#masthead_url= 'http://www.tvn24.pl/_d/topmenu/logo2.gif'
cover_url= 'http://www.userlogos.org/files/logos/Struna/TVN24.jpg'
extra_css = 'ul {list-style:none;} \
li {list-style:none; float: left; margin: 0 0.15em;} \
h2 {font-size: medium} \
.date60m {float: left; margin: 0 10px 0 5px;}'
masthead_url= 'http://www.tvn24.pl/_d/topmenu/logo2.gif'
cover_url= 'http://www.tvn24.pl/_d/topmenu/logo2.gif'
extra_css= 'ul {list-style: none; padding: 0; margin: 0;} li {float: left;margin: 0 0.15em;}'
remove_empty_feeds = True
remove_javascript = True
no_stylesheets = True
use_embedded_content = False
ignore_duplicate_articles = {'title', 'url'}
keep_only_tags=[dict(name='h1', attrs={'class':['size30 mt10 pb10', 'size38 mt10 pb15']}), dict(name='figure', attrs={'class':'articleMainPhoto articleMainPhotoWide'}), dict(name='article', attrs={'class':['mb20', 'mb20 textArticleDefault']}), dict(name='ul', attrs={'class':'newsItem'})]
remove_tags = [dict(name='aside', attrs={'class':['innerArticleModule onRight cols externalContent', 'innerArticleModule center']}), dict(name='div', attrs={'class':['thumbsGallery', 'articleTools', 'article right rd7', 'heading', 'quizContent']}), dict(name='a', attrs={'class':'watchMaterial text'}), dict(name='section', attrs={'class':['quiz toCenter', 'quiz toRight']})]
feeds = [(u'Najnowsze', u'http://www.tvn24.pl/najnowsze.xml'),
(u'Polska', u'www.tvn24.pl/polska.xml'), (u'\u015awiat', u'http://www.tvn24.pl/swiat.xml'), (u'Sport', u'http://www.tvn24.pl/sport.xml'), (u'Biznes', u'http://www.tvn24.pl/biznes.xml'), (u'Meteo', u'http://www.tvn24.pl/meteo.xml'), (u'Micha\u0142ki', u'http://www.tvn24.pl/michalki.xml'), (u'Kultura', u'http://www.tvn24.pl/kultura.xml')]
keep_only_tags=[
# dict(name='h1', attrs={'class':'size38 mt20 pb20'}),
dict(name='div', attrs={'class':'mainContainer'}),
# dict(name='p'),
# dict(attrs={'class':['size18 mt10 mb15', 'bold topicSize1', 'fromUsers content', 'textArticleDefault']})
]
remove_tags=[
dict(attrs={'class':['commentsInfo', 'textSize', 'related newsNews align-right', 'box', 'watchMaterial text', 'related galleryGallery align-center', 'advert block-alignment-right', 'userActions', 'socialBookmarks', 'im yourArticle fl', 'dynamicButton addComment fl', 'innerArticleModule onRight cols externalContent', 'thumbsGallery', 'relatedObject customBlockquote align-right', 'lead', 'mainRightColumn', 'articleDateContainer borderGreyBottom', 'socialMediaContainer onRight loaded', 'quizContent', 'twitter', 'facebook', 'googlePlus', 'share', 'voteResult', 'reportTitleBar bgBlue_v4 mb15', 'innerVideoModule center']}),
dict(name='article', attrs={'class':['singleArtPhotoCenter', 'singleArtPhotoRight', 'singleArtPhotoLeft']}),
dict(name='section', attrs={'id':['forum', 'innerArticle', 'quiz toCenter', 'mb20']}),
dict(name='div', attrs={'class':'socialMediaContainer big p20 mb20 borderGrey loaded'})
]
remove_tags_after=[dict(name='li', attrs={'class':'share'})]
feeds = [(u'Najnowsze', u'http://www.tvn24.pl/najnowsze.xml'), ]
#(u'Polska', u'www.tvn24.pl/polska.xml'), (u'\u015awiat', u'http://www.tvn24.pl/swiat.xml'), (u'Sport', u'http://www.tvn24.pl/sport.xml'), (u'Biznes', u'http://www.tvn24.pl/biznes.xml'), (u'Meteo', u'http://www.tvn24.pl/meteo.xml'), (u'Micha\u0142ki', u'http://www.tvn24.pl/michalki.xml'), (u'Kultura', u'http://www.tvn24.pl/kultura.xml')]
def preprocess_html(self, soup):
for item in soup.findAll(style=True):
del item['style']
tag = soup.find(name='ul', attrs={'class':'newsItem'})
if tag:
tag.name='div'
tag.li.name='div'
for alink in soup.findAll('a'):
if alink.string is not None:
tstr = alink.string
alink.replaceWith(tstr)
return soup
def postprocess_html(self, soup, first):
#process all the images
for tag in soup.findAll(lambda tag: tag.name.lower()=='img' and tag.has_key('src')):
iurl = tag['src']
img = Image()
img.open(iurl)
if img < 0:
raise RuntimeError('Out of memory')
img.type = "GrayscaleType"
img.save(iurl)
return soup

View File

@ -3,6 +3,8 @@
__license__ = 'GPL v3'
__copyright__ = '2010, matek09, matek09@gmail.com'
__copyright__ = 'Modified 2011, Mariusz Wolek <mariusz_dot_wolek @ gmail dot com>'
__copyright__ = 'Modified 2012, Artur Stachecki <artur.stachecki@gmail.com>'
from calibre.web.feeds.news import BasicNewsRecipe
import re
@ -11,7 +13,7 @@ class Wprost(BasicNewsRecipe):
EDITION = 0
FIND_LAST_FULL_ISSUE = True
EXCLUDE_LOCKED = True
ICO_BLOCKED = 'http://www.wprost.pl/G/icons/ico_blocked.gif'
ICO_BLOCKED = 'http://www.wprost.pl/G/layout2/ico_blocked.png'
title = u'Wprost'
__author__ = 'matek09'
@ -20,6 +22,7 @@ class Wprost(BasicNewsRecipe):
no_stylesheets = True
language = 'pl'
remove_javascript = True
recursions = 0
remove_tags_before = dict(dict(name = 'div', attrs = {'id' : 'print-layer'}))
remove_tags_after = dict(dict(name = 'div', attrs = {'id' : 'print-layer'}))
@ -35,13 +38,15 @@ class Wprost(BasicNewsRecipe):
(re.compile(r'\<td\>\<tr\>\<\/table\>'), lambda match: ''),
(re.compile(r'\<table .*?\>'), lambda match: ''),
(re.compile(r'\<tr>'), lambda match: ''),
(re.compile(r'\<td .*?\>'), lambda match: '')]
(re.compile(r'\<td .*?\>'), lambda match: ''),
(re.compile(r'\<div id="footer"\>.*?\</footer\>'), lambda match: '')]
remove_tags =[]
remove_tags.append(dict(name = 'div', attrs = {'class' : 'def element-date'}))
remove_tags.append(dict(name = 'div', attrs = {'class' : 'def silver'}))
remove_tags.append(dict(name = 'div', attrs = {'id' : 'content-main-column-right'}))
extra_css = '''
.div-header {font-size: x-small; font-weight: bold}
'''
@ -59,27 +64,26 @@ class Wprost(BasicNewsRecipe):
a = 0
if self.FIND_LAST_FULL_ISSUE:
ico_blocked = soup.findAll('img', attrs={'src' : self.ICO_BLOCKED})
a = ico_blocked[-1].findNext('a', attrs={'title' : re.compile('Zobacz spis tre.ci')})
a = ico_blocked[-1].findNext('a', attrs={'title' : re.compile(r'Spis *', re.IGNORECASE | re.DOTALL)})
else:
a = soup.find('a', attrs={'title' : re.compile('Zobacz spis tre.ci')})
a = soup.find('a', attrs={'title' : re.compile(r'Spis *', re.IGNORECASE | re.DOTALL)})
self.EDITION = a['href'].replace('/tygodnik/?I=', '')
self.cover_url = a.img['src']
self.EDITION_SHORT = a['href'].replace('/tygodnik/?I=15', '')
self.cover_url = a.img['src']
def parse_index(self):
self.find_last_issue()
soup = self.index_to_soup('http://www.wprost.pl/tygodnik/?I=' + self.EDITION)
feeds = []
for main_block in soup.findAll(attrs={'class':'main-block-s3 s3-head head-red3'}):
for main_block in soup.findAll(attrs={'id': 'content-main-column-element-content'}):
articles = list(self.find_articles(main_block))
if len(articles) > 0:
section = self.tag_to_string(main_block)
section = self.tag_to_string(main_block.find('h3'))
feeds.append((section, articles))
return feeds
def find_articles(self, main_block):
for a in main_block.findAllNext( attrs={'style':['','padding-top: 15px;']}):
for a in main_block.findAll('a'):
if a.name in "td":
break
if self.EXCLUDE_LOCKED & self.is_blocked(a):
@ -91,3 +95,4 @@ class Wprost(BasicNewsRecipe):
'description' : ''
}

Binary file not shown.

View File

@ -6,7 +6,7 @@ __license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import os, socket, struct, subprocess
import os, socket, struct, subprocess, sys
from distutils.spawn import find_executable
from PyQt4 import pyqtconfig
@ -16,6 +16,7 @@ from setup import isosx, iswindows, islinux
OSX_SDK = '/Developer/SDKs/MacOSX10.5.sdk'
os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.5'
is64bit = sys.maxsize > 2**32
NMAKE = RC = msvc = MT = win_inc = win_lib = win_ddk = win_ddk_lib_dirs = None
if iswindows:

View File

@ -18,7 +18,7 @@ from setup.build_environment import (chmlib_inc_dirs,
msvc, MT, win_inc, win_lib, win_ddk, magick_inc_dirs, magick_lib_dirs,
magick_libs, chmlib_lib_dirs, sqlite_inc_dirs, icu_inc_dirs,
icu_lib_dirs, win_ddk_lib_dirs, ft_libs, ft_lib_dirs, ft_inc_dirs,
zlib_libs, zlib_lib_dirs, zlib_inc_dirs)
zlib_libs, zlib_lib_dirs, zlib_inc_dirs, is64bit)
MT
isunix = islinux or isosx or isbsd
@ -278,6 +278,8 @@ if iswindows:
ldflags = '/DLL /nologo /INCREMENTAL:NO /NODEFAULTLIB:libcmt.lib'.split()
#cflags = '/c /nologo /Ox /MD /W3 /EHsc /Zi'.split()
#ldflags = '/DLL /nologo /INCREMENTAL:NO /DEBUG'.split()
if is64bit:
cflags.append('/GS-')
for p in win_inc:
cflags.append('-I'+p)

View File

@ -301,7 +301,7 @@ class LinuxFreeze(Command):
export MAGICK_CONFIGURE_PATH=$lib/{1}/config
export MAGICK_CODER_MODULE_PATH=$lib/{1}/modules-Q16/coders
export MAGICK_CODER_FILTER_PATH=$lib/{1}/modules-Q16/filters
$base/bin/{0} "$@"
exec $base/bin/{0} "$@"
''')
dest = self.j(self.obj_dir, bname+'.o')

View File

@ -6,13 +6,11 @@ __license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import os, shutil, subprocess, re
import os, shutil, subprocess
from setup import Command, __appname__, __version__
from setup.installer import VMInstaller
SIGNTOOL = r'C:\cygwin\home\kovid\sign.bat'
class Win(Command):
description = 'Build windows binary installers'
@ -38,11 +36,7 @@ class Win32(VMInstaller):
def sign_msi(self):
print ('Signing installers ...')
raw = open(self.VM).read()
vmx = re.search(r'''launch_vmware\(['"](.+?)['"]''', raw).group(1)
subprocess.check_call(['vmrun', '-T', 'ws', '-gu', 'kovid', '-gp',
"et tu brutus", 'runProgramInGuest', vmx, 'cmd.exe', '/C',
r'C:\cygwin\home\kovid\sign.bat'])
subprocess.check_call(['ssh', self.VM_NAME, '~/sign.sh'], shell=False)
def download_installer(self):
installer = self.installer()

View File

@ -13,12 +13,11 @@ from setup import (Command, modules, functions, basenames, __version__,
from setup.build_environment import msvc, MT, RC
from setup.installer.windows.wix import WixMixIn
ICU_DIR = r'Q:\icu'
OPENSSL_DIR = r'Q:\openssl'
QT_DIR = 'Q:\\Qt\\4.8.2'
ICU_DIR = os.environ.get('ICU_DIR', r'Q:\icu')
OPENSSL_DIR = os.environ.get('OPENSSL_DIR', r'Q:\openssl')
QT_DIR = os.environ.get('QT_DIR', 'Q:\\Qt\\4.8.2')
QT_DLLS = ['Core', 'Gui', 'Network', 'Svg', 'WebKit', 'Xml', 'XmlPatterns']
QTCURVE = r'C:\plugins\styles'
LIBUNRAR = 'C:\\Program Files\\UnrarDLL\\unrar.dll'
LIBUNRAR = os.environ.get('UNRARDLL', 'C:\\Program Files\\UnrarDLL\\unrar.dll')
SW = r'C:\cygwin\home\kovid\sw'
IMAGEMAGICK = os.path.join(SW, 'build', 'ImageMagick-6.7.6',
'VisualMagick', 'bin')
@ -262,8 +261,8 @@ class Win32Freeze(Command, WixMixIn):
print
print 'Adding third party dependencies'
print '\tAdding unrar'
shutil.copyfile(LIBUNRAR,
os.path.join(self.dll_dir, os.path.basename(LIBUNRAR)))
shutil.copyfile(LIBUNRAR, os.path.join(self.dll_dir,
os.path.basename(LIBUNRAR).replace('64', '')))
print '\tAdding misc binary deps'
bindir = os.path.join(SW, 'bin')
@ -278,8 +277,10 @@ class Win32Freeze(Command, WixMixIn):
if not ok: continue
dest = self.dll_dir
shutil.copy2(f, dest)
for x in ('zlib1.dll', 'libxml2.dll'):
shutil.copy2(self.j(bindir, x+'.manifest'), self.dll_dir)
for x in ('zlib1.dll', 'libxml2.dll', 'libxslt.dll', 'libexslt.dll'):
msrc = self.j(bindir, x+'.manifest')
if os.path.exists(msrc):
shutil.copy2(msrc, self.dll_dir)
# Copy ImageMagick
for pat in ('*.dll', '*.xml'):

View File

@ -4,16 +4,98 @@ Notes on setting up the windows development environment
Overview
----------
calibre and all its dependencies are compiled using Visual Studio 2008 express edition (free from MS). All the following instructions must be run in a visual studio command prompt unless otherwise noted.
calibre and all its dependencies are compiled using Visual Studio 2008. All the
following instructions must be run in a visual studio command prompt (the
various commands use unix notation, so if you want to use them directly, you
have to setup cygwin).
calibre contains build script to automate the building of the calibre installer. These scripts make certain assumptions about where dependencies are installed. Your best best is to setup a VM and replicate the paths mentioned below exactly.
calibre contains build script to automate the building of the calibre
installer. These scripts make certain assumptions about where dependencies are
installed. Your best best is to setup a VM and replicate the paths mentioned
below exactly.
Microsoft Visual Studio and Windows SDK
----------------------------------------
You have to use Visual Studio 2008 as that is the version Python 2.x works
with.
You need Visual Studio 2008 Express Edition for 32-bit and Professional for 64
bit.
1) Install Visual Studio
2) Install Visual Studio SP1 from http://www.microsoft.com/en-us/download/details.aspx?id=10986
(First check if the version of VS 2008 you have is not already SP1)
3) Install The Windows SDK. You need to install a version that is built for VS
2008. Get it from here: http://www.microsoft.com/en-us/download/details.aspx?id=3138
4) If you are building 64bit, edit the properties of the Visual Studio command
prompt shortcut to pass "amd64" instead of "x86" to the vsvars.bat file so that
it uses the 64 bit tools.
I've read that it is possible to use the 64-bit compiler that comes with the
Windows SDK With VS 2008 Express Edition, but I can't be bothered figuring it
out. Just use the Professional Edition.
Cygwin
------------
This is needed for automation of the build process, and the ease of use of the
unix shell (bash).
Install, vim, rsync, openssh, unzip, wget, make at a minimum.
After installing python run::
python setup/vcvars.py && echo 'source ~/.vcvars' >> ~/.bash_profile
To allow you to use the visual studio tools in the cygwin shell.
The following is only needed for automation (setting up ssh access to the
windows machine).
In order to build debug builds (.pdb files and sign files), you have to be able
to login as the normal user account with ssh. To do this, follow these steps:
* Setup a password for your user account
* Follow the steps here:
http://pcsupport.about.com/od/windows7/ht/auto-logon-windows-7.htm or
http://pcsupport.about.com/od/windowsxp/ht/auto-logon-xp.htm to allow the
machine to bootup without having to enter the password
* First clean out any existing cygwin ssh setup with::
net stop sshd
cygrunsrv -R sshd
net user sshd /DELETE
net user cyg_server /DELETE (delete any other cygwin users account you
can list them with net user)
rm -R /etc/ssh*
mkpasswd -cl > /etc/passwd
mkgroup --local > /etc/group
* Assign the necessary rights to the normal user account::
editrights.exe -a SeAssignPrimaryTokenPrivilege -u kovid
editrights.exe -a SeCreateTokenPrivilege -u kovid
editrights.exe -a SeTcbPrivilege -u kovid
editrights.exe -a SeServiceLogonRight -u kovid
* Run::
ssh-host-config
And answer (yes) to all questions. If it asks do you want to use a
different user name, specify the name of your user account and enter
username and password (it asks on Win 7 not on Win XP)
* On Windows XP, I also had to run::
passwd -R
to allow sshd to use my normal user account even with public key
authentication. See http://cygwin.com/cygwin-ug-net/ntsec.html for
details. On Windows 7 this wasn't necessary for some reason.
* Start sshd with::
net start sshd
* See http://www.kgx.net.nz/2010/03/cygwin-sshd-and-windows-7/ for details
Pass port 22 through Windows firewall. Create ~/.ssh/authorized_keys
Basic dependencies
--------------------
Install cygwin and setup sshd (optional). Used to enable automation of the calibre build VM from linux, not needed if you are building manually.
Install cmake, python, WiX (WiX is used to generate the .msi installer)
Install MS Visual Studio 2008, cmake, python and WiX.
You have to
Set CMAKE_PREFIX_PATH environment variable to C:\cygwin\home\kovid\sw
@ -21,10 +103,16 @@ This is where all dependencies will be installed.
Add C:\Python27\Scripts and C:\Python27 to PATH
Edit mimetypes.py in C:\Python27\Lib and set _winreg = None to prevent reading of mimetypes from the windows registry
Edit mimetypes.py in C:\Python27\Lib and set _winreg = None to prevent reading
of mimetypes from the windows registry
Install setuptools from http://pypi.python.org/pypi/setuptools
If there are no windows binaries already compiled for the version of python you are using then download the source and run the following command in the folder where the source has been unpacked::
Python packages
------------------
Install setuptools from http://pypi.python.org/pypi/setuptools If there are no
windows binaries already compiled for the version of python you are using then
download the source and run the following command in the folder where the
source has been unpacked::
python setup.py install
@ -32,10 +120,9 @@ Run the following command to install python dependencies::
easy_install --always-unzip -U mechanize pyreadline python-dateutil dnspython cssutils clientform pycrypto cssselect
Install BeautifulSoup 3.0.x manually into site-packages (3.1.x parses broken HTML very poorly)
Install pywin32 and edit win32com\__init__.py setting _frozen = True and
__gen_path__ to a temp dir (otherwise it tries to set it to a dir in the install tree which leads to permission errors)
__gen_path__ to a temp dir (otherwise it tries to set it to a dir in the
install tree which leads to permission errors)
Note that you should use::
import tempfile
@ -43,42 +130,58 @@ Note that you should use::
tempfile.gettempdir(), "gen_py",
"%d.%d" % (sys.version_info[0], sys.version_info[1]))
Use gettempdir instead of the win32 api method as gettempdir returns a temp dir that is guaranteed to actually work.
Use gettempdir instead of the win32 api method as gettempdir returns a temp dir
that is guaranteed to actually work.
Also edit win32com\client\gencache.py and change the except IOError on line 57 to catch all exceptions.
Also edit win32com\client\gencache.py and change the except IOError on line 57
to catch all exceptions.
SQLite
---------
Put sqlite3*.h from the sqlite windows amlgamation in ~/sw/include
Put sqlite3*.h from the sqlite windows amalgamation in ~/sw/include
APSW
-----
Download source from http://code.google.com/p/apsw/downloads/list and run in visual studio prompt
python setup.py fetch --all build --missing-checksum-ok --enable-all-extensions install test
python setup.py fetch --all --missing-checksum-ok build --enable-all-extensions install test
OpenSSL
--------
First install ActiveState Perl if you dont already have perl in windows
Download and untar the openssl tarball, follow the instructions in INSTALL.W32 (use no-asm)
Then, get nasm.exe from
http://www.nasm.us/pub/nasm/releasebuilds/2.05/nasm-2.05-win32.zip and put it
somewhere on your PATH (I chose ~/sw/bin)
Download and untar the openssl tarball, follow the instructions in INSTALL.(W32|W64)
to install use prefix q:\openssl
perl Configure VC-WIN32 no-asm enable-static-engine --prefix=Q:/openssl
ms\do_ms.bat
nmake -f ms\ntdll.mak
nmake -f ms\ntdll.mak test
nmake -f ms\ntdll.mak install
For 32-bit::
perl Configure VC-WIN32 no-asm enable-static-engine --prefix=Q:/openssl
ms\do_ms.bat
nmake -f ms\ntdll.mak
nmake -f ms\ntdll.mak test
nmake -f ms\ntdll.mak install
For 64-bit::
perl Configure VC-WIN64A no-asm enable-static-engine --prefix=C:/cygwin/home/kovid/sw/private/openssl
ms\do_win64a
nmake -f ms\ntdll.mak
nmake -f ms\ntdll.mak test
nmake -f ms\ntdll.mak install
Qt
--------
Download Qt sourcecode (.zip) from: http://qt-project.org/downloads
Extract Qt sourcecode to C:\Qt\current
Extract Qt sourcecode to C:\Qt\4.x.x.
Qt uses its own routine to locate and load "system libraries" including the openssl libraries needed for "Get Books". This means that we have to apply the following patch to have Qt load the openssl libraries bundled with calibre:
Qt uses its own routine to locate and load "system libraries" including the
openssl libraries needed for "Get Books". This means that we have to apply the
following patch to have Qt load the openssl libraries bundled with calibre:
--- src/corelib/plugin/qsystemlibrary.cpp 2011-02-22 05:04:00.000000000 -0700
@ -97,7 +200,7 @@ Now, run configure and make::
-no-plugin-manifests is needed so that loading the plugins does not fail looking for the CRT assembly
configure -ltcg -opensource -release -qt-zlib -qt-libmng -qt-libpng -qt-libtiff -qt-libjpeg -release -platform win32-msvc2008 -no-qt3support -webkit -xmlpatterns -no-phonon -no-style-plastique -no-style-cleanlooks -no-style-motif -no-style-cde -no-declarative -no-scripttools -no-audio-backend -no-multimedia -no-dbus -no-openvg -no-opengl -no-qt3support -confirm-license -nomake examples -nomake demos -nomake docs -no-plugin-manifests -openssl -I Q:\openssl\include -L Q:\openssl\lib && nmake
./configure.exe -ltcg -opensource -release -qt-zlib -qt-libmng -qt-libpng -qt-libtiff -qt-libjpeg -release -platform win32-msvc2008 -no-qt3support -webkit -xmlpatterns -no-phonon -no-style-plastique -no-style-cleanlooks -no-style-motif -no-style-cde -no-declarative -no-scripttools -no-audio-backend -no-multimedia -no-dbus -no-openvg -no-opengl -no-qt3support -confirm-license -nomake examples -nomake demos -nomake docs -nomake tools -no-plugin-manifests -openssl -I $OPENSSL_DIR/include -L $OPENSSL_DIR/lib && nmake
Add the path to the bin folder inside the Qt dir to your system PATH.
@ -106,9 +209,7 @@ SIP
Available from: http://www.riverbankcomputing.co.uk/software/sip/download ::
python configure.py -p win32-msvc2008
nmake
nmake install
python configure.py -p win32-msvc2008 && nmake && nmake install
PyQt4
----------
@ -119,15 +220,6 @@ Compiling instructions::
nmake
nmake install
Python Imaging Library
------------------------
Install as normal using installer at http://www.lfd.uci.edu/~gohlke/pythonlibs/
Test it on the target system with
calibre-debug -c "import _imaging, _imagingmath, _imagingft, _imagingcms"
ICU
-------
@ -151,71 +243,63 @@ Optionally run make check
Libunrar
----------
http://www.rarlab.com/rar/UnRARDLL.exe install and add C:\Program Files\UnrarDLL to PATH
Get the source from http://www.rarlab.com/rar_add.htm
lxml
------
Open UnrarDll.vcproj, change build type to release.
If building 64 bit change Win32 to x64.
http://pypi.python.org/pypi/lxml
Build the Solution, find the dll in the build subdir. As best as I can tell,
the vcproj already defines the SILENT preprocessor directive, but you should
test this.
jpeg-7
-------
.. http://www.rarlab.com/rar/UnRARDLL.exe install and add C:\Program Files\UnrarDLL to PATH
Copy::
jconfig.vc to jconfig.h, makejsln.vc9 to jpeg.sln,
makeasln.vc9 to apps.sln, makejvcp.vc9 to jpeg.vcproj,
makecvcp.vc9 to cjpeg.vcproj, makedvcp.vc9 to djpeg.vcproj,
maketvcp.vc9 to jpegtran.vcproj, makervcp.vc9 to rdjpgcom.vcproj, and
makewvcp.vc9 to wrjpgcom.vcproj. (Note that the renaming is critical!)
Load jpeg.sln in Visual Studio
Goto Project->Properties->General Properties and change Configuration Type to dll
Add
#define USE_WINDOWS_MESSAGEBOX
to jconfig.h (this will cause error messages to show up in a box)
Change the definitions of GLOBAL and EXTERN in jmorecfg.h to
#define GLOBAL(type) __declspec(dllexport) type
#define EXTERN(type) extern __declspec(dllexport) type
cp build/jpeg-7/Release/jpeg.dll bin/
cp build/jpeg-7/Release/jpeg.lib build/jpeg-7/Release/jpeg.exp
cp build/jpeg-7/jerror.h build/jpeg-7/jpeglib.h build/jpeg-7/jconfig.h build/jpeg-7/jmorecfg.h include/
TODO: 64-bit check that SILENT is defined and that the ctypes bindings actuall
work
zlib
------
nmake -f win32/Makefile.msc
nmake -f win32/Makefile.msc test
Build with::
nmake -f win32/Makefile.msc
nmake -f win32/Makefile.msc test
cp zlib1.dll* ../../bin
cp zlib.lib zdll.* ../../lib
cp zconf.h zlib.h ../../include
cp zlib1.dll* ../../bin
cp zlib.lib zdll.* ../../lib
cp zconf.h zlib.h ../../include
jpeg-8
-------
Get the source code from: http://sourceforge.net/projects/libjpeg-turbo/files/
Run::
chmod +x cmakescripts/* && cd build
cmake -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release -DWITH_JPEG8=1 ..
nmake
cp sharedlib/jpeg8.dll* ~/sw/bin/
cp sharedlib/jpeg.lib ~/sw/lib/
cp jconfig.h ../jerror.h ../jpeglib.h ../jmorecfg.h ~/sw/include
libpng
---------
cp scripts/CMakelists.txt .
mkdir build
Run cmake-gui.exe with source directory . and build directory build
You will have to point to sw/lib/zdll.lib and sw/include for zlib
Also disable PNG_NO_STDIO and PNG_NO_CONSOLE_IO
Download the libpng .zip source file from:
http://www.libpng.org/pub/png/libpng.html
Now open PNG.sln in VS2008
Set Build type to Release
cp build/libpng-1.2.40/build/Release/libpng12.dll bin/
cp build/libpng-1.2.40/build/Release/png12.* lib/
cp build/libpng-1.2.40/png.h build/libpng-1.2.40/pngconf.h include/
Run::
mkdir build && cd build
cmake -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release -DZLIB_INCLUDE_DIR=C:/cygwin/home/kovid/sw/include -DZLIB_LIBRARY=C:/cygwin/home/kovid/sw/lib/zdll.lib ..
nmake
cp libpng*.dll ~/sw/bin/
cp libpng*.lib ~/sw/lib/
cp pnglibconf.h ../png.h ../pngconf.h ~/sw/include/
freetype
-----------
Get the .zip source from: http://download.savannah.gnu.org/releases/freetype/
Edit *all copies* of the file ftoption.h and add to generate a .lib
and a correct dll
@ -225,38 +309,123 @@ and a correct dll
VS 2008 .sln file is present, open it
Change active build type to release mutithreaded
* If you are doing x64 build, click the Win32 dropdown, select
Configuration manager->Active solution platform -> New -> x64
Project->Properties->Configuration Properties
change configuration type to dll
* Change active build type to release mutithreaded
cp build/freetype-2.3.9/objs/release_mt/freetype.dll bin/
* Project->Properties->Configuration Properties change configuration type
to dll and build solution
cp "`find . -name *.dll`" ~/sw/bin/
cp "`find . -name freetype.lib`" ~/sw/lib/
Now change configuration back to static for .lib and build solution
cp "`find . -name freetype*MT.lib`" ~/sw/lib/
Now change configuration back to static for .lib
cp build/freetype-2.3.9/objs/win32/vc2008/freetype239MT.lib lib/
cp -rf build/freetype-2.3.9/include/* include/
cp -rf include/* ~/sw/include/
TODO: Test if this bloody thing actually works on 64 bit (apparently freetype
assumes sizeof(long) == sizeof(ptr) which is not true in Win64. See for
example: http://forum.openscenegraph.org/viewtopic.php?t=2880
expat
--------
Has a VC 6 project file expat.dsw
Get from: http://sourceforge.net/projects/expat/files/expat/
Set active build to Relase and change build type to dll
Apparently expat requires stdint.h which VS 2008 does not have. So we get our
own.
cp build/expat-2.0.1/win32/bin/Release/*.lib lib/
cp build/expat-2.0.1/win32/bin/Release/*.exp lib/
cp build/expat-2.0.1/win32/bin/Release/*.dll bin/
cp build/expat-2.0.1/lib/expat.h build/expat-2.0.1/lib/expat_external.h include/
Run::
cd lib
wget http://msinttypes.googlecode.com/svn/trunk/stdint.h
mkdir build && cd build
cmake -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release ..
nmake
cp expat.dll ~/sw/bin/ && cp expat.lib ~/sw/lib/
cp ../lib/expat.h ../lib/expat_external.h ~/sw/include
libiconv
----------
Run::
mkdir vs2008 && cd vs2008
Then follow these instructions:
http://www.codeproject.com/Articles/302012/How-to-Build-libiconv-with-Microsoft-Visual-Studio
Change the type to Release and config to x64 or Win32 and Build solution and
then::
cp "`find . -name *.dll`" ~/sw/bin/
cp "`find . -name *.dll.manifest`" ~/sw/bin/
cp "`find . -name *.lib`" ~/sw/lib/iconv.lib
cp "`find . -name iconv.h`" ~/sw/include/
Information for using a static version of libiconv is at the link above.
libxml2
-------------
cd win32
cscript configure.js include=C:\cygwin\home\kovid\sw\include lib=C:\cygwin\home\sw\lib prefix=C:\cygwin\home\kovid\sw zlib=yes iconv=no
nmake /f Makefile.msvc
nmake /f Makefile.msvc install
mv lib/libxml2.dll bin/
cp ./build/libxml2-2.7.5/win32/bin.msvc/*.manifest bin/
Get it from: ftp://xmlsoft.org/libxml2/
Run::
cd win32
cscript.exe configure.js include=C:/cygwin/home/kovid/sw/include lib=C:/cygwin/home/kovid/sw/lib prefix=C:/cygwin/home/kovid/sw zlib=yes iconv=yes
nmake /f Makefile.msvc
mkdir -p ~/sw/include/libxml2/libxml
cp include/libxml/*.h ~/sw/include/libxml2/libxml/
find . -type f \( -name "*.dll" -o -name "*.dll.manifest" \) -exec cp "{}" ~/sw/bin/ \;
find . -name libxml2.lib -exec cp "{}" ~/sw/lib/ \;
libxslt
---------
Get it from: ftp://xmlsoft.org/libxml2/
Run::
cd win32
cscript.exe configure.js include=C:/cygwin/home/kovid/sw/include include=C:/cygwin/home/kovid/sw/include/libxml2 lib=C:/cygwin/home/kovid/sw/lib prefix=C:/cygwin/home/kovid/sw zlib=yes iconv=yes
nmake /f Makefile.msvc
mkdir -p ~/sw/include/libxslt ~/sw/include/libexslt
cp libxslt/*.h ~/sw/include/libxslt/
cp libexslt/*.h ~/sw/include/libexslt/
find . -type f \( -name "*.dll" -o -name "*.dll.manifest" \) -exec cp "{}" ~/sw/bin/ \;
find . -name lib*xslt.lib -exec cp "{}" ~/sw/lib/ \;
lxml
------
Get the source from: http://pypi.python.org/pypi/lxml
Add the following to the top of setupoptions.py::
if option == 'cflags':
return ['-IC:/cygwin/home/kovid/sw/include/libxml2',
'-IC:/cygwin/home/kovid/sw/include']
else:
return ['-LC:/cygwin/home/kovid/sw/lib']
Then, edit src/lxml/includes/etree_defs.h and change the section starting with
#ifndef LIBXML2_NEW_BUFFER
to
#ifdef LIBXML2_NEW_BUFFER
# define xmlBufContent(buf) xmlBufferContent(buf)
# define xmlBufLength(buf) xmlBufferLength(buf)
#endif
Run::
python setup.py install
Python Imaging Library
------------------------
Install as normal using installer at http://www.lfd.uci.edu/~gohlke/pythonlibs/
Test it on the target system with
calibre-debug -c "from PIL import _imaging, _imagingmath, _imagingft, _imagingcms"
kdewin32-msvc
----------------
@ -352,6 +521,8 @@ cp -r build/lib.win32-*/* /cygdrive/c/Python27/Lib/site-packages/
easylzma
----------
This is only needed to build the portable installer.
Get it from http://lloyd.github.com/easylzma/ (use the trunk version)
Run cmake and build the Visual Studio solution (generates CLI tools and dll and

File diff suppressed because it is too large Load Diff

82
setup/vcvars.py Normal file
View File

@ -0,0 +1,82 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:fdm=marker:ai
from __future__ import (unicode_literals, division, absolute_import,
print_function)
__license__ = 'GPL v3'
__copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import os, sys, subprocess
from distutils.msvc9compiler import find_vcvarsall, get_build_version
plat = 'amd64' if sys.maxsize > 2**32 else 'x86'
def remove_dups(variable):
old_list = variable.split(os.pathsep)
new_list = []
for i in old_list:
if i not in new_list:
new_list.append(i)
return os.pathsep.join(new_list)
def query_process(cmd):
result = {}
popen = subprocess.Popen(cmd, stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
try:
stdout, stderr = popen.communicate()
if popen.wait() != 0:
raise RuntimeError(stderr.decode("mbcs"))
stdout = stdout.decode("mbcs")
for line in stdout.splitlines():
if '=' not in line:
continue
line = line.strip()
key, value = line.split('=', 1)
key = key.lower()
if key == 'path':
if value.endswith(os.pathsep):
value = value[:-1]
value = remove_dups(value)
result[key] = value
finally:
popen.stdout.close()
popen.stderr.close()
return result
def query_vcvarsall():
vcvarsall = find_vcvarsall(get_build_version())
return query_process('"%s" %s & set' % (vcvarsall, plat))
env = query_vcvarsall()
paths = env['path'].split(';')
lib = env['lib']
include = env['include']
libpath = env['libpath']
def unix(paths):
up = []
for p in paths:
prefix, p = p.replace(os.sep, '/').partition('/')[0::2]
up.append('/cygdrive/%s/%s'%(prefix[0].lower(), p))
return ':'.join(up)
raw = '''\
#!/bin/sh
export PATH="%s:$PATH"
export LIB="%s"
export INCLUDE="%s"
export LIBPATH="%s"
'''%(unix(paths), lib, include, libpath)
with open(os.path.expanduser('~/.vcvars'), 'wb') as f:
f.write(raw.encode('utf-8'))

View File

@ -4,7 +4,7 @@ __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en'
__appname__ = u'calibre'
numeric_version = (0, 9, 5)
numeric_version = (0, 9, 6)
__version__ = u'.'.join(map(unicode, numeric_version))
__author__ = u"Kovid Goyal <kovid@kovidgoyal.net>"

View File

@ -447,8 +447,8 @@ class CatalogPlugin(Plugin): # {{{
# Return a list of requested fields, with opts.sort_by first
all_std_fields = set(
['author_sort','authors','comments','cover','formats',
'id','isbn','ondevice','pubdate','publisher','rating',
'series_index','series','size','tags','timestamp',
'id','isbn','library_name','ondevice','pubdate','publisher',
'rating','series_index','series','size','tags','timestamp',
'title_sort','title','uuid','languages','identifiers'])
all_custom_fields = set(db.custom_field_keys())
for field in list(all_custom_fields):
@ -460,6 +460,16 @@ class CatalogPlugin(Plugin): # {{{
if opts.fields != 'all':
# Make a list from opts.fields
requested_fields = set(opts.fields.split(','))
# Validate requested_fields
if requested_fields - all_fields:
from calibre.library import current_library_name
invalid_fields = sorted(list(requested_fields - all_fields))
print("invalid --fields specified: %s" % ', '.join(invalid_fields))
print("available fields in '%s': %s" %
(current_library_name(), ', '.join(sorted(list(all_fields)))))
raise ValueError("unable to generate catalog with specified fields")
fields = list(all_fields & requested_fields)
else:
fields = list(all_fields)

View File

@ -18,7 +18,7 @@ from calibre.ebooks.metadata import author_to_author_sort
class Book(Book_):
def __init__(self, prefix, lpath, title=None, authors=None, mime=None, date=None, ContentType=None,
thumbnail_name=None, size=0, other=None):
thumbnail_name=None, size=None, other=None):
# debug_print('Book::__init__ - title=', title)
show_debug = title is not None and title.lower().find("xxxxx") >= 0
if show_debug:
@ -57,7 +57,7 @@ class Book(Book_):
except:
self.datetime = time.gmtime()
self.contentID = None
self.contentID = None
self.current_shelves = []
self.kobo_collections = []
@ -65,7 +65,8 @@ class Book(Book_):
self.thumbnail = ImageWrapper(thumbnail_name)
if show_debug:
debug_print("Book::__init__ - self=", self)
debug_print("Book::__init__ end - self=", self)
debug_print("Book::__init__ end - title=", title, 'authors=', authors)
class ImageWrapper(object):

View File

@ -517,7 +517,7 @@ class KOBO(USBMS):
lpath = lpath[1:]
#print "path: " + lpath
book = self.book_class(prefix, lpath, other=info)
if book.size is None:
if book.size is None or book.size == 0:
book.size = os.stat(self.normalize_path(path)).st_size
b = booklists[blist].add_book(book, replace_metadata=True)
if b:
@ -667,6 +667,7 @@ class KOBO(USBMS):
[_('Unknown')])
size = os.stat(cls.normalize_path(os.path.join(prefix, lpath))).st_size
book = cls.book_class(prefix, lpath, title, authors, mime, date, ContentType, ImageID, size=size, other=mi)
return book
def get_device_paths(self):
@ -1430,6 +1431,7 @@ class KOBOTOUCH(KOBO):
idx = bl_cache.get(lpath, None)
if idx is not None:# and not (accessibility == 1 and isdownloaded == 'false'):
if show_debug:
self.debug_index = idx
debug_print("KoboTouch:update_booklist - idx=%d"%idx)
debug_print('KoboTouch:update_booklist - bl[idx].device_collections=', bl[idx].device_collections)
debug_print('KoboTouch:update_booklist - playlist_map=', playlist_map)
@ -1464,13 +1466,13 @@ class KOBOTOUCH(KOBO):
bl[idx].device_collections = playlist_map.get(lpath,[])
bl[idx].current_shelves = bookshelves
bl[idx].kobo_collections = kobo_collections
changed = True
if show_debug:
debug_print('KoboTouch:update_booklist - updated bl[idx].device_collections=', bl[idx].device_collections)
debug_print('KoboTouch:update_booklist - playlist_map=', playlist_map, 'changed=', changed)
# debug_print('KoboTouch:update_booklist - book=', bl[idx])
debug_print("KoboTouch:update_booklist - book class=%s"%bl[idx].__class__)
debug_print("KoboTouch:update_booklist - book title=%s"%bl[idx].title)
else:
if show_debug:
debug_print('KoboTouch:update_booklist - idx is none')
@ -1494,7 +1496,7 @@ class KOBOTOUCH(KOBO):
if show_debug:
debug_print('KoboTouch:update_booklist - class:', book.__class__)
# debug_print(' resolution:', book.__class__.__mro__)
debug_print(" contentid:'%s'"%book.contentID)
debug_print(" contentid: '%s'"%book.contentID)
debug_print(" title:'%s'"%book.title)
debug_print(" the book:", book)
debug_print(" author_sort:'%s'"%book.author_sort)
@ -1512,6 +1514,7 @@ class KOBOTOUCH(KOBO):
changed = True
if show_debug:
debug_print(' book.device_collections', book.device_collections)
debug_print(' book.title', book.title)
except: # Probably a path encoding error
import traceback
traceback.print_exc()
@ -1534,6 +1537,7 @@ class KOBOTOUCH(KOBO):
# debug_print("KoboTouch:get_bookshelvesforbook - count bookshelves=" + unicode(count_bookshelves))
return bookshelves
self.debug_index = 0
import sqlite3 as sqlite
with closing(sqlite.connect(
self.normalize_path(self._main_prefix +
@ -1635,8 +1639,11 @@ class KOBOTOUCH(KOBO):
# Do the operation in reverse order so indices remain valid
for idx in sorted(bl_cache.itervalues(), reverse=True):
if idx is not None:
need_sync = True
del bl[idx]
if not os.path.exists(self.normalize_path(os.path.join(prefix, bl[idx].lpath))):
need_sync = True
del bl[idx]
# else:
# debug_print("KoboTouch:books - Book in mtadata.calibre, on file system but not database - bl[idx].title:'%s'"%bl[idx].title)
#print "count found in cache: %d, count of files in metadata: %d, need_sync: %s" % \
# (len(bl_cache), len(bl), need_sync)
@ -1650,6 +1657,7 @@ class KOBOTOUCH(KOBO):
USBMS.sync_booklists(self, (None, bl, None))
else:
USBMS.sync_booklists(self, (bl, None, None))
debug_print("KoboTouch:books - have done sync_booklists")
self.report_progress(1.0, _('Getting list of books on device...'))
debug_print("KoboTouch:books - end - oncard='%s'"%oncard)
@ -1894,7 +1902,7 @@ class KOBOTOUCH(KOBO):
# debug_print("KoboTouch:update_device_database_collections - self.bookshelvelist=", self.bookshelvelist)
# Process any collections that exist
for category, books in collections.items():
debug_print("KoboTouch:update_device_database_collections - category='%s'"%category)
debug_print("KoboTouch:update_device_database_collections - category='%s' books=%d"%(category, len(books)))
if create_bookshelves and not (category in supportedcategories or category in readstatuslist or category in accessibilitylist):
self.check_for_bookshelf(connection, category)
# if category in self.bookshelvelist:
@ -1906,9 +1914,11 @@ class KOBOTOUCH(KOBO):
debug_print(' Title="%s"'%book.title, 'category="%s"'%category)
# debug_print(book)
debug_print(' class=%s'%book.__class__)
# debug_print(' resolution:', book.__class__.__mro__)
# debug_print(' subclasses:', book.__class__.__subclasses__())
debug_print(' book.contentID="%s"'%book.contentID)
debug_print(' book.application_id="%s"'%book.application_id)
if book.application_id is None:
continue
category_added = False
@ -1924,7 +1934,7 @@ class KOBOTOUCH(KOBO):
if category not in book.device_collections:
if show_debug:
debug_print(' Setting bookshelf on device')
self.set_bookshelf(connection, book.contentID, category)
self.set_bookshelf(connection, book, category)
category_added = True
elif category in readstatuslist.keys():
# Manage ReadStatus
@ -1956,12 +1966,10 @@ class KOBOTOUCH(KOBO):
else: # No collections
# Since no collections exist the ReadStatus needs to be reset to 0 (Unread)
debug_print("No Collections - reseting ReadStatus")
if oncard == 'carda':
debug_print("Booklists=", booklists)
if self.dbversion < 53:
self.reset_readstatus(connection, oncard)
if self.dbversion >= 14:
debug_print("No Collections - reseting FavouritesIndex")
debug_print("No Collections - resetting FavouritesIndex")
self.reset_favouritesindex(connection, oncard)
if self.supports_bookshelves():
@ -2189,16 +2197,23 @@ class KOBOTOUCH(KOBO):
return bookshelves
def set_bookshelf(self, connection, ContentID, bookshelf):
show_debug = self.is_debugging_title(ContentID)
def set_bookshelf(self, connection, book, shelfName):
show_debug = self.is_debugging_title(book.title)
if show_debug:
debug_print('KoboTouch:set_bookshelf ContentID=' + ContentID)
test_query = 'SELECT 1 FROM ShelfContent WHERE ShelfName = ? and ContentId = ?'
test_values = (bookshelf, ContentID, )
debug_print('KoboTouch:set_bookshelf book.ContentID="%s"'%book.contentID)
debug_print('KoboTouch:set_bookshelf book.current_shelves="%s"'%book.current_shelves)
if shelfName in book.current_shelves:
if show_debug:
debug_print(' book already on shelf.')
return
test_query = 'SELECT _IsDeleted FROM ShelfContent WHERE ShelfName = ? and ContentId = ?'
test_values = (shelfName, book.contentID, )
addquery = 'INSERT INTO ShelfContent ("ShelfName","ContentId","DateModified","_IsDeleted","_IsSynced") VALUES (?, ?, ?, "false", "false")'
add_values = (bookshelf, ContentID, time.strftime(self.TIMESTAMP_STRING, time.gmtime()), )
add_values = (shelfName, book.contentID, time.strftime(self.TIMESTAMP_STRING, time.gmtime()), )
updatequery = 'UPDATE ShelfContent SET _IsDeleted = "false" WHERE ShelfName = ? and ContentId = ?'
update_values = (bookshelf, ContentID, )
update_values = (shelfName, book.contentID, )
cursor = connection.cursor()
cursor.execute(test_query, test_values)
@ -2208,9 +2223,9 @@ class KOBOTOUCH(KOBO):
debug_print(' Did not find a record - adding')
cursor.execute(addquery, add_values)
connection.commit()
else:
elif result[0] == 'true':
if show_debug:
debug_print(' Found a record - updating')
debug_print(' Found a record - updating - result=', result)
cursor.execute(updatequery, update_values)
connection.commit()

View File

@ -12,6 +12,7 @@ import os
import cStringIO
from calibre.constants import isosx
from calibre.devices.usbms.driver import USBMS
class NOOK(USBMS):
@ -84,6 +85,8 @@ class NOOK_COLOR(NOOK):
description = _('Communicate with the Nook Color, TSR and Tablet eBook readers.')
PRODUCT_ID = [0x002, 0x003, 0x004]
if isosx:
PRODUCT_ID.append(0x005) # Nook HD+
BCD = [0x216]
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = ['EBOOK_DISK', 'NOOK_TABLET',

View File

@ -19,9 +19,9 @@ class TECLAST_K3(USBMS):
PRODUCT_ID = [0x3203]
BCD = [0x0000, 0x0100]
VENDOR_NAME = ['TECLAST', 'IMAGIN', 'RK28XX', 'PER3274B']
VENDOR_NAME = ['TECLAST', 'IMAGIN', 'RK28XX', 'PER3274B', 'BEBOOK']
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = ['DIGITAL_PLAYER', 'TL-K5',
'EREADER', 'USB-MSC', 'PER3274B']
'EREADER', 'USB-MSC', 'PER3274B', 'BEBOOK']
MAIN_MEMORY_VOLUME_LABEL = 'K3 Main Memory'
STORAGE_CARD_VOLUME_LABEL = 'K3 Storage Card'

View File

@ -14,6 +14,7 @@ device. This class handles device detection.
import os, subprocess, time, re, sys, glob
from itertools import repeat
from calibre import prints, as_unicode
from calibre.devices.interface import DevicePlugin
from calibre.devices.errors import DeviceError
from calibre.devices.usbms.deviceconfig import DeviceConfig
@ -901,8 +902,11 @@ class Device(DeviceConfig, DevicePlugin):
for d in drives:
try:
winutil.eject_drive(bytes(d)[0])
except:
pass
except Exception as e:
try:
prints("Eject failed:", as_unicode(e))
except:
pass
t = Thread(target=do_it, args=[drives])
t.daemon = True

View File

@ -263,8 +263,11 @@ def generate_masthead(title, output_path=None, width=600, height=60):
masthead_font_family = recs.get('masthead_font', 'Default')
if masthead_font_family != 'Default':
from calibre.utils.fonts.scanner import font_scanner
faces = font_scanner.fonts_for_family(masthead_font_family)
from calibre.utils.fonts.scanner import font_scanner, NoFonts
try:
faces = font_scanner.fonts_for_family(masthead_font_family)
except NoFonts:
faces = []
if faces:
font_path = faces[0]['path']

View File

@ -150,8 +150,15 @@ class EPUBInput(InputFormatPlugin):
from calibre import walk
from calibre.ebooks import DRMError
from calibre.ebooks.metadata.opf2 import OPF
zf = ZipFile(stream)
zf.extractall(os.getcwdu())
try:
zf = ZipFile(stream)
zf.extractall(os.getcwdu())
except:
log.exception('EPUB appears to be invalid ZIP file, trying a'
' more forgiving ZIP parser')
from calibre.utils.localunzip import extractall
stream.seek(0)
extractall(stream)
encfile = os.path.abspath(os.path.join('META-INF', 'encryption.xml'))
opf = self.find_opf()
if opf is None:

View File

@ -144,6 +144,22 @@ class EPUBOutput(OutputFormatPlugin):
for u in XPath('//h:u')(root):
u.tag = 'span'
u.set('style', 'text-decoration:underline')
seen_ids, seen_names = set(), set()
for x in XPath('//*[@id or @name]')(root):
eid, name = x.get('id', None), x.get('name', None)
if eid:
if eid in seen_ids:
del x.attrib['id']
else:
seen_ids.add(eid)
if name:
if name in seen_names:
del x.attrib['name']
else:
seen_names.add(name)
# }}}
def convert(self, oeb, output_path, input_plugin, opts, log):

View File

@ -210,9 +210,7 @@ OptionRecommendation(name='subset_embedded_fonts',
'Subset all embedded fonts. Every embedded font is reduced '
'to contain only the glyphs used in this document. This decreases '
'the size of the font files. Useful if you are embedding a '
'particularly large font with lots of unused glyphs. Note that '
'subsetting is only supported for fonts that contain TrueType '
'outlines, not Postscript outlines.')
'particularly large font with lots of unused glyphs.')
),
OptionRecommendation(name='linearize_tables',

View File

@ -10,6 +10,7 @@ from cStringIO import StringIO
from contextlib import closing
from calibre.utils.zipfile import ZipFile, BadZipfile, safe_replace
from calibre.utils.localunzip import LocalZipFile
from calibre.ebooks.BeautifulSoup import BeautifulStoneSoup
from calibre.ebooks.metadata import MetaInformation
from calibre.ebooks.metadata.opf2 import OPF
@ -105,10 +106,13 @@ class OCFReader(OCF):
class OCFZipReader(OCFReader):
def __init__(self, stream, mode='r', root=None):
try:
self.archive = ZipFile(stream, mode=mode)
except BadZipfile:
raise EPubException("not a ZIP .epub OCF container")
if isinstance(stream, (LocalZipFile, ZipFile)):
self.archive = stream
else:
try:
self.archive = ZipFile(stream, mode=mode)
except BadZipfile:
raise EPubException("not a ZIP .epub OCF container")
self.root = root
if self.root is None:
name = getattr(stream, 'name', False)
@ -119,8 +123,18 @@ class OCFZipReader(OCFReader):
super(OCFZipReader, self).__init__()
def open(self, name, mode='r'):
if isinstance(self.archive, LocalZipFile):
return self.archive.open(name)
return StringIO(self.archive.read(name))
def get_zip_reader(stream, root=None):
try:
zf = ZipFile(stream, mode='r')
except:
stream.seek(0)
zf = LocalZipFile(stream)
return OCFZipReader(zf, root=root)
class OCFDirReader(OCFReader):
def __init__(self, path):
self.root = path
@ -184,7 +198,12 @@ def render_cover(opf, opf_path, zf, reader=None):
def get_cover(opf, opf_path, stream, reader=None):
raster_cover = opf.raster_cover
stream.seek(0)
zf = ZipFile(stream)
try:
zf = ZipFile(stream)
except:
stream.seek(0)
zf = LocalZipFile(stream)
if raster_cover:
base = posixpath.dirname(opf_path)
cpath = posixpath.normpath(posixpath.join(base, raster_cover))
@ -207,7 +226,7 @@ def get_cover(opf, opf_path, stream, reader=None):
def get_metadata(stream, extract_cover=True):
""" Return metadata as a :class:`Metadata` object """
stream.seek(0)
reader = OCFZipReader(stream)
reader = get_zip_reader(stream)
mi = reader.opf.to_book_metadata()
if extract_cover:
try:
@ -232,7 +251,7 @@ def _write_new_cover(new_cdata, cpath):
def set_metadata(stream, mi, apply_null=False, update_timestamp=False):
stream.seek(0)
reader = OCFZipReader(stream, root=os.getcwdu())
reader = get_zip_reader(stream, root=os.getcwdu())
raster_cover = reader.opf.raster_cover
mi = MetaInformation(mi)
new_cdata = None
@ -283,7 +302,11 @@ def set_metadata(stream, mi, apply_null=False, update_timestamp=False):
reader.opf.timestamp = mi.timestamp
newopf = StringIO(reader.opf.render())
safe_replace(stream, reader.container[OPF.MIMETYPE], newopf,
if isinstance(reader.archive, LocalZipFile):
reader.archive.safe_replace(reader.container[OPF.MIMETYPE], newopf,
extra_replacements=replacements)
else:
safe_replace(stream, reader.container[OPF.MIMETYPE], newopf,
extra_replacements=replacements)
try:
if cpath is not None:

View File

@ -71,7 +71,7 @@ class PagedDisplay
this.margin_side = margin_side
this.margin_bottom = margin_bottom
layout: () ->
layout: (is_single_page=false) ->
# start_time = new Date().getTime()
body_style = window.getComputedStyle(document.body)
bs = document.body.style
@ -151,6 +151,8 @@ class PagedDisplay
has_svg = document.getElementsByTagName('svg').length > 0
only_img = document.getElementsByTagName('img').length == 1 and document.getElementsByTagName('div').length < 3 and document.getElementsByTagName('p').length < 2
this.is_full_screen_layout = (only_img or has_svg) and single_screen and document.body.scrollWidth > document.body.clientWidth
if is_single_page
this.is_full_screen_layout = true
this.in_paged_mode = true
this.current_margin_side = sm

View File

@ -126,6 +126,7 @@ class EbookIterator(BookmarksMixin):
self.spine = []
Spiny = partial(SpineItem, read_anchor_map=read_anchor_map,
run_char_count=run_char_count)
is_comic = plumber.input_fmt.lower() in {'cbc', 'cbz', 'cbr', 'cb7'}
for i in ordered:
spath = i.path
mt = None
@ -135,6 +136,8 @@ class EbookIterator(BookmarksMixin):
mt = guess_type(spath)[0]
try:
self.spine.append(Spiny(spath, mime_type=mt))
if is_comic:
self.spine[-1].is_single_page = True
except:
self.log.warn('Missing spine item:', repr(spath))

View File

@ -53,6 +53,7 @@ class SpineItem(unicode):
if mime_type is None:
mime_type = guess_type(obj)[0]
obj.mime_type = mime_type
obj.is_single_page = None
return obj
class IndexEntry(object):

View File

@ -89,7 +89,7 @@ class SubsetFonts(object):
except (IndexError, KeyError, AttributeError, TypeError, ValueError):
val = None if q in {'src', 'font-family'} else default
if q in {'font-weight', 'font-stretch', 'font-style'}:
val = val.lower() if val else val
val = unicode(val).lower() if (val or val == 0) else val
if val == 'inherit':
val = default
if q == 'font-weight':

View File

@ -320,15 +320,23 @@ class ChooseLibraryAction(InterfaceAction):
_('Path to library too long. Must be less than'
' %d characters.')%LibraryDatabase2.WINDOWS_LIBRARY_PATH_LIMIT,
show=True)
if not os.path.exists(loc):
error_dialog(self.gui, _('Not found'),
_('Cannot rename as no library was found at %s. '
'Try switching to this library first, then switch back '
'and retry the renaming.')%loc, show=True)
return
try:
os.rename(loc, newloc)
except:
import traceback
det_msg = 'Location: %r New Location: %r\n%s'%(loc, newloc,
traceback.format_exc())
error_dialog(self.gui, _('Rename failed'),
_('Failed to rename the library at %s. '
'The most common cause for this is if one of the files'
' in the library is open in another program.') % loc,
det_msg=traceback.format_exc(), show=True)
det_msg=det_msg, show=True)
return
self.stats.rename(location, newloc)
self.build_menus()

View File

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>655</width>
<width>699</width>
<height>619</height>
</rect>
</property>
@ -411,7 +411,7 @@
<item row="6" column="3" colspan="2">
<widget class="QCheckBox" name="opt_subset_embedded_fonts">
<property name="text">
<string>&amp;Subset all embedded fonts</string>
<string>&amp;Subset all embedded fonts (Experimental)</string>
</property>
</widget>
</item>

View File

@ -7,8 +7,8 @@ __docformat__ = 'restructuredtext en'
import functools
from PyQt4.Qt import Qt, QStackedWidget, QMenu, \
QSize, QSizePolicy, QStatusBar, QLabel, QFont
from PyQt4.Qt import (Qt, QStackedWidget, QMenu, QTimer,
QSize, QSizePolicy, QStatusBar, QLabel, QFont)
from calibre.utils.config import prefs
from calibre.constants import (isosx, __appname__, preferred_encoding,
@ -274,7 +274,7 @@ class LayoutMixin(object): # {{{
m = self.library_view.model()
if m.rowCount(None) > 0:
self.library_view.set_current_row(0)
QTimer.singleShot(0, self.library_view.set_current_row)
m.current_changed(self.library_view.currentIndex(),
self.library_view.currentIndex())
self.library_view.setFocus(Qt.OtherFocusReason)

View File

@ -1057,6 +1057,7 @@ class DeviceBooksModel(BooksModel): # {{{
booklist_dirtied = pyqtSignal()
upload_collections = pyqtSignal(object)
resize_rows = pyqtSignal()
def __init__(self, parent):
BooksModel.__init__(self, parent)
@ -1163,6 +1164,11 @@ class DeviceBooksModel(BooksModel): # {{{
return flags
def search(self, text, reset=True):
# This should not be here, but since the DeviceBooksModel does not
# implement count_changed and I am too lazy to fix that, this kludge
# will have to do
self.resize_rows.emit()
if not text or not text.strip():
self.map = list(range(len(self.db)))
else:

View File

@ -90,6 +90,7 @@ class BooksView(QTableView): # {{{
def __init__(self, parent, modelcls=BooksModel, use_edit_metadata_dialog=True):
QTableView.__init__(self, parent)
self.row_sizing_done = False
if not tweaks['horizontal_scrolling_per_column']:
self.setHorizontalScrollMode(self.ScrollPerPixel)
@ -141,6 +142,8 @@ class BooksView(QTableView): # {{{
self.display_parent = parent
self._model = modelcls(self)
self.setModel(self._model)
self._model.count_changed_signal.connect(self.do_row_sizing,
type=Qt.QueuedConnection)
self.setSelectionBehavior(QAbstractItemView.SelectRows)
self.setSortingEnabled(True)
self.selectionModel().currentRowChanged.connect(self._model.current_changed)
@ -521,13 +524,17 @@ class BooksView(QTableView): # {{{
self.apply_state(old_state, max_sort_levels=max_levels)
self.column_header.blockSignals(False)
# Resize all rows to have the correct height
if self.model().rowCount(QModelIndex()) > 0:
self.resizeRowToContents(0)
self.verticalHeader().setDefaultSectionSize(self.rowHeight(0))
self.do_row_sizing()
self.was_restored = True
def do_row_sizing(self):
# Resize all rows to have the correct height
if not self.row_sizing_done and self.model().rowCount(QModelIndex()) > 0:
self.resizeRowToContents(0)
self.verticalHeader().setDefaultSectionSize(self.rowHeight(0))
self.row_sizing_done = True
def resize_column_to_fit(self, column):
col = self.column_map.index(column)
self.column_resized(col, self.columnWidth(col), self.columnWidth(col))
@ -777,7 +784,7 @@ class BooksView(QTableView): # {{{
self.scrollTo(self.model().index(row, i), self.PositionAtCenter)
break
def set_current_row(self, row, select=True):
def set_current_row(self, row=0, select=True):
if row > -1 and row < self.model().rowCount(QModelIndex()):
h = self.horizontalHeader()
logical_indices = list(range(h.count()))
@ -943,6 +950,8 @@ class DeviceBooksView(BooksView): # {{{
def __init__(self, parent):
BooksView.__init__(self, parent, DeviceBooksModel,
use_edit_metadata_dialog=False)
self._model.resize_rows.connect(self.do_row_sizing,
type=Qt.QueuedConnection)
self.can_add_columns = False
self.columns_resized = False
self.resize_on_select = False

View File

@ -867,6 +867,7 @@ class Cover(ImageView): # {{{
def __init__(self, parent):
ImageView.__init__(self, parent)
self.show_size = True
self.dialog = parent
self._cdata = None
self.cover_changed.connect(self.set_pixmap_from_data)

View File

@ -18,6 +18,7 @@ from calibre.customize.ui import (initialized_plugins, is_disabled, enable_plugi
remove_plugin, NameConflict)
from calibre.gui2 import (NONE, error_dialog, info_dialog, choose_files,
question_dialog, gprefs)
from calibre.gui2.dialogs.confirm_delete import confirm
from calibre.utils.search_query_parser import SearchQueryParser
from calibre.utils.icu import lower
from calibre.constants import iswindows
@ -363,6 +364,12 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
if plugin.do_user_config(self.gui):
self._plugin_model.refresh_plugin(plugin)
elif op == 'remove':
if not confirm('<p>' +
_('Are you sure you want to remove the plugin: %s?')%
'<b>{0}</b>'.format(plugin.name),
'confirm_plugin_removal_msg', parent=self):
return
msg = _('Plugin <b>{0}</b> successfully removed').format(plugin.name)
if remove_plugin(plugin):
self._plugin_model.populate()

View File

@ -196,6 +196,8 @@ class QueueBulk(QProgressDialog):
dtitle = unicode(mi.title)
except:
dtitle = repr(mi.title)
if len(dtitle) > 50:
dtitle = dtitle[:50].rpartition(' ')[0]+'...'
self.setLabelText(_('Queueing ')+dtitle)
desc = _('Convert book %(num)d of %(tot)d (%(title)s)') % dict(
num=self.i, tot=len(self.book_ids), title=dtitle)

View File

@ -204,7 +204,7 @@ class Document(QWebPage): # {{{
_pass_json_value = pyqtProperty(QString, fget=_pass_json_value_getter,
fset=_pass_json_value_setter)
def after_load(self):
def after_load(self, last_loaded_path=None):
self.javascript('window.paged_display.read_document_margins()')
self.set_bottom_padding(0)
self.fit_images()
@ -213,7 +213,7 @@ class Document(QWebPage): # {{{
if self.in_fullscreen_mode:
self.switch_to_fullscreen_mode()
if self.in_paged_mode:
self.switch_to_paged_mode()
self.switch_to_paged_mode(last_loaded_path=last_loaded_path)
self.read_anchor_positions(use_cache=False)
evaljs = self.mainFrame().evaluateJavaScript
for pl in self.all_viewer_plugins:
@ -240,7 +240,7 @@ class Document(QWebPage): # {{{
self.anchor_positions = {}
return {k:tuple(v) for k, v in self.anchor_positions.iteritems()}
def switch_to_paged_mode(self, onresize=False):
def switch_to_paged_mode(self, onresize=False, last_loaded_path=None):
if onresize and not self.loaded_javascript:
return
self.javascript('''
@ -251,9 +251,12 @@ class Document(QWebPage): # {{{
self.cols_per_screen, self.top_margin, self.side_margin,
self.bottom_margin
))
side_margin = self.javascript('window.paged_display.layout()', typ=int)
force_fullscreen_layout = bool(getattr(last_loaded_path,
'is_single_page', False))
f = 'true' if force_fullscreen_layout else 'false'
side_margin = self.javascript('window.paged_display.layout(%s)'%f, typ=int)
# Setup the contents size to ensure that there is a right most margin.
# Without this webkit renders the final column with no margin, as the
# Without this WebKit renders the final column with no margin, as the
# columns extend beyond the boundaries (and margin) of body
mf = self.mainFrame()
sz = mf.contentsSize()
@ -730,7 +733,7 @@ class DocumentView(QWebView): # {{{
return
self.loading_url = None
self.document.load_javascript_libraries()
self.document.after_load()
self.document.after_load(self.last_loaded_path)
self._size_hint = self.document.mainFrame().contentsSize()
scrolled = False
if self.to_bottom:

View File

@ -54,6 +54,8 @@ class Printing(QObject):
pr.height()/zoomy))
evaljs = self.mf.evaluateJavaScript
loop = QEventLoop(self)
pagenum = 0
from_, to = printer.fromPage(), printer.toPage()
first = True
for path in self.iterator.spine:
@ -74,10 +76,12 @@ class Printing(QObject):
''')
while True:
if not first:
printer.newPage()
first = False
self.mf.render(painter)
pagenum += 1
if (pagenum >= from_ and (to == 0 or pagenum <= to)):
if not first:
printer.newPage()
first = False
self.mf.render(painter)
nsl = evaljs('paged_display.next_screen_location()').toInt()
if not nsl[1] or nsl[0] <= 0: break
evaljs('window.scrollTo(%d, 0)'%nsl[0])

View File

@ -283,6 +283,7 @@ class ImageView(QWidget, ImageDropMixin): # {{{
self.setMinimumSize(QSize(150, 200))
ImageDropMixin.__init__(self)
self.draw_border = True
self.show_size = False
def setPixmap(self, pixmap):
if not isinstance(pixmap, QPixmap):
@ -305,6 +306,7 @@ class ImageView(QWidget, ImageDropMixin): # {{{
if pmap.isNull():
return
w, h = pmap.width(), pmap.height()
ow, oh = w, h
cw, ch = self.rect().width(), self.rect().height()
scaled, nw, nh = fit_image(w, h, cw, ch)
if scaled:
@ -317,12 +319,23 @@ class ImageView(QWidget, ImageDropMixin): # {{{
p = QPainter(self)
p.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform)
p.drawPixmap(target, pmap)
pen = QPen()
pen.setWidth(self.BORDER_WIDTH)
p.setPen(pen)
if self.draw_border:
pen = QPen()
pen.setWidth(self.BORDER_WIDTH)
p.setPen(pen)
p.drawRect(target)
#p.drawRect(self.rect())
if self.show_size:
sztgt = target.adjusted(0, 0, 0, -4)
f = p.font()
f.setBold(True)
f.setPointSize(12)
p.setFont(f)
sz = u'\u00a0%d x %d\u00a0'%(ow, oh)
flags = Qt.AlignBottom|Qt.AlignRight|Qt.TextSingleLine
szrect = p.boundingRect(sztgt, flags, sz)
p.fillRect(szrect.adjusted(0, 0, 0, 4), QColor(0, 0, 0, 200))
p.setPen(QPen(QColor(255,255,255)))
p.drawText(sztgt, flags, sz)
p.end()
# }}}

View File

@ -9,7 +9,7 @@ __docformat__ = 'restructuredtext en'
FIELDS = ['all', 'title', 'title_sort', 'author_sort', 'authors', 'comments',
'cover', 'formats','id', 'isbn', 'ondevice', 'pubdate', 'publisher',
'cover', 'formats','id', 'isbn', 'library_name','ondevice', 'pubdate', 'publisher',
'rating', 'series_index', 'series', 'size', 'tags', 'timestamp',
'uuid', 'languages', 'identifiers']

View File

@ -48,17 +48,21 @@ class CSV_XML(CatalogPlugin):
"Applies to: CSV, XML output formats"))]
def run(self, path_to_output, opts, db, notification=DummyReporter()):
from calibre.library import current_library_name
from calibre.utils.date import isoformat
from calibre.utils.html2text import html2text
from lxml import etree
from calibre.utils.logging import default_log as log
from lxml import etree
self.fmt = path_to_output.rpartition('.')[2]
self.notification = notification
current_library = current_library_name()
if getattr(opts, 'library_path', None):
current_library = os.path.basename(opts.library_path)
if opts.verbose:
opts_dict = vars(opts)
log("%s(): Generating %s" % (self.name,self.fmt.upper()))
log("%s('%s'): Generating %s" % (self.name, current_library, self.fmt.upper()))
if opts.connected_device['is_device_connected']:
log(" connected_device: %s" % opts.connected_device['name'])
if opts_dict['search_text']:
@ -110,6 +114,8 @@ class CSV_XML(CatalogPlugin):
for field in fields:
if field.startswith('#'):
item = db.get_field(entry['id'],field,index_is_id=True)
elif field == 'library_name':
item = current_library
elif field == 'title_sort':
item = entry['sort']
else:
@ -215,6 +221,9 @@ class CSV_XML(CatalogPlugin):
fmt.append(E.format(f.replace(os.sep, '/')))
record.append(fmt)
if 'library_name' in fields:
record.append(E.library_name(current_library))
with open(path_to_output, 'w') as f:
f.write(etree.tostring(root, encoding='utf-8',
xml_declaration=True, pretty_print=True))

View File

@ -14,6 +14,7 @@ from calibre import strftime
from calibre.customize import CatalogPlugin
from calibre.customize.conversion import OptionRecommendation, DummyReporter
from calibre.ebooks import calibre_cover
from calibre.library import current_library_name
from calibre.library.catalogs import AuthorSortMismatchException, EmptyCatalogException
from calibre.ptempfile import PersistentTemporaryFile
from calibre.utils.localization import get_lang
@ -208,8 +209,9 @@ class EPUB_MOBI(CatalogPlugin):
build_log = []
build_log.append(u"%s(): Generating %s %sin %s environment, locale: '%s'" %
build_log.append(u"%s('%s'): Generating %s %sin %s environment, locale: '%s'" %
(self.name,
current_library_name(),
self.fmt,
'for %s ' % opts.output_profile if opts.output_profile else '',
'CLI' if opts.cli_environment else 'GUI',
@ -408,8 +410,8 @@ class EPUB_MOBI(CatalogPlugin):
# Run ebook-convert
from calibre.ebooks.conversion.plumber import Plumber
plumber = Plumber(os.path.join(catalog.catalog_path,
opts.basename + '.opf'), path_to_output, log, report_progress=notification,
plumber = Plumber(os.path.join(catalog.catalog_path, opts.basename + '.opf'),
path_to_output, log, report_progress=notification,
abort_after_input_dump=False)
plumber.merge_ui_recommendations(recommendations)
plumber.run()

View File

@ -3905,7 +3905,7 @@ class CatalogBuilder(object):
mtc = 0
titleTag = Tag(soup, "dc:title")
titleTag.insert(0,self.opts.catalog_title)
titleTag.insert(0,escape(self.opts.catalog_title))
metadata.insert(mtc, titleTag)
mtc += 1
@ -4615,6 +4615,8 @@ class CatalogBuilder(object):
index_is_id=True)
if addendum is None:
addendum = ''
elif type(addendum) is list:
addendum = (', '.join(addendum))
include_hr = eval(self.merge_comments_rule['hr'])
if self.merge_comments_rule['position'] == 'before':
merged = addendum
@ -4631,10 +4633,12 @@ class CatalogBuilder(object):
merged += '\n'
merged += addendum
else:
# Return the custom field contents
# Return only the custom field contents
merged = self.db.get_field(record['id'],
self.merge_comments_rule['field'],
index_is_id=True)
if type(merged) is list:
merged = (', '.join(merged))
return merged

View File

@ -23,13 +23,16 @@ FIELDS = set(['title', 'authors', 'author_sort', 'publisher', 'rating',
'formats', 'isbn', 'uuid', 'pubdate', 'cover', 'last_modified',
'identifiers'])
do_notify = True
def send_message(msg=''):
global do_notify
if not do_notify:
return
prints('Notifying calibre of the change')
from calibre.utils.ipc import RC
import time
t = RC(print_error=False)
t.start()
time.sleep(3)
t.join(3)
if t.done:
t.conn.send('refreshdb:'+msg)
t.conn.close()
@ -42,16 +45,22 @@ def get_parser(usage):
parser = OptionParser(usage)
go = parser.add_option_group(_('GLOBAL OPTIONS'))
go.add_option('--library-path', '--with-library', default=None, help=_('Path to the calibre library. Default is to use the path stored in the settings.'))
go.add_option('--dont-notify-gui', default=False, action='store_true',
help=_('Do not notify the running calibre GUI (if any) that the database has'
' changed. Use with care, as it can lead to database corruption!'))
return parser
def get_db(dbpath, options):
global do_notify
if options.library_path is not None:
dbpath = options.library_path
if dbpath is None:
raise ValueError('No saved library path, either run the GUI or use the'
' --with-library option')
dbpath = os.path.abspath(dbpath)
if options.dont_notify_gui:
do_notify = False
return LibraryDatabase2(dbpath)
def do_list(db, fields, afields, sort_by, ascending, search_text, line_width, separator,

View File

@ -292,6 +292,7 @@ class CustomColumns(object):
if num is not None:
data = self.custom_column_num_map[num]
table,lt = self.custom_table_names(data['num'])
self.dirty_books_referencing('#'+data['label'], id, commit=False)
self.conn.execute('DELETE FROM %s WHERE value=?'%lt, (id,))
self.conn.execute('DELETE FROM %s WHERE id=?'%table, (id,))
self.conn.commit()

View File

@ -32,11 +32,6 @@ def test_lxml():
else:
raise RuntimeError('lxml failed')
def test_freetype():
from calibre.utils.fonts.free_type import test
test()
print ('FreeType OK!')
def test_winutil():
from calibre.devices.scanner import win_pnp_drives
matches = win_pnp_drives.scanner()
@ -79,7 +74,8 @@ def test_imaging():
print ('ImageMagick OK!')
else:
raise RuntimeError('ImageMagick choked!')
from PIL import Image
from PIL import Image, _imaging, _imagingmath, _imagingft, _imagingcms
_imaging, _imagingmath, _imagingft, _imagingcms
i = Image.open(cStringIO.StringIO(data))
if i.size < (20, 20):
raise RuntimeError('PIL choked!')
@ -114,7 +110,6 @@ def test_woff():
def test():
test_plugins()
test_lxml()
test_freetype()
test_sqlite()
test_imaging()
test_unrar()

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

Some files were not shown because too many files have changed in this diff Show More