mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-07 10:14:46 -04:00
0.8.55
This commit is contained in:
commit
0715c8ec03
@ -16,6 +16,12 @@ License: BSD
|
||||
The full text of the BSD license is distributed as in
|
||||
/usr/share/common-licenses/BSD on Debian systems.
|
||||
|
||||
Files: src/qtcurve/*
|
||||
Copyright: Craig Drummond, 2007 - 2010 craig.p.drummond@gmail.com
|
||||
License: GPL-2
|
||||
The full text of the GPL is distributed as in
|
||||
/usr/share/common-licenses/GPL-2 on Debian systems.
|
||||
|
||||
Files: src/calibre/ebooks/chardet/*
|
||||
Copyright: Copyright (C) 1998-2001 Netscape Communications Corporation
|
||||
License: LGPL-2.1+
|
||||
|
@ -19,6 +19,69 @@
|
||||
# new recipes:
|
||||
# - title:
|
||||
|
||||
|
||||
- version: 0.8.55
|
||||
date: 2012-06-08
|
||||
|
||||
new features:
|
||||
- title: "Add a new 'Calibre style' interface look that is more modern than the default look. You can select it via Preferences->Look & Feel->User interface style."
|
||||
|
||||
- title: "New, subtler look for the Tag Browser"
|
||||
|
||||
- title: "Driver for Trekstor Pyrus and Pantech Android Tablet"
|
||||
tickets: [1008946, 1007929]
|
||||
|
||||
- title: "Conversion pipeline: Handle guide elements with incorrectly cased hrefs. Also handle guide elements of type coverimagestandard and thumbimagestandard."
|
||||
|
||||
- title: "Allow user to customize trekstor plugin to send books into sub directories."
|
||||
tickets: [1007646]
|
||||
|
||||
- title: "EPUB Input: Add support for EPUB files that use the IDPF font obfuscation algorithm. Apparently, people have started producing these now."
|
||||
tickets: [1008810]
|
||||
|
||||
- title: "Save single format to disk: Only show the format available in the selected books."
|
||||
tickets: [1007287]
|
||||
|
||||
bug fixes:
|
||||
- title: "MOBI Output: When using the insert metadata at start of book option, do not use a table to layout the metadata, as the Kindle Fire crashes when rendering the table."
|
||||
tickets: [1002119]
|
||||
|
||||
- title: "Device detection: Fix a bug that could cause device detection to fail completely if devices with certain vendor/product ids are connected."
|
||||
tickets: [1009718]
|
||||
|
||||
- title: "MOBI Output: When rasterizing svgs only compute style information when an actual svg image is present. Small speedup when converting large svg-free documents to MOBI."
|
||||
|
||||
- title: "SONY T1 driver: Fix support for collections of books placed on the SD card"
|
||||
tickets: [986044]
|
||||
|
||||
- title: "Fix partitioning problems in tag browser with fields that have no name, such as identifiers and formats"
|
||||
|
||||
- title: "Welcome wizard: Preferentially use the kindle email address set as default when more than one such address exists."
|
||||
tickets: [1007932 ]
|
||||
|
||||
- title: "Fix regression in 0.8.54 that broke the use of the shortcut Alt+A to select books by the same author"
|
||||
|
||||
improved recipes:
|
||||
- Various Polish recipes
|
||||
- Vice Magazine
|
||||
- EL Mundo Today
|
||||
- Haaretz
|
||||
- Good Housekeeping
|
||||
- El Pais
|
||||
- Christian Science Monitor
|
||||
- Marketing Magazine
|
||||
- Instapaper
|
||||
|
||||
new recipes:
|
||||
- title: Various Philippine news sources
|
||||
author: jde
|
||||
|
||||
- title: Natemat.pl and wirtualnemedia.pl
|
||||
author: fenuks
|
||||
|
||||
- title: Rabble.ca
|
||||
author: timtoo
|
||||
|
||||
- version: 0.8.54
|
||||
date: 2012-05-31
|
||||
|
||||
|
68
recipes/banat_news.recipe
Normal file
68
recipes/banat_news.recipe
Normal file
@ -0,0 +1,68 @@
|
||||
|
||||
'''
|
||||
www.philstar.com
|
||||
'''
|
||||
|
||||
import time
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
|
||||
class BanatNews(BasicNewsRecipe):
|
||||
title = 'Banat News'
|
||||
custom_title = "Banat News - " + time.strftime('%d %b %Y %I:%M %p')
|
||||
__author__ = 'jde'
|
||||
__date__ = '31 May 2012'
|
||||
__version__ = '1.0'
|
||||
description = 'Banat News is a daily Cebuano-language newspaper based in Cebu, Philippines - philstar.com is a Philippine news and entertainment portal for the Filipino global community. It is the online presence of the STAR Group of Publications, a leading publisher of newspapers and magazines in the Philippines.'
|
||||
language = 'ceb'
|
||||
publisher = 'The Philippine STAR'
|
||||
category = 'news, Philippines'
|
||||
tags = 'news, Philippines'
|
||||
cover_url = 'http://www.philstar.com/images/logo_Banat.jpg'
|
||||
masthead_url = 'http://www.philstar.com/images/logo_Banat.jpg'
|
||||
oldest_article = 1.5 #days
|
||||
max_articles_per_feed = 25
|
||||
simultaneous_downloads = 10
|
||||
publication_type = 'newspaper'
|
||||
timefmt = ' [%a, %d %b %Y %I:%M %p]'
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
encoding = None
|
||||
recursions = 0
|
||||
needs_subscription = False
|
||||
remove_javascript = True
|
||||
remove_empty_feeds = True
|
||||
auto_cleanup = False
|
||||
|
||||
remove_tags = [dict(name='img', attrs={'id':'Image1'}) #Logo
|
||||
,dict(name='span', attrs={'id':'ControlArticle1_LabelHeader'}) #Section (Headlines, Nation, Metro, ...)
|
||||
,dict(name='a', attrs={'id':'ControlArticle1_FormView1_hlComments'}) #Comments
|
||||
,dict(name='img', attrs={'src':'images/post-comments.jpg'}) #View Comments
|
||||
,dict(name='a', attrs={'id':'ControlArticle1_FormView1_ControlPhotoAndCaption1_hlImageCaption'}) #Zoom
|
||||
]
|
||||
conversion_options = { 'title' : custom_title,
|
||||
'comments' : description,
|
||||
'tags' : tags,
|
||||
'language' : language,
|
||||
'publisher' : publisher,
|
||||
'authors' : publisher,
|
||||
'smarten_punctuation' : True
|
||||
}
|
||||
|
||||
feeds = [
|
||||
('Balita' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=101' )
|
||||
,('Opinyon' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=102' )
|
||||
,('Kalingawan' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=104' )
|
||||
,('Showbiz' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=62' )
|
||||
,('Palaro' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=103' )
|
||||
,('Imong Kapalaran' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=105' )
|
||||
]
|
||||
|
||||
# process the printer friendly version of article
|
||||
def print_version(self, url):
|
||||
return url.replace('/Article', '/ArticlePrinterFriendly')
|
||||
|
||||
# obtain title from printer friendly version of article; avoiding add_toc_thumbnail changing title when article has image
|
||||
def populate_article_metadata(self, article, soup, first):
|
||||
article.title = soup.find('span', {'id': 'ControlArticle1_FormView1_ArticleHeaderLabel'}).contents[0].strip()
|
||||
|
||||
|
@ -1,152 +1,110 @@
|
||||
#!/usr/bin/env python
|
||||
__license__ = 'GPL v3'
|
||||
__author__ = 'Kovid Goyal and Sujata Raman, Lorenzo Vigentini'
|
||||
__copyright__ = '2009, Kovid Goyal and Sujata Raman'
|
||||
__version__ = 'v1.02'
|
||||
__date__ = '10, January 2010'
|
||||
__description__ = 'Providing context and clarity on national and international news, peoples and cultures'
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2012, Darko Miletic <darko.miletic at gmail.com>'
|
||||
'''
|
||||
www.csmonitor.com
|
||||
'''
|
||||
|
||||
'''csmonitor.com'''
|
||||
|
||||
import re
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
|
||||
class ChristianScienceMonitor(BasicNewsRecipe):
|
||||
|
||||
__author__ = 'Kovid Goyal'
|
||||
description = 'Providing context and clarity on national and international news, peoples and cultures'
|
||||
|
||||
cover_url = 'http://www.csmonitor.com/extension/csm_base/design/csm_design/images/csmlogo_179x46.gif'
|
||||
title = 'Christian Science Monitor'
|
||||
publisher = 'The Christian Science Monitor'
|
||||
category = 'News, politics, culture, economy, general interest'
|
||||
|
||||
language = 'en'
|
||||
encoding = 'utf-8'
|
||||
timefmt = '[%a, %d %b, %Y]'
|
||||
|
||||
oldest_article = 16
|
||||
max_articles_per_feed = 20
|
||||
class CSMonitor(BasicNewsRecipe):
|
||||
title = 'The Christian Science Monitor - daily'
|
||||
__author__ = 'Darko Miletic'
|
||||
description = 'The Christian Science Monitor is an international news organization that delivers thoughtful, global coverage via its website, weekly magazine, daily news briefing, and email newsletters.'
|
||||
publisher = 'The Christian Science Monitor'
|
||||
category = 'news, politics, USA'
|
||||
oldest_article = 2
|
||||
max_articles_per_feed = 200
|
||||
no_stylesheets = True
|
||||
encoding = 'utf8'
|
||||
use_embedded_content = False
|
||||
recursion = 10
|
||||
language = 'en'
|
||||
remove_empty_feeds = True
|
||||
publication_type = 'newspaper'
|
||||
masthead_url = 'http://www.csmonitor.com/extension/csm_base/design/csm_design/images/csmlogo_179x46.gif'
|
||||
extra_css = """
|
||||
body{font-family: Arial,Tahoma,Verdana,Helvetica,sans-serif }
|
||||
img{margin-bottom: 0.4em; display:block}
|
||||
.head {font-family: Georgia,"Times New Roman",Times,serif}
|
||||
.sByline,.caption{font-size: x-small}
|
||||
.hide{display: none}
|
||||
.sLoc{font-weight: bold}
|
||||
ul{list-style-type: none}
|
||||
"""
|
||||
|
||||
remove_javascript = True
|
||||
no_stylesheets = True
|
||||
requires_version = (0, 8, 39)
|
||||
conversion_options = {
|
||||
'comment' : description
|
||||
, 'tags' : category
|
||||
, 'publisher' : publisher
|
||||
, 'language' : language
|
||||
}
|
||||
|
||||
def preprocess_raw_html(self, raw, url):
|
||||
try:
|
||||
from html5lib import parse
|
||||
root = parse(raw, namespaceHTMLElements=False,
|
||||
treebuilder='lxml').getroot()
|
||||
from lxml import etree
|
||||
for tag in root.xpath(
|
||||
'//script|//style|//noscript|//meta|//link|//object'):
|
||||
tag.getparent().remove(tag)
|
||||
for elem in list(root.iterdescendants(tag=etree.Comment)):
|
||||
elem.getparent().remove(elem)
|
||||
ans = etree.tostring(root, encoding=unicode)
|
||||
ans = re.sub('.*<html', '<html', ans, flags=re.DOTALL)
|
||||
return ans
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
raise
|
||||
remove_tags = [
|
||||
dict(name=['meta','link','iframe','object','embed'])
|
||||
,dict(attrs={'class':['podStoryRel','bottom-rel','hide']})
|
||||
,dict(attrs={'id':['pgallerycarousel_enlarge','pgallerycarousel_related']})
|
||||
]
|
||||
keep_only_tags = [
|
||||
dict(name='h1', attrs={'class':'head'})
|
||||
,dict(name='h2', attrs={'class':'subhead'})
|
||||
,dict(attrs={'class':['sByline','podStoryGal','ui-body-header','sBody']})
|
||||
]
|
||||
remove_attributes=['xmlns:fb']
|
||||
|
||||
def index_to_soup(self, url):
|
||||
raw = BasicNewsRecipe.index_to_soup(self, url,
|
||||
raw=True).decode('utf-8')
|
||||
raw = self.preprocess_raw_html(raw, url)
|
||||
return BasicNewsRecipe.index_to_soup(self, raw)
|
||||
feeds = [
|
||||
(u'USA' , u'http://rss.csmonitor.com/feeds/usa' )
|
||||
,(u'World' , u'http://rss.csmonitor.com/feeds/world' )
|
||||
,(u'Politics' , u'http://rss.csmonitor.com/feeds/politics' )
|
||||
,(u'Business' , u'http://rss.csmonitor.com/feeds/wam' )
|
||||
,(u'Commentary' , u'http://rss.csmonitor.com/feeds/commentary' )
|
||||
,(u'Books' , u'http://rss.csmonitor.com/feeds/books' )
|
||||
,(u'Arts' , u'http://rss.csmonitor.com/feeds/arts' )
|
||||
,(u'Environment' , u'http://rss.csmonitor.com/feeds/environment')
|
||||
,(u'Innovation' , u'http://rss.csmonitor.com/feeds/scitech' )
|
||||
,(u'Living' , u'http://rss.csmonitor.com/feeds/living' )
|
||||
,(u'Science' , u'http://rss.csmonitor.com/feeds/science' )
|
||||
,(u'The Culture' , u'http://rss.csmonitor.com/feeds/theculture' )
|
||||
,(u'The Home Forum', u'http://rss.csmonitor.com/feeds/homeforum' )
|
||||
,(u'Articles' , u'http://rss.csmonitor.com/feeds/csarticles' )
|
||||
]
|
||||
|
||||
def append_page(self, soup, appendtag, position):
|
||||
nav = soup.find('div',attrs={'class':'navigation'})
|
||||
if nav:
|
||||
pager = nav.findAll('a')
|
||||
for part in pager:
|
||||
if 'Next' in part:
|
||||
nexturl = ('http://www.csmonitor.com' +
|
||||
re.findall(r'href="(.*?)"', str(part))[0])
|
||||
soup2 = self.index_to_soup(nexturl)
|
||||
texttag = soup2.find('div',
|
||||
attrs={'class': re.compile('list-article-.*')})
|
||||
trash_c = soup2.findAll(attrs={'class': 'list-description'})
|
||||
trash_h = soup2.h1
|
||||
for tc in trash_c: tc.extract()
|
||||
trash_h.extract()
|
||||
|
||||
newpos = len(texttag.contents)
|
||||
self.append_page(soup2, texttag, newpos)
|
||||
texttag.extract()
|
||||
appendtag.insert(position, texttag)
|
||||
def append_page(self, soup):
|
||||
pager = soup.find('div', attrs={'class':'navigation'})
|
||||
if pager:
|
||||
nexttag = pager.find(attrs={'id':'next-button'})
|
||||
if nexttag:
|
||||
nurl = 'http://www.csmonitor.com' + nexttag['href']
|
||||
soup2 = self.index_to_soup(nurl)
|
||||
texttag = soup2.find(attrs={'class':'sBody'})
|
||||
if texttag:
|
||||
appendtag = soup.find(attrs={'class':'sBody'})
|
||||
for citem in texttag.findAll(attrs={'class':['podStoryRel','bottom-rel','hide']}):
|
||||
citem.extract()
|
||||
self.append_page(soup2)
|
||||
texttag.extract()
|
||||
pager.extract()
|
||||
appendtag.append(texttag)
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
PRINT_RE = re.compile(r'/layout/set/print/content/view/print/[0-9]*')
|
||||
html = str(soup)
|
||||
try:
|
||||
print_found = PRINT_RE.findall(html)
|
||||
except Exception:
|
||||
pass
|
||||
if print_found:
|
||||
print_url = 'http://www.csmonitor.com' + print_found[0]
|
||||
print_soup = self.index_to_soup(print_url)
|
||||
else:
|
||||
self.append_page(soup, soup.body, 3)
|
||||
|
||||
trash_a = soup.findAll(attrs={'class': re.compile('navigation.*')})
|
||||
trash_b = soup.findAll(attrs={'style': re.compile('.*')})
|
||||
trash_d = soup.findAll(attrs={'class': 'sByline'})
|
||||
for ta in trash_a: ta.extract()
|
||||
for tb in trash_b: tb.extract()
|
||||
for td in trash_d: td.extract()
|
||||
|
||||
print_soup = soup
|
||||
return print_soup
|
||||
|
||||
extra_css = '''
|
||||
h1{ color:#000000;font-family: Georgia,Times,"Times New Roman",serif; font-size: large}
|
||||
.sub{ color:#000000;font-family: Georgia,Times,"Times New Roman",serif; font-size: small;}
|
||||
.byline{ font-family:Arial,Helvetica,sans-serif ; color:#999999; font-size: x-small;}
|
||||
.postdate{color:#999999 ; font-family:Arial,Helvetica,sans-serif ; font-size: x-small; }
|
||||
h3{color:#999999 ; font-family:Arial,Helvetica,sans-serif ; font-size: x-small; }
|
||||
.photoCutline{ color:#333333 ; font-family:Arial,Helvetica,sans-serif ; font-size: x-small; }
|
||||
.photoCredit{ color:#999999 ; font-family:Arial,Helvetica,sans-serif ; font-size: x-small; }
|
||||
#story{font-family:Arial,Tahoma,Verdana,Helvetica,sans-serif ; font-size: small; }
|
||||
#main{font-family:Arial,Tahoma,Verdana,Helvetica,sans-serif ; font-size: small; }
|
||||
#photo-details{ font-family:Arial,Helvetica,sans-serif ; color:#999999; font-size: x-small;}
|
||||
span.name{color:#205B87;font-family: Georgia,Times,"Times New Roman",serif; font-size: x-small}
|
||||
p#dateline{color:#444444 ; font-family:Arial,Helvetica,sans-serif ; font-style:italic;} '''
|
||||
|
||||
feeds = [(u'Top Stories', u'http://rss.csmonitor.com/feeds/top'),
|
||||
(u'World' , u'http://rss.csmonitor.com/feeds/world'),
|
||||
(u'USA' , u'http://rss.csmonitor.com/feeds/usa'),
|
||||
(u'Commentary' , u'http://rss.csmonitor.com/feeds/commentary'),
|
||||
(u'Money' , u'http://rss.csmonitor.com/feeds/wam'),
|
||||
(u'Learning' , u'http://rss.csmonitor.com/feeds/learning'),
|
||||
(u'Living', u'http://rss.csmonitor.com/feeds/living'),
|
||||
(u'Innovation', u'http://rss.csmonitor.com/feeds/scitech'),
|
||||
(u'Gardening', u'http://rss.csmonitor.com/feeds/gardening'),
|
||||
(u'Environment',u'http://rss.csmonitor.com/feeds/environment'),
|
||||
(u'Arts', u'http://rss.csmonitor.com/feeds/arts'),
|
||||
(u'Books', u'http://rss.csmonitor.com/feeds/books'),
|
||||
(u'Home Forum' , u'http://rss.csmonitor.com/feeds/homeforum')
|
||||
]
|
||||
|
||||
keep_only_tags = [dict(name='div', attrs={'id':'mainColumn'}), ]
|
||||
|
||||
remove_tags = [
|
||||
dict(name='div', attrs={'id':['story-tools','videoPlayer','storyRelatedBottom','enlarge-photo','photo-paginate']}),
|
||||
dict(name=['div','a'], attrs={'class':
|
||||
['storyToolbar cfx','podStoryRel','spacer3',
|
||||
'divvy spacer7','comment','storyIncludeBottom',
|
||||
'hide', 'podBrdr']}),
|
||||
dict(name='ul', attrs={'class':[ 'centerliststories']}) ,
|
||||
dict(name='form', attrs={'id':[ 'commentform']}) ,
|
||||
dict(name='div', attrs={'class': ['ui-comments']})
|
||||
]
|
||||
|
||||
remove_tags_after = [ dict(name='div', attrs={'class':[ 'ad csmAd']}),
|
||||
dict(name='div', attrs={'class': [re.compile('navigation.*')]}),
|
||||
dict(name='div', attrs={'style': [re.compile('.*')]})
|
||||
]
|
||||
self.append_page(soup)
|
||||
pager = soup.find('div', attrs={'class':'navigation'})
|
||||
if pager:
|
||||
pager.extract()
|
||||
for item in soup.findAll('a'):
|
||||
limg = item.find('img')
|
||||
if item.string is not None:
|
||||
str = item.string
|
||||
item.replaceWith(str)
|
||||
else:
|
||||
if limg:
|
||||
item.name = 'div'
|
||||
item.attrs = []
|
||||
else:
|
||||
str = self.tag_to_string(item)
|
||||
item.replaceWith(str)
|
||||
for item in soup.findAll('img'):
|
||||
if 'scorecardresearch' in item['src']:
|
||||
item.extract()
|
||||
else:
|
||||
if not item.has_key('alt'):
|
||||
item['alt'] = 'image'
|
||||
return soup
|
||||
|
@ -1,3 +1,4 @@
|
||||
import re
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class ElMundoTodayRecipe(BasicNewsRecipe):
|
||||
@ -7,11 +8,32 @@ class ElMundoTodayRecipe(BasicNewsRecipe):
|
||||
category = 'Noticias, humor'
|
||||
cover_url = 'http://www.elmundotoday.com/wp-content/themes/EarthlyTouch/images/logo.png'
|
||||
oldest_article = 30
|
||||
max_articles_per_feed = 30
|
||||
auto_cleanup = True
|
||||
max_articles_per_feed = 60
|
||||
auto_cleanup = False
|
||||
no_stylesheets = True
|
||||
remove_javascript = True
|
||||
language = 'es'
|
||||
use_embedded_content = True
|
||||
use_embedded_content = False
|
||||
|
||||
preprocess_regexps = [
|
||||
(re.compile(r'</title>.*<!--Begin Article Single-->', re.DOTALL),
|
||||
lambda match: '</title><body>'),
|
||||
#(re.compile(r'^\t{5}<a href.*Permanent Link to ">$'), lambda match: ''),
|
||||
#(re.compile(r'\t{5}</a>$'), lambda match: ''),
|
||||
(re.compile(r'<div class="social4i".*</body>', re.DOTALL),
|
||||
lambda match: '</body>'),
|
||||
]
|
||||
|
||||
keep_only_tags = [
|
||||
dict(name='div', attrs={'class':'post-wrapper'})
|
||||
]
|
||||
|
||||
remove_attributes = [ 'href', 'title', 'alt' ]
|
||||
|
||||
extra_css = '''
|
||||
.antetitulo{font-variant:small-caps; font-weight:bold} .articleinfo{font-size:small}
|
||||
img{margin-bottom:0.4em; display:block; margin-left:auto; margin-right:auto}
|
||||
'''
|
||||
|
||||
feeds = [('El Mundo Today', 'http://www.elmundotoday.com/feed/')]
|
||||
|
||||
|
@ -10,6 +10,7 @@ class Elektroda(BasicNewsRecipe):
|
||||
category = 'electronics'
|
||||
language = 'pl'
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets= True
|
||||
remove_tags_before=dict(name='span', attrs={'class':'postbody'})
|
||||
remove_tags_after=dict(name='td', attrs={'class':'spaceRow'})
|
||||
remove_tags=[dict(name='a', attrs={'href':'#top'})]
|
||||
|
@ -1,5 +1,6 @@
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2010, Darko Miletic <darko.miletic at gmail.com>'
|
||||
__copyright__ = '2010-2012, Darko Miletic <darko.miletic at gmail.com>'
|
||||
'''
|
||||
www.elpais.com
|
||||
'''
|
||||
@ -7,23 +8,24 @@ www.elpais.com
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class ElPais_RSS(BasicNewsRecipe):
|
||||
title = 'El Pais'
|
||||
title = u'El País'
|
||||
__author__ = 'Darko Miletic'
|
||||
description = 'el periodico global en Castellano'
|
||||
description = u'Noticias de última hora sobre la actualidad en España y el mundo: política, economía, deportes, cultura, sociedad, tecnología, gente, opinión, viajes, moda, televisión, los blogs y las firmas de EL PAÍS. Además especiales, vídeos, fotos, audios, gráficos, entrevistas, promociones y todos los servicios de EL PAÍS.'
|
||||
publisher = 'EDICIONES EL PAIS, S.L.'
|
||||
category = 'news, politics, finances, world, spain'
|
||||
oldest_article = 2
|
||||
max_articles_per_feed = 200
|
||||
no_stylesheets = True
|
||||
encoding = 'cp1252'
|
||||
encoding = 'utf8'
|
||||
use_embedded_content = False
|
||||
language = 'es'
|
||||
remove_empty_feeds = True
|
||||
publication_type = 'newspaper'
|
||||
masthead_url = 'http://www.elpais.com/im/tit_logo.gif'
|
||||
masthead_url = 'http://ep01.epimg.net/iconos/v1.x/v1.0/logos/cabecera_portada.png'
|
||||
extra_css = """
|
||||
body{font-family: Georgia,"Times New Roman",Times,serif }
|
||||
h3{font-family: Arial,Helvetica,sans-serif}
|
||||
h1{font-family: Georgia,"Times New Roman",Times,serif }
|
||||
#subtitulo_noticia, .firma, .figcaption{font-size: small}
|
||||
body{font-family: Arial,Helvetica,Garuda,sans-serif}
|
||||
img{margin-bottom: 0.4em; display:block}
|
||||
"""
|
||||
|
||||
@ -34,49 +36,61 @@ class ElPais_RSS(BasicNewsRecipe):
|
||||
, 'language' : language
|
||||
}
|
||||
|
||||
keep_only_tags = [dict(attrs={'class':['cabecera_noticia estirar','cabecera_noticia','','contenido_noticia']})]
|
||||
remove_tags = [
|
||||
dict(name=['meta','link','base','iframe','embed','object'])
|
||||
,dict(attrs={'class':['info_complementa','estructura_2col_der','votos estirar','votos']})
|
||||
,dict(attrs={'id':'utilidades'})
|
||||
keep_only_tags = [
|
||||
dict(attrs={'id':['titulo_noticia','subtitulo_noticia']})
|
||||
,dict(attrs={'class':['firma','columna_texto','entrevista_p_r']})
|
||||
]
|
||||
remove_tags = [
|
||||
dict(name=['meta','link','base','iframe','embed','object'])
|
||||
,dict(attrs={'class':'disposicion_vertical'})
|
||||
]
|
||||
remove_tags_after = dict(attrs={'id':'utilidades'})
|
||||
remove_attributes = ['lang','border','width','height']
|
||||
|
||||
feeds = [
|
||||
(u'Lo ultimo' , u'http://www.elpais.com/rss/feed.html?feedId=17046')
|
||||
,(u'America Latina' , u'http://www.elpais.com/rss/feed.html?feedId=17041')
|
||||
,(u'Mexico' , u'http://www.elpais.com/rss/feed.html?feedId=17042')
|
||||
,(u'Europa' , u'http://www.elpais.com/rss/feed.html?feedId=17043')
|
||||
,(u'Estados Unidos' , u'http://www.elpais.com/rss/feed.html?feedId=17044')
|
||||
,(u'Oriente proximo' , u'http://www.elpais.com/rss/feed.html?feedId=17045')
|
||||
,(u'Espana' , u'http://www.elpais.com/rss/feed.html?feedId=1002' )
|
||||
,(u'Andalucia' , u'http://www.elpais.com/rss/feed.html?feedId=17057')
|
||||
,(u'Catalunia' , u'http://www.elpais.com/rss/feed.html?feedId=17059')
|
||||
,(u'Comunidad Valenciana' , u'http://www.elpais.com/rss/feed.html?feedId=17061')
|
||||
,(u'Madrid' , u'http://www.elpais.com/rss/feed.html?feedId=1016' )
|
||||
,(u'Pais Vasco' , u'http://www.elpais.com/rss/feed.html?feedId=17062')
|
||||
,(u'Galicia' , u'http://www.elpais.com/rss/feed.html?feedId=17063')
|
||||
,(u'Opinion' , u'http://www.elpais.com/rss/feed.html?feedId=1003' )
|
||||
,(u'Sociedad' , u'http://www.elpais.com/rss/feed.html?feedId=1004' )
|
||||
,(u'Deportes' , u'http://www.elpais.com/rss/feed.html?feedId=1007' )
|
||||
,(u'Cultura' , u'http://www.elpais.com/rss/feed.html?feedId=1008' )
|
||||
,(u'Cine' , u'http://www.elpais.com/rss/feed.html?feedId=17052')
|
||||
,(u'Literatura' , u'http://www.elpais.com/rss/feed.html?feedId=17053')
|
||||
,(u'Musica' , u'http://www.elpais.com/rss/feed.html?feedId=17051')
|
||||
,(u'Arte' , u'http://www.elpais.com/rss/feed.html?feedId=17060')
|
||||
,(u'Tecnologia' , u'http://www.elpais.com/rss/feed.html?feedId=1005' )
|
||||
,(u'Economia' , u'http://www.elpais.com/rss/feed.html?feedId=1006' )
|
||||
,(u'Ciencia' , u'http://www.elpais.com/rss/feed.html?feedId=17068')
|
||||
,(u'Salud' , u'http://www.elpais.com/rss/feed.html?feedId=17074')
|
||||
,(u'Ocio' , u'http://www.elpais.com/rss/feed.html?feedId=17075')
|
||||
,(u'Justicia y Leyes' , u'http://www.elpais.com/rss/feed.html?feedId=17069')
|
||||
,(u'Guerras y conflictos' , u'http://www.elpais.com/rss/feed.html?feedId=17070')
|
||||
,(u'Politica' , u'http://www.elpais.com/rss/feed.html?feedId=17073')
|
||||
(u'Lo ultimo' , u'http://ep00.epimg.net/rss/tags/ultimas_noticias.xml')
|
||||
,(u'America Latina' , u'http://elpais.com/tag/rss/latinoamerica/a/' )
|
||||
,(u'Mexico' , u'http://elpais.com/tag/rss/mexico/a/' )
|
||||
,(u'Europa' , u'http://elpais.com/tag/rss/europa/a/' )
|
||||
,(u'Estados Unidos' , u'http://elpais.com/tag/rss/estados_unidos/a/' )
|
||||
,(u'Oriente proximo' , u'http://elpais.com/tag/rss/oriente_proximo/a/' )
|
||||
,(u'Andalucia' , u'http://ep00.epimg.net/rss/ccaa/andalucia.xml' )
|
||||
,(u'Catalunia' , u'http://ep00.epimg.net/rss/ccaa/catalunya.xml' )
|
||||
,(u'Comunidad Valenciana' , u'http://ep00.epimg.net/rss/ccaa/valencia.xml' )
|
||||
,(u'Madrid' , u'http://ep00.epimg.net/rss/ccaa/madrid.xml' )
|
||||
,(u'Pais Vasco' , u'http://ep00.epimg.net/rss/ccaa/paisvasco.xml' )
|
||||
,(u'Galicia' , u'http://ep00.epimg.net/rss/ccaa/galicia.xml' )
|
||||
,(u'Sociedad' , u'http://ep00.epimg.net/rss/sociedad/portada.xml' )
|
||||
,(u'Deportes' , u'http://ep00.epimg.net/rss/deportes/portada.xml' )
|
||||
,(u'Cultura' , u'http://ep00.epimg.net/rss/cultura/portada.xml' )
|
||||
,(u'Cine' , u'http://elpais.com/tag/rss/cine/a/' )
|
||||
,(u'Economía' , u'http://elpais.com/tag/rss/economia/a/' )
|
||||
,(u'Literatura' , u'http://elpais.com/tag/rss/libros/a/' )
|
||||
,(u'Musica' , u'http://elpais.com/tag/rss/musica/a/' )
|
||||
,(u'Arte' , u'http://elpais.com/tag/rss/arte/a/' )
|
||||
,(u'Medio Ambiente' , u'http://elpais.com/tag/rss/medio_ambiente/a/' )
|
||||
,(u'Tecnologia' , u'http://ep01.epimg.net/rss/tecnologia/portada.xml' )
|
||||
,(u'Ciencia' , u'http://ep00.epimg.net/rss/tags/c_ciencia.xml' )
|
||||
,(u'Salud' , u'http://elpais.com/tag/rss/salud/a/' )
|
||||
,(u'Ocio' , u'http://elpais.com/tag/rss/ocio/a/' )
|
||||
,(u'Justicia y Leyes' , u'http://elpais.com/tag/rss/justicia/a/' )
|
||||
,(u'Guerras y conflictos' , u'http://elpais.com/tag/rss/conflictos/a/' )
|
||||
,(u'Politica' , u'http://ep00.epimg.net/rss/politica/portada.xml' )
|
||||
,(u'Opinion' , u'http://ep01.epimg.net/rss/politica/opinion.xml' )
|
||||
]
|
||||
|
||||
def print_version(self, url):
|
||||
return url + '?print=1'
|
||||
def get_article_url(self, article):
|
||||
url = BasicNewsRecipe.get_article_url(self, article)
|
||||
if url and (not('/album/' in url) and not('/futbol/partido/' in url)):
|
||||
return url
|
||||
self.log('Skipping non-article', url)
|
||||
return None
|
||||
|
||||
def get_cover_url(self):
|
||||
soup = self.index_to_soup('http://elpais.com/')
|
||||
for image in soup.findAll('img'):
|
||||
if image['src'].endswith('elpaisTodayMiddle.jpg'):
|
||||
sstr = image['src']
|
||||
return sstr.replace('elpaisTodayMiddle.jpg', 'elpaisToday.jpg')
|
||||
return None
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
for item in soup.findAll(style=True):
|
||||
|
@ -12,8 +12,8 @@ class Gameplay_pl(BasicNewsRecipe):
|
||||
max_articles_per_feed = 100
|
||||
remove_javascript= True
|
||||
no_stylesheets= True
|
||||
keep_only_tags=[dict(name='div', attrs={'class':['news_endpage_tit', 'news']})]
|
||||
remove_tags=[dict(name='div', attrs={'class':['galeria', 'noedit center im', 'news_list', 'news_list_autor', 'stop_bot', 'tagi']}), dict(attrs={'usemap':'#map'})]
|
||||
keep_only_tags=[dict(name='div', attrs={'class':['news_endpage_tit', 'news', 'news_container']})]
|
||||
remove_tags=[dict(name='div', attrs={'class':['galeria', 'noedit center im', 'news_list', 'news_list_autor', 'stop_bot', 'tagi', 'news_tagi']}), dict(attrs={'usemap':'#map'}), dict(name='a', attrs={'class':['pin-it-button', 'twitter-share-button']})]
|
||||
feeds = [(u'Wiadomo\u015bci', u'http://gameplay.pl/rss/')]
|
||||
|
||||
def image_url_processor(self, baseurl, url):
|
||||
|
@ -8,12 +8,17 @@ class AdvancedUserRecipe1305547242(BasicNewsRecipe):
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
#auto_cleanup = True
|
||||
remove_javascript = True
|
||||
|
||||
def print_version(self,url):
|
||||
segments = url.split('/')
|
||||
printURL = '/'.join(segments[0:3]) + '/print-this/' + '/'.join(segments[4:])
|
||||
return printURL
|
||||
if '/tips-for-making-desserts?' in url:
|
||||
return None
|
||||
segments = url.split('/')
|
||||
segments[-1] = segments[-1].split('?')[0]
|
||||
segments[-1] +='?page=all'
|
||||
printURL = '/'.join(segments[0:3]) + '/print-this/' + segments[-1]
|
||||
return printURL
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
for alink in soup.findAll('a'):
|
||||
@ -22,10 +27,19 @@ class AdvancedUserRecipe1305547242(BasicNewsRecipe):
|
||||
alink.replaceWith(tstr)
|
||||
return soup
|
||||
|
||||
feeds = [ (u'Recipes & Entertaining', u'http://www.goodhousekeeping.com/food/food-rss/?src=rss'),
|
||||
(u'Home & House', u'http://www.goodhousekeeping.com/home/home-rss/?src=rss'),
|
||||
(u'Diet & Health', u'http://www.goodhousekeeping.com/health/health-rss/?src=rss'),
|
||||
(u'Beauty & Style', u'http://www.goodhousekeeping.com/beauty/beauty-rss/?src=rss'),
|
||||
(u'Family & Pets', u'http://www.goodhousekeeping.com/family/family-rss/?src=rss'),
|
||||
(u'Saving Money', u'http://www.goodhousekeeping.com/money/money-rss/?src=rss'),
|
||||
]
|
||||
|
||||
#feeds = [
|
||||
#(u'Food and Recipes', u'http://www.goodhousekeeping.com/rss/recipes/'),
|
||||
#]
|
||||
|
||||
|
||||
feeds = [
|
||||
(u'Food and Recipes', u'http://www.goodhousekeeping.com/rss/recipes/'),
|
||||
(u'Home and Organizing', u'http://www.goodhousekeeping.com/rss/home/'),
|
||||
(u'Diet and Health', u'http://www.goodhousekeeping.com/rss/health/'),
|
||||
(u'Beauty and Anti-Aging', u'http://www.goodhousekeeping.com/rss/beauty/'),
|
||||
(u'Family and Relationships', u'http://www.goodhousekeeping.com/rss/family/'),
|
||||
(u'Holidays', u'http://www.goodhousekeeping.com/rss/holidays/'),
|
||||
(u'In the Test Kitchen', 'http://www.goodhousekeeping.com/rss/test-kitchen-blog/'),
|
||||
]
|
||||
|
||||
|
@ -12,13 +12,16 @@ class Gram_pl(BasicNewsRecipe):
|
||||
no_stylesheets= True
|
||||
extra_css = 'h2 {font-style: italic; font-size:20px;} .picbox div {float: left;}'
|
||||
cover_url=u'http://www.gram.pl/www/01/img/grampl_zima.png'
|
||||
remove_tags= [dict(name='p', attrs={'class':['extraText', 'must-log-in']}), dict(attrs={'class':['el', 'headline', 'post-info']}), dict(name='div', attrs={'class':['twojaOcena', 'comment-body', 'comment-author vcard', 'comment-meta commentmetadata', 'tw_button']}), dict(id=['igit_rpwt_css', 'comments', 'reply-title', 'igit_title'])]
|
||||
keep_only_tags= [dict(name='div', attrs={'class':['main', 'arkh-postmetadataheader', 'arkh-postcontent', 'post', 'content', 'news_header', 'news_subheader', 'news_text']}), dict(attrs={'class':['contentheading', 'contentpaneopen']})]
|
||||
remove_tags= [dict(name='p', attrs={'class':['extraText', 'must-log-in']}), dict(attrs={'class':['el', 'headline', 'post-info', 'entry-footer clearfix']}), dict(name='div', attrs={'class':['twojaOcena', 'comment-body', 'comment-author vcard', 'comment-meta commentmetadata', 'tw_button', 'entry-comment-counter', 'snap_nopreview sharing robots-nocontent']}), dict(id=['igit_rpwt_css', 'comments', 'reply-title', 'igit_title'])]
|
||||
keep_only_tags= [dict(name='div', attrs={'class':['main', 'arkh-postmetadataheader', 'arkh-postcontent', 'post', 'content', 'news_header', 'news_subheader', 'news_text']}), dict(attrs={'class':['contentheading', 'contentpaneopen']}), dict(name='article')]
|
||||
feeds = [(u'Informacje', u'http://www.gram.pl/feed_news.asp'),
|
||||
(u'Publikacje', u'http://www.gram.pl/feed_news.asp?type=articles')]
|
||||
(u'Publikacje', u'http://www.gram.pl/feed_news.asp?type=articles'),
|
||||
(u'Kolektyw- Indie Games', u'http://indie.gram.pl/feed/'),
|
||||
#(u'Kolektyw- Moto Games', u'http://www.motogames.gram.pl/news.rss')
|
||||
]
|
||||
|
||||
def parse_feeds (self):
|
||||
feeds = BasicNewsRecipe.parse_feeds(self)
|
||||
def parse_feeds (self):
|
||||
feeds = BasicNewsRecipe.parse_feeds(self)
|
||||
for feed in feeds:
|
||||
for article in feed.articles[:]:
|
||||
if 'REKLAMA SKLEP' in article.title.upper() or u'ARTYKUŁ:' in article.title.upper():
|
||||
@ -56,4 +59,4 @@ class Gram_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
|
||||
|
@ -1,13 +0,0 @@
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class GreenLinux(BasicNewsRecipe):
|
||||
title = u'GreenLinux.pl'
|
||||
__author__ = 'fenuks'
|
||||
category = 'IT'
|
||||
language = 'pl'
|
||||
cover_url = 'http://lh5.ggpht.com/_xd_6Y9kXhEc/S8tjyqlfhfI/AAAAAAAAAYU/zFNTp07ZQko/top.png'
|
||||
oldest_article = 15
|
||||
max_articles_per_feed = 100
|
||||
auto_cleanup = True
|
||||
|
||||
feeds = [(u'Newsy', u'http://feeds.feedburner.com/greenlinux')]
|
@ -1,16 +1,15 @@
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2010, Darko Miletic <darko.miletic at gmail.com>'
|
||||
__copyright__ = '2010-2012, Darko Miletic <darko.miletic at gmail.com>'
|
||||
'''
|
||||
www.haaretz.com
|
||||
'''
|
||||
|
||||
import re
|
||||
from calibre import strftime
|
||||
from time import gmtime
|
||||
import urllib
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class HaaretzPrint_en(BasicNewsRecipe):
|
||||
title = 'Haaretz - print edition'
|
||||
class Haaretz_en(BasicNewsRecipe):
|
||||
title = 'Haaretz'
|
||||
__author__ = 'Darko Miletic'
|
||||
description = "Haaretz.com is the world's leading English-language Website for real-time news and analysis of Israel and the Middle East."
|
||||
publisher = 'Haaretz'
|
||||
@ -21,10 +20,16 @@ class HaaretzPrint_en(BasicNewsRecipe):
|
||||
encoding = 'utf8'
|
||||
use_embedded_content = False
|
||||
language = 'en_IL'
|
||||
needs_subscription = True
|
||||
remove_empty_feeds = True
|
||||
publication_type = 'newspaper'
|
||||
PREFIX = 'http://www.haaretz.com'
|
||||
masthead_url = PREFIX + '/images/logos/logoGrey.gif'
|
||||
extra_css = ' body{font-family: Verdana,Arial,Helvetica,sans-serif } '
|
||||
masthead_url = PREFIX + '/images/logos/HaaretzLogo.gif'
|
||||
extra_css = """
|
||||
body{font-family: Verdana,Arial,Helvetica,sans-serif }
|
||||
h1, .articleBody {font-family: Georgia, serif}
|
||||
.authorBar {font-size: small}
|
||||
"""
|
||||
|
||||
preprocess_regexps = [(re.compile(r'</body>.*?</html>', re.DOTALL|re.IGNORECASE),lambda match: '</body></html>')]
|
||||
|
||||
@ -44,53 +49,42 @@ class HaaretzPrint_en(BasicNewsRecipe):
|
||||
|
||||
|
||||
feeds = [
|
||||
(u'News' , PREFIX + u'/print-edition/news' )
|
||||
,(u'Opinion' , PREFIX + u'/print-edition/opinion' )
|
||||
,(u'Business' , PREFIX + u'/print-edition/business' )
|
||||
,(u'Real estate' , PREFIX + u'/print-edition/real-estate' )
|
||||
,(u'Sports' , PREFIX + u'/print-edition/sports' )
|
||||
,(u'Travel' , PREFIX + u'/print-edition/travel' )
|
||||
,(u'Books' , PREFIX + u'/print-edition/books' )
|
||||
,(u'Food & Wine' , PREFIX + u'/print-edition/food-wine' )
|
||||
,(u'Arts & Leisure', PREFIX + u'/print-edition/arts-leisure' )
|
||||
,(u'Features' , PREFIX + u'/print-edition/features' )
|
||||
(u'Headlines' , 'http://feeds.feedburner.com/haaretz/LBao' )
|
||||
,(u'Opinion' , 'http://feeds.feedburner.com/haaretz/opinions' )
|
||||
,(u'Defence and diplomacy' , 'http://feeds.feedburner.com/DefenseAndDiplomacy' )
|
||||
,(u'National' , 'http://feeds.feedburner.com/haaretz/National' )
|
||||
,(u'International' , 'http://feeds.feedburner.com/InternationalRss' )
|
||||
,(u'Jewish World' , 'http://feeds.feedburner.com/JewishWorldRss' )
|
||||
,(u'Business' , 'http://feeds.feedburner.com/BusinessPrintRss' )
|
||||
,(u'Real Estate' , 'http://feeds.feedburner.com/RealEstatePrintRss' )
|
||||
,(u'Features' , 'http://feeds.feedburner.com/FeaturesPrintRss' )
|
||||
,(u'Arts & Leisure' , 'http://feeds.feedburner.com/ArtsAndLeisureRss' )
|
||||
,(u'Books' , 'http://www.haaretz.com/cmlink/books-rss-1.264947?localLinksEnabled=false')
|
||||
,(u'Food & Wine' , 'http://feeds.feedburner.com/FoodAndWinePrintRss' )
|
||||
,(u'Sports' , 'http://feeds.feedburner.com/haaretz/Sport' )
|
||||
]
|
||||
|
||||
def get_browser(self):
|
||||
br = BasicNewsRecipe.get_browser()
|
||||
br.open(self.PREFIX)
|
||||
if self.username is not None and self.password is not None:
|
||||
data = urllib.urlencode({ 'cb':'parseEngReply'
|
||||
,'newsso':'true'
|
||||
,'fromlogin':'true'
|
||||
,'layer':'eng_login'
|
||||
,'userName':self.username
|
||||
,'password':self.password
|
||||
})
|
||||
br.open('https://sso.haaretz.com/sso/sso/signIn',data)
|
||||
return br
|
||||
|
||||
def get_article_url(self, article):
|
||||
url = BasicNewsRecipe.get_article_url(self, article)
|
||||
return self.browser.open_novisit(url).geturl()
|
||||
|
||||
def print_version(self, url):
|
||||
article = url.rpartition('/')[2]
|
||||
return 'http://www.haaretz.com/misc/article-print-page/' + article
|
||||
|
||||
def parse_index(self):
|
||||
totalfeeds = []
|
||||
lfeeds = self.get_feeds()
|
||||
for feedobj in lfeeds:
|
||||
feedtitle, feedurl = feedobj
|
||||
self.report_progress(0, _('Fetching feed')+' %s...'%(feedtitle if feedtitle else feedurl))
|
||||
articles = []
|
||||
soup = self.index_to_soup(feedurl)
|
||||
for item in soup.findAll(attrs={'class':'text'}):
|
||||
sp = item.find('span',attrs={'class':'h3 font-weight-normal'})
|
||||
desc = item.find('p')
|
||||
description = ''
|
||||
if sp:
|
||||
if desc:
|
||||
description = self.tag_to_string(desc)
|
||||
link = sp.a
|
||||
url = self.PREFIX + link['href']
|
||||
title = self.tag_to_string(link)
|
||||
times = strftime('%a, %d %b %Y %H:%M:%S +0000',gmtime())
|
||||
articles.append({
|
||||
'title' :title
|
||||
,'date' :times
|
||||
,'url' :url
|
||||
,'description':description
|
||||
})
|
||||
totalfeeds.append((feedtitle, articles))
|
||||
return totalfeeds
|
||||
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
for item in soup.findAll(style=True):
|
||||
del item['style']
|
||||
return soup
|
||||
def preprocess_raw_html(self, raw, url):
|
||||
return '<html><head>'+raw[raw.find('</head>'):]
|
||||
|
@ -8,15 +8,21 @@ class Historia_org_pl(BasicNewsRecipe):
|
||||
category = 'history'
|
||||
language = 'pl'
|
||||
oldest_article = 8
|
||||
remove_empty_feeds=True
|
||||
remove_empty_feeds= True
|
||||
no_stylesheets = True
|
||||
use_embedded_content = True
|
||||
max_articles_per_feed = 100
|
||||
|
||||
feeds = [(u'Wszystkie', u'http://www.historia.org.pl/index.php?format=feed&type=rss'),
|
||||
(u'Wiadomości', u'http://www.historia.org.pl/index.php/wiadomosci.feed?type=rss'),
|
||||
(u'Publikacje', u'http://www.historia.org.pl/index.php/publikacje.feed?type=rss'),
|
||||
(u'Publicystyka', u'http://www.historia.org.pl/index.php/publicystyka.feed?type=rss'),
|
||||
(u'Recenzje', u'http://historia.org.pl/index.php/recenzje.feed?type=rss'),
|
||||
(u'Kultura i sztuka', u'http://www.historia.org.pl/index.php/kultura-i-sztuka.feed?type=rss'),
|
||||
(u'Rekonstykcje', u'http://www.historia.org.pl/index.php/rekonstrukcje.feed?type=rss'),
|
||||
(u'Projekty', u'http://www.historia.org.pl/index.php/projekty.feed?type=rss'),
|
||||
(u'Konkursy'), (u'http://www.historia.org.pl/index.php/konkursy.feed?type=rss')]
|
||||
feeds = [(u'Wszystkie', u'http://www.historia.org.pl/index.php?format=feed&type=atom'),
|
||||
(u'Wiadomości', u'http://www.historia.org.pl/index.php/wiadomosci.feed?type=atom'),
|
||||
(u'Publikacje', u'http://www.historia.org.pl/index.php/publikacje.feed?type=atom'),
|
||||
(u'Publicystyka', u'http://www.historia.org.pl/index.php/publicystyka.feed?type=atom'),
|
||||
(u'Recenzje', u'http://historia.org.pl/index.php/recenzje.feed?type=atom'),
|
||||
(u'Kultura i sztuka', u'http://www.historia.org.pl/index.php/kultura-i-sztuka.feed?type=atom'),
|
||||
(u'Rekonstykcje', u'http://www.historia.org.pl/index.php/rekonstrukcje.feed?type=atom'),
|
||||
(u'Projekty', u'http://www.historia.org.pl/index.php/projekty.feed?type=atom'),
|
||||
(u'Konkursy'), (u'http://www.historia.org.pl/index.php/konkursy.feed?type=atom')]
|
||||
|
||||
|
||||
def print_version(self, url):
|
||||
return url + '?tmpl=component&print=1&layout=default&page='
|
BIN
recipes/icons/natemat_pl.png
Normal file
BIN
recipes/icons/natemat_pl.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 326 B |
BIN
recipes/icons/wirtualnemedia_pl.png
Normal file
BIN
recipes/icons/wirtualnemedia_pl.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 312 B |
@ -1,9 +1,13 @@
|
||||
#v2 2011-07-25
|
||||
# Calibre recipe for Instapaper.com (Stable version)
|
||||
#
|
||||
# Homepage: http://khromov.wordpress.com/projects/instapaper-calibre-recipe/
|
||||
# Code Repository: https://bitbucket.org/khromov/calibre-instapaper
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class AdvancedUserRecipe1299694372(BasicNewsRecipe):
|
||||
title = u'Instapaper'
|
||||
__author__ = 'Darko Miletic, Stanislav Khromov'
|
||||
__author__ = 'Darko Miletic, Stanislav Khromov, Jim Ramsay'
|
||||
publisher = 'Instapaper.com'
|
||||
category = 'info, custom, Instapaper'
|
||||
oldest_article = 365
|
||||
@ -11,25 +15,27 @@ class AdvancedUserRecipe1299694372(BasicNewsRecipe):
|
||||
no_stylesheets = True
|
||||
remove_javascript = True
|
||||
remove_tags = [
|
||||
dict(name='div', attrs={'id':'text_controls_toggle'})
|
||||
,dict(name='script')
|
||||
,dict(name='div', attrs={'id':'text_controls'})
|
||||
,dict(name='div', attrs={'id':'editing_controls'})
|
||||
,dict(name='div', attrs={'class':'bar bottom'})
|
||||
dict(name='div', attrs={'id':'text_controls_toggle'})
|
||||
,dict(name='script')
|
||||
,dict(name='div', attrs={'id':'text_controls'})
|
||||
,dict(name='div', attrs={'id':'editing_controls'})
|
||||
,dict(name='div', attrs={'class':'bar bottom'})
|
||||
,dict(name='div', attrs={'id':'controlbar_container'})
|
||||
,dict(name='div', attrs={'id':'footer'})
|
||||
]
|
||||
]
|
||||
use_embedded_content = False
|
||||
needs_subscription = True
|
||||
INDEX = u'http://www.instapaper.com'
|
||||
LOGIN = INDEX + u'/user/login'
|
||||
|
||||
|
||||
feeds = [
|
||||
(u'Instapaper Unread', u'http://www.instapaper.com/u'),
|
||||
(u'Instapaper Starred', u'http://www.instapaper.com/starred')
|
||||
]
|
||||
|
||||
#Adds the title tag to the body of the recipe. Use this if your articles miss headings.
|
||||
add_title_tag = False;
|
||||
|
||||
def get_browser(self):
|
||||
br = BasicNewsRecipe.get_browser()
|
||||
if self.username is not None:
|
||||
@ -67,7 +73,10 @@ class AdvancedUserRecipe1299694372(BasicNewsRecipe):
|
||||
article.title = soup.find('title').contents[0].strip()
|
||||
|
||||
def postprocess_html(self, soup, first_fetch):
|
||||
for link_tag in soup.findAll(attrs={"id" : "story"}):
|
||||
link_tag.insert(0,'<h1>'+soup.find('title').contents[0].strip()+'</h1>')
|
||||
#adds the title to each story, as it is not always included
|
||||
if self.add_title_tag:
|
||||
for link_tag in soup.findAll(attrs={"id" : "story"}):
|
||||
link_tag.insert(0,'<h1>'+soup.find('title').contents[0].strip()+'</h1>')
|
||||
|
||||
#print repr(soup)
|
||||
return soup
|
||||
|
@ -8,6 +8,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class AdvancedUserRecipe1324038402(BasicNewsRecipe):
|
||||
title = u'La Gazzetta del Mezzogiorno'
|
||||
language = 'it'
|
||||
__author__ = 'faber1971'
|
||||
description = 'Italian regional magazine - Apulia'
|
||||
oldest_article = 1
|
||||
|
77
recipes/malaya_business_insight.recipe
Normal file
77
recipes/malaya_business_insight.recipe
Normal file
@ -0,0 +1,77 @@
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
import time
|
||||
class MalayaBusinessInsight(BasicNewsRecipe):
|
||||
title = u'Malaya Business Insight'
|
||||
custom_title = "Malaya Business Insight - " + time.strftime('%d %b %Y %I:%M %p')
|
||||
__author__ = 'jde'
|
||||
__date__ = '07 June 2012'
|
||||
__version__ = '1.2'
|
||||
description = "The Malaya Business Insight is a broadsheet newspaper in the Philippines. The newspaper's name was derived from the Filipino word that means 'freedom'."
|
||||
language = 'en_PH'
|
||||
publisher = 'Malaya Business Insight'
|
||||
category = 'news, Philippines'
|
||||
tags = 'news, Philippines'
|
||||
cover_url = 'http://www.malaya.com.ph/templates/ja_teline_iv/images/logo.png'
|
||||
masthead_url = 'http://www.malaya.com.ph/templates/ja_teline_iv/images/logo.png'
|
||||
oldest_article = 1.5 #days
|
||||
max_articles_per_feed = 25
|
||||
simultaneous_downloads = 20
|
||||
publication_type = 'newspaper'
|
||||
timefmt = ' [%a, %d %b %Y %I:%M %p]'
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
encoding = None
|
||||
recursions = 0
|
||||
needs_subscription = False
|
||||
remove_javascript = True
|
||||
remove_empty_feeds = True
|
||||
auto_cleanup = False
|
||||
|
||||
|
||||
keep_only_tags = [
|
||||
dict(name='div', attrs={'id':'ja-main'})
|
||||
]
|
||||
|
||||
remove_tags = [
|
||||
dict(name='a', attrs={'class':'ja-back-btn'})
|
||||
,dict(name='li', attrs={'class':'print-icon'})
|
||||
,dict(name='li', attrs={'class':'email-icon'})
|
||||
,dict(name='p', attrs={'class':'dnn'})
|
||||
,dict(name='span', attrs={'class':'breadcrumbs pathway'})
|
||||
,dict(name='dt', attrs={'class':'article-info-term'})
|
||||
,dict(name='div', attrs={'class':'ja-articles-mainwrap'})
|
||||
,dict(name='h1', attrs={'class':'componentheading'})
|
||||
,dict(name='div', attrs={'id':'ja-content-mass-top'})
|
||||
]
|
||||
|
||||
|
||||
|
||||
conversion_options = { 'title' : custom_title,
|
||||
'comments' : description,
|
||||
'tags' : tags,
|
||||
'language' : language,
|
||||
'publisher' : publisher,
|
||||
'authors' : publisher,
|
||||
'smarten_punctuation' : True
|
||||
}
|
||||
|
||||
|
||||
|
||||
feeds = [
|
||||
(u'Business', u'http://www.malaya.com.ph/index.php/business?format=feed&type=rss')
|
||||
, (u'Market', u'http://www.malaya.com.ph/index.php/business/market?format=feed&type=rss')
|
||||
, (u'Shipping and Transportation', u'http://www.malaya.com.ph/index.php/business/shipping-and-transportation?format=feed&type=rss')
|
||||
, (u'Business Incidental', u'http://www.malaya.com.ph/index.php/business/business-incidental?format=feed&type=rss')
|
||||
, (u'Banking and Finance', u'http://www.malaya.com.ph/index.php/special-features/banking-and-finance?format=feed&type=rss')
|
||||
, (u'Motoring', u'http://www.malaya.com.ph/index.php/special-features/motoring?format=feed&type=rss')
|
||||
, (u'Info Tech - Telecoms', u'http://www.malaya.com.ph/index.php/special-features/infotech-telecoms?format=feed&type=rss')
|
||||
, (u'Property', u'http://www.malaya.com.ph/index.php/special-features/property?format=feed&type=rss')
|
||||
, (u'Environment', u'http://www.malaya.com.ph/index.php/special-features/environment?format=feed&type=rss')
|
||||
, (u'Agriculture', u'http://www.malaya.com.ph/index.php/special-features/agriculture?format=feed&type=rss')
|
||||
, (u'News - National', u'http://www.malaya.com.ph/index.php/news/nation?format=feed&type=rss')
|
||||
, (u'News - International', u'http://www.malaya.com.ph/index.php/news/international?format=feed&type=rss')
|
||||
, (u'Sports', u'http://www.malaya.com.ph/index.php/sports?format=feed&type=rss')
|
||||
, (u'Entertainment', u'http://www.malaya.com.ph/index.php/entertainment?format=feed&type=rss')
|
||||
, (u'Living', u'http://www.malaya.com.ph/index.php/living?format=feed&type=rss')
|
||||
, (u'Opinion', u'http://www.malaya.com.ph/index.php/opinion?format=feed&type=rss')
|
||||
]
|
54
recipes/manila_standard_today.recipe
Normal file
54
recipes/manila_standard_today.recipe
Normal file
@ -0,0 +1,54 @@
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
import time
|
||||
class ManilaStandardToday(BasicNewsRecipe):
|
||||
title = u'Manila Standard Today'
|
||||
custom_title = "Manila Standard Today - " + time.strftime('%d %b %Y %I:%M %p')
|
||||
__author__ = 'jde'
|
||||
__date__ = '06 June 2012'
|
||||
__version__ = '1.0'
|
||||
description = 'The Manila Standard Today is the fourth-largest broadsheet newspaper in the Philippines as of 2006. Initially established as the Manila Standard, it merged with another newspaper of record, Today, on March 6, 2005. It was the first newspaper merger in the Philippines.'
|
||||
language = 'en_PH'
|
||||
publisher = 'Manila Standard Today'
|
||||
category = 'news, Philippines'
|
||||
tags = 'news, Philippines'
|
||||
cover_url = 'http://www.manilastandardtoday.com/wp-content/uploads/Manila-Standard-Today-June-06-12.jpg'
|
||||
masthead_url = 'http://www.manilastandardtoday.com/wp-content/uploads/Manila-Standard-Today-June-06-12.jpg'
|
||||
oldest_article = 1.5 #days
|
||||
max_articles_per_feed = 25
|
||||
simultaneous_downloads = 20
|
||||
publication_type = 'newspaper'
|
||||
timefmt = ' [%a, %d %b %Y %I:%M %p]'
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
encoding = None
|
||||
recursions = 0
|
||||
needs_subscription = False
|
||||
remove_javascript = True
|
||||
remove_empty_feeds = True
|
||||
auto_cleanup = False
|
||||
|
||||
|
||||
keep_only_tags = [
|
||||
dict(name='div', attrs={'id':'main'})
|
||||
]
|
||||
|
||||
conversion_options = { 'title' : custom_title,
|
||||
'comments' : description,
|
||||
'tags' : tags,
|
||||
'language' : language,
|
||||
'publisher' : publisher,
|
||||
'authors' : publisher,
|
||||
'smarten_punctuation' : True
|
||||
}
|
||||
|
||||
|
||||
feeds = [
|
||||
(u'Headlines', u'http://news.manilastandardtoday.com/feed/')
|
||||
, (u'Nation', u'http://news.manilastandardtoday.com/archives/nation/feed/')
|
||||
, (u'Business', u'http://business.manilastandardtoday.com/feed/')
|
||||
, (u'Metro', u'http://news.manilastandardtoday.com/archives/metro/feed/')
|
||||
, (u'Sports', u'http://sports.manilastandardtoday.com/feed/')
|
||||
, (u'Entertainment', u'http://entertainment.manilastandardtoday.com/feed/')
|
||||
, (u'Opinion', u'http://opinion.manilastandardtoday.com/feed/')
|
||||
, (u'Lifestyle', u'http://lifestyle.manilastandardtoday.com/feed/')
|
||||
]
|
@ -17,5 +17,5 @@ class AdvancedUserRecipe1327062445(BasicNewsRecipe):
|
||||
remove_tags = [
|
||||
dict(name='ul', attrs={'id':'ads0'})
|
||||
]
|
||||
masthead_url = 'http://www.simrendeogun.com/wp-content/uploads/2011/06/New-Marketing-Magazine-Logo.jpg'
|
||||
feeds = [(u'My Marketing', u'http://feed43.com/0537744466058428.xml'), (u'My Marketing_', u'http://feed43.com/8126723074604845.xml'), (u'MarketingArena', u'http://feeds.feedburner.com/marketingarena'), (u'Marketing Journal', u'http://feeds.feedburner.com/marketingjournal/jPwA'), (u'Venturini', u'http://robertoventurini.blogspot.com/feeds/posts/default?alt=rss'), (u'Brandforum news', u'http://www.brandforum.it/rss/news'), (u'Brandforum papers', u'http://www.brandforum.it/rss/papers'), (u'minimarketing', u'http://feeds.feedburner.com/minimarketingit'), (u'[4]marketing.biz', u'http://feeds.feedburner.com/4marketing'), (u'Ninja Marketing', u'http://feeds.feedburner.com/NinjaMarketing'), (u'Bloguerrilla', u'http://feeds.feedburner.com/Bloguerrilla'), (u'Nonconvenzionale', u'http://feeds.feedburner.com/nonconvenzionale'), (u'Comunitàzione', u'http://www.comunitazione.it/feed/novita.asp'), (u'Disambiguando', u'http://giovannacosenza.wordpress.com/feed/')]
|
||||
masthead_url = 'http://www.linkedin-marketing.it/blog/wp-content/uploads/2012/03/pb.jpg'
|
||||
feeds = [(u'MarketingArena', u'http://feeds.feedburner.com/marketingarena'), (u'My Marketing', u'http://feed43.com/0537744466058428.xml'), (u'My Marketing_', u'http://feed43.com/8126723074604845.xml'), (u'Marketing Journal', u'http://feeds.feedburner.com/marketingjournal/jPwA'), (u'Venturini', u'http://robertoventurini.blogspot.com/feeds/posts/default?alt=rss'), (u'Brandforum news', u'http://www.brandforum.it/rss/news'), (u'Brandforum papers', u'http://www.brandforum.it/rss/papers'), (u'SintBlog', u'http://sint-blog.blogspot.com/feeds/posts/default'), (u'Ninja Marketing', u'http://feeds.feedburner.com/NinjaMarketing'), (u'Bloguerrilla', u'http://feeds.feedburner.com/Bloguerrilla'), (u'Nonconvenzionale', u'http://feeds.feedburner.com/nonconvenzionale'), (u'Disambiguando', u'http://giovannacosenza.wordpress.com/feed/')]
|
||||
|
@ -1,18 +0,0 @@
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
import re
|
||||
class naczytniki(BasicNewsRecipe):
|
||||
title = u'naczytniki.pl'
|
||||
__author__ = 'fenuks'
|
||||
masthead_url= 'http://naczytniki.pl/wp-content/uploads/2010/08/logo_nc28.png'
|
||||
cover_url = 'http://naczytniki.pl/wp-content/uploads/2010/08/logo_nc28.png'
|
||||
language = 'pl'
|
||||
description ='everything about e-readers'
|
||||
category='e-readers'
|
||||
no_stylesheets=True
|
||||
use_embedded_content=False
|
||||
oldest_article = 7
|
||||
max_articles_per_feed = 100
|
||||
preprocess_regexps = [(re.compile(ur'<p><br><b>Zobacz także:</b></p>.*?</body>', re.DOTALL), lambda match: '</body>') ]
|
||||
keep_only_tags=[dict(name='div', attrs={'class':'post'})]
|
||||
remove_tags=[dict(name='span', attrs={'class':'comments'}), dict(name='div', attrs={'class':'sociable'})]
|
||||
feeds = [(u'Wpisy', u'http://naczytniki.pl/?feed=rss2')]
|
15
recipes/natemat_pl.recipe
Normal file
15
recipes/natemat_pl.recipe
Normal file
@ -0,0 +1,15 @@
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class NaTemat(BasicNewsRecipe):
|
||||
title = u'NaTemat.pl'
|
||||
oldest_article = 7
|
||||
max_articles_per_feed = 100
|
||||
__author__ = 'fenuks'
|
||||
description = u'informacje, komentarze, opinie'
|
||||
category = 'news'
|
||||
language = 'pl'
|
||||
cover_url= 'http://blog.plona.pl/wp-content/uploads/2012/05/natemat.png'
|
||||
no_stylesheets = True
|
||||
keep_only_tags= [dict(id='main')]
|
||||
remove_tags= [dict(attrs={'class':['button', 'block-inside style_default', 'article-related']})]
|
||||
feeds = [(u'Artyku\u0142y', u'http://natemat.pl/rss/wszystkie')]
|
@ -11,7 +11,7 @@ class OCLab(BasicNewsRecipe):
|
||||
no_stylesheets = True
|
||||
keep_only_tags=[dict(id='main')]
|
||||
remove_tags_after= dict(attrs={'class':'single-postmetadata'})
|
||||
remove_tags=[dict(attrs={'class':['single-postmetadata', 'pagebar']})]
|
||||
remove_tags=[dict(attrs={'class':['single-postmetadata', 'pagebar', 'shr-bookmarks shr-bookmarks-expand shr-bookmarks-center shr-bookmarks-bg-enjoy']})]
|
||||
feeds = [(u'Wpisy', u'http://oclab.pl/feed/')]
|
||||
|
||||
|
||||
|
73
recipes/philippino_star_ngayon.recipe
Normal file
73
recipes/philippino_star_ngayon.recipe
Normal file
@ -0,0 +1,73 @@
|
||||
|
||||
'''
|
||||
www.philstar.com
|
||||
'''
|
||||
|
||||
import time
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
|
||||
class PilipinoStarNgayon(BasicNewsRecipe):
|
||||
title = 'Pilipino Star Ngayon'
|
||||
custom_title = "Pilipino Star Ngayon - " + time.strftime('%d %b %Y %I:%M %p')
|
||||
__author__ = 'jde'
|
||||
__date__ = '31 May 2012'
|
||||
__version__ = '1.0'
|
||||
description = 'A daily Tabloid written in Tagalog, distributed in the Philippines. A tabloid style newspaper published in the national language - philstar.com is a Philippine news and entertainment portal for the Filipino global community. It is the online presence of the STAR Group of Publications, a leading publisher of newspapers and magazines in the Philippines.'
|
||||
language = 'tgl'
|
||||
publisher = 'The Philippine STAR'
|
||||
category = 'news, Philippines'
|
||||
tags = 'news, Philippines'
|
||||
cover_url = 'http://www.philstar.com/images/logo_PSN.jpg'
|
||||
masthead_url = 'http://www.philstar.com/images/logo_PSN.jpg'
|
||||
oldest_article = 1.5 #days
|
||||
max_articles_per_feed = 25
|
||||
simultaneous_downloads = 10
|
||||
publication_type = 'newspaper'
|
||||
timefmt = ' [%a, %d %b %Y %I:%M %p]'
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
encoding = None
|
||||
recursions = 0
|
||||
needs_subscription = False
|
||||
remove_javascript = True
|
||||
remove_empty_feeds = True
|
||||
auto_cleanup = False
|
||||
|
||||
remove_tags = [dict(name='img', attrs={'id':'Image1'}) #Logo
|
||||
,dict(name='span', attrs={'id':'ControlArticle1_LabelHeader'}) #Section (Headlines, Nation, Metro, ...)
|
||||
,dict(name='a', attrs={'id':'ControlArticle1_FormView1_hlComments'}) #Comments
|
||||
,dict(name='img', attrs={'src':'images/post-comments.jpg'}) #View Comments
|
||||
,dict(name='a', attrs={'id':'ControlArticle1_FormView1_ControlPhotoAndCaption1_hlImageCaption'}) #Zoom
|
||||
]
|
||||
conversion_options = { 'title' : custom_title,
|
||||
'comments' : description,
|
||||
'tags' : tags,
|
||||
'language' : language,
|
||||
'publisher' : publisher,
|
||||
'authors' : publisher,
|
||||
'smarten_punctuation' : True
|
||||
}
|
||||
|
||||
feeds = [
|
||||
('Litra-talk' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=535' )
|
||||
,('Bansa' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=92' )
|
||||
,('Probinsiya' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=49' )
|
||||
,('Metro' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=93' )
|
||||
,('Opinyon' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=94' )
|
||||
,('Palaro' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=95' )
|
||||
,('Showbiz' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=96' )
|
||||
,('True Confessions' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=97' )
|
||||
,('Dr. Love' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=98' )
|
||||
,('Kutob' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=99' )
|
||||
,('Komiks' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=100' )
|
||||
]
|
||||
|
||||
# process the printer friendly version of article
|
||||
def print_version(self, url):
|
||||
return url.replace('/Article', '/ArticlePrinterFriendly')
|
||||
|
||||
# obtain title from printer friendly version of article; avoiding add_toc_thumbnail changing title when article has image
|
||||
def populate_article_metadata(self, article, soup, first):
|
||||
article.title = soup.find('span', {'id': 'ControlArticle1_FormView1_ArticleHeaderLabel'}).contents[0].strip()
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
import re
|
||||
class Polska_times(BasicNewsRecipe):
|
||||
title = u'Polska Times'
|
||||
__author__ = 'fenuks'
|
||||
@ -11,71 +10,20 @@ class Polska_times(BasicNewsRecipe):
|
||||
max_articles_per_feed = 100
|
||||
remove_emty_feeds= True
|
||||
no_stylesheets = True
|
||||
preprocess_regexps = [(re.compile(ur'<b>Czytaj także:.*?</b>', re.DOTALL), lambda match: ''), (re.compile(ur',<b>Czytaj też:.*?</b>', re.DOTALL), lambda match: ''), (re.compile(ur'<b>Zobacz także:.*?</b>', re.DOTALL), lambda match: ''), (re.compile(ur'<center><h4><a.*?</a></h4></center>', re.DOTALL), lambda match: ''), (re.compile(ur'<b>CZYTAJ TEŻ:.*?</b>', re.DOTALL), lambda match: ''), (re.compile(ur'<b>CZYTAJ WIĘCEJ:.*?</b>', re.DOTALL), lambda match: ''), (re.compile(ur'<b>CZYTAJ TAKŻE:.*?</b>', re.DOTALL), lambda match: ''), (re.compile(ur'<b>\* CZYTAJ KONIECZNIE:.*', re.DOTALL), lambda match: '</body>'), (re.compile(ur'<b>Nasze serwisy:</b>.*', re.DOTALL), lambda match: '</body>') ]
|
||||
keep_only_tags= [dict(id=['tytul-artykulu', 'kontent'])]
|
||||
remove_tags_after= dict(id='material-tagi')
|
||||
remove_tags=[dict(attrs={'id':'reklama_srodtekst_0'}), dict(attrs={'id':'material-tagi'}), dict(name='div', attrs={'class':'zakladki'}), dict(attrs={'title':u'CZYTAJ TAKŻE'}), dict(attrs={'id':'podobne'}), dict(name='a', attrs={'href':'http://www.dzienniklodzki.pl/newsletter'})]
|
||||
#preprocess_regexps = [(re.compile(ur'<b>Czytaj także:.*?</b>', re.DOTALL), lambda match: ''), (re.compile(ur',<b>Czytaj też:.*?</b>', re.DOTALL), lambda match: ''), (re.compile(ur'<b>Zobacz także:.*?</b>', re.DOTALL), lambda match: ''), (re.compile(ur'<center><h4><a.*?</a></h4></center>', re.DOTALL), lambda match: ''), (re.compile(ur'<b>CZYTAJ TEŻ:.*?</b>', re.DOTALL), lambda match: ''), (re.compile(ur'<b>CZYTAJ WIĘCEJ:.*?</b>', re.DOTALL), lambda match: ''), (re.compile(ur'<b>CZYTAJ TAKŻE:.*?</b>', re.DOTALL), lambda match: ''), (re.compile(ur'<b>\* CZYTAJ KONIECZNIE:.*', re.DOTALL), lambda match: '</body>'), (re.compile(ur'<b>Nasze serwisy:</b>.*', re.DOTALL), lambda match: '</body>') ]
|
||||
remove_tags_after= dict(attrs={'src':'http://nm.dz.com.pl/dz.png'})
|
||||
remove_tags=[dict(id='mat-podobne'), dict(name='a', attrs={'class':'czytajDalej'}), dict(attrs={'src':'http://nm.dz.com.pl/dz.png'})]
|
||||
feeds = [(u'Fakty', u'http://polskatimes.feedsportal.com/c/32980/f/533648/index.rss'), (u'Opinie', u'http://www.polskatimes.pl/rss/opinie.xml'), (u'Sport', u'http://polskatimes.feedsportal.com/c/32980/f/533649/index.rss'), (u'Pieni\u0105dze', u'http://polskatimes.feedsportal.com/c/32980/f/533657/index.rss'), (u'Twoje finanse', u'http://www.polskatimes.pl/rss/twojefinanse.xml'), (u'Kultura', u'http://polskatimes.feedsportal.com/c/32980/f/533650/index.rss'), (u'Dodatki', u'http://www.polskatimes.pl/rss/dodatki.xml')]
|
||||
|
||||
def print_version(self, url):
|
||||
return url.replace('artykul', 'drukuj')
|
||||
|
||||
def skip_ad_pages(self, soup):
|
||||
if 'Advertisement' in soup.title:
|
||||
nexturl=soup.find('a')['href']
|
||||
return self.index_to_soup(nexturl, raw=True)
|
||||
|
||||
def append_page(self, soup, appendtag):
|
||||
nexturl=soup.find(id='nastepna_strona')
|
||||
while nexturl:
|
||||
soup2= self.index_to_soup(nexturl['href'])
|
||||
nexturl=soup2.find(id='nastepna_strona')
|
||||
pagetext = soup2.find(id='tresc')
|
||||
for dictionary in self.remove_tags:
|
||||
v=pagetext.findAll(attrs=dictionary['attrs'])
|
||||
for delete in v:
|
||||
delete.extract()
|
||||
for b in pagetext.findAll(name='b'):
|
||||
if b.string:
|
||||
if u'CZYTAJ TEŻ' in b.string or u'Czytaj także' in b.string or u'Czytaj też' in b.string or u'Zobacz także' in b.string:
|
||||
b.extract()
|
||||
for center in pagetext.findAll(name='center'):
|
||||
if center.h4:
|
||||
if center.h4.a:
|
||||
center.extract()
|
||||
pos = len(appendtag.contents)
|
||||
appendtag.insert(pos, pagetext)
|
||||
for paginator in appendtag.findAll(attrs={'class':'stronicowanie'}):
|
||||
paginator.extract()
|
||||
|
||||
def image_article(self, soup, appendtag):
|
||||
nexturl=soup.find('a', attrs={'class':'nastepna'})
|
||||
urls=[]
|
||||
while nexturl:
|
||||
if nexturl not in urls:
|
||||
urls.append(nexturl)
|
||||
else:
|
||||
break
|
||||
soup2= self.index_to_soup('http://www.polskatimes.pl/artykul/' + nexturl['href'])
|
||||
nexturl=soup2.find('a', attrs={'class':'nastepna'})
|
||||
if nexturl in urls:
|
||||
break;
|
||||
pagetext = soup2.find(id='galeria-material')
|
||||
pos = len(appendtag.contents)
|
||||
appendtag.insert(pos, '<br />')
|
||||
pos = len(appendtag.contents)
|
||||
appendtag.insert(pos, pagetext)
|
||||
for rem in appendtag.findAll(attrs={'class':['galeriaNawigator', 'miniaturyPojemnik']}):
|
||||
rem.extract()
|
||||
for paginator in appendtag.findAll(attrs={'class':'stronicowanie'}):
|
||||
paginator.extract()
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
if soup.find('a', attrs={'class':'nastepna'}):
|
||||
self.image_article(soup, soup.body)
|
||||
elif soup.find(id='nastepna_strona'):
|
||||
self.append_page(soup, soup.body)
|
||||
return soup
|
||||
|
||||
|
||||
def get_cover_url(self):
|
||||
soup = self.index_to_soup('http://www.prasa24.pl/gazeta/metropolia-warszawska/')
|
||||
self.cover_url=soup.find(id='pojemnik').img['src']
|
||||
return getattr(self, 'cover_url', self.cover_url)
|
||||
return getattr(self, 'cover_url', self.cover_url)
|
||||
|
70
recipes/the_freeman.recipe
Normal file
70
recipes/the_freeman.recipe
Normal file
@ -0,0 +1,70 @@
|
||||
|
||||
'''
|
||||
www.philstar.com
|
||||
'''
|
||||
|
||||
import time
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
|
||||
class Freeman(BasicNewsRecipe):
|
||||
title = 'The Freeman'
|
||||
custom_title = "The Freeman - " + time.strftime('%d %b %Y %I:%M %p')
|
||||
__author__ = 'jde'
|
||||
__date__ = '31 May 2012'
|
||||
__version__ = '1.0'
|
||||
description = 'The Freeman is a daily English-language newspaper published in Cebu, Philippines, by the Philippine Star. It was the first newspaper in Cebu, first published in May 1919. The motto of the newspaper is "The fair and fearless" - philstar.com is a Philippine news and entertainment portal for the Filipino global community. It is the online presence of the STAR Group of Publications, a leading publisher of newspapers and magazines in the Philippines.'
|
||||
language = 'en_PH'
|
||||
publisher = 'The Philippine STAR'
|
||||
category = 'news, Philippines'
|
||||
tags = 'news, Philippines'
|
||||
cover_url = 'http://www.philstar.com/images/logo_Freeman.jpg'
|
||||
masthead_url = 'http://www.philstar.com/images/logo_Freeman.jpg'
|
||||
oldest_article = 1.5 #days
|
||||
max_articles_per_feed = 25
|
||||
simultaneous_downloads = 10
|
||||
publication_type = 'newspaper'
|
||||
timefmt = ' [%a, %d %b %Y %I:%M %p]'
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
encoding = None
|
||||
recursions = 0
|
||||
needs_subscription = False
|
||||
remove_javascript = True
|
||||
remove_empty_feeds = True
|
||||
auto_cleanup = False
|
||||
|
||||
remove_tags = [dict(name='img', attrs={'id':'Image1'}) #Logo
|
||||
,dict(name='span', attrs={'id':'ControlArticle1_LabelHeader'}) #Section (Headlines, Nation, Metro, ...)
|
||||
,dict(name='a', attrs={'id':'ControlArticle1_FormView1_hlComments'}) #Comments
|
||||
,dict(name='img', attrs={'src':'images/post-comments.jpg'}) #View Comments
|
||||
,dict(name='a', attrs={'id':'ControlArticle1_FormView1_ControlPhotoAndCaption1_hlImageCaption'}) #Zoom
|
||||
]
|
||||
conversion_options = { 'title' : custom_title,
|
||||
'comments' : description,
|
||||
'tags' : tags,
|
||||
'language' : language,
|
||||
'publisher' : publisher,
|
||||
'authors' : publisher,
|
||||
'smarten_punctuation' : True
|
||||
}
|
||||
|
||||
feeds = [
|
||||
('Cebu News' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=107' )
|
||||
,('Freeman Opinion' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=109' )
|
||||
,('Metro Cebu' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=531' )
|
||||
,('Region' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=530' )
|
||||
,('Cebu Business' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=108' )
|
||||
,('Cebu Sports' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=110' )
|
||||
,('Cebu Lifestyle' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=111' )
|
||||
,('Cebu Entertainment' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=51' )
|
||||
]
|
||||
|
||||
# process the printer friendly version of article
|
||||
def print_version(self, url):
|
||||
return url.replace('/Article', '/ArticlePrinterFriendly')
|
||||
|
||||
# obtain title from printer friendly version of article; avoiding add_toc_thumbnail changing title when article has image
|
||||
def populate_article_metadata(self, article, soup, first):
|
||||
article.title = soup.find('span', {'id': 'ControlArticle1_FormView1_ArticleHeaderLabel'}).contents[0].strip()
|
||||
|
||||
|
88
recipes/the_manila_bulletin.recipe
Normal file
88
recipes/the_manila_bulletin.recipe
Normal file
@ -0,0 +1,88 @@
|
||||
import time
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
|
||||
class TheManilaBulletin(BasicNewsRecipe):
|
||||
title = u'The Manila Bulletin'
|
||||
custom_title = "The Manila Bulletin - " + time.strftime('%d %b %Y %I:%M %p')
|
||||
__author__ = 'jde'
|
||||
__date__ = '06 June 2012'
|
||||
__version__ = '1.0'
|
||||
description = "The Manila Bulletin, (also known as the Bulletin and previously known as the Manila Daily Bulletin and the Bulletin Today) is the Philippines' largest broadsheet newspaper by circulation."
|
||||
language = 'en_PH'
|
||||
publisher = 'The Manila Bulletin'
|
||||
category = 'news, Philippines'
|
||||
tags = 'news, Philippines'
|
||||
cover_url = 'http://www.mb.com.ph/sites/default/files/mb_logo.jpg'
|
||||
masthead_url = 'http://www.mb.com.ph/sites/default/files/mb_logo.jpg'
|
||||
oldest_article = 1.5 #days
|
||||
max_articles_per_feed = 25
|
||||
simultaneous_downloads = 20
|
||||
publication_type = 'newspaper'
|
||||
timefmt = ' [%a, %d %b %Y %I:%M %p]'
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
encoding = None
|
||||
recursions = 0
|
||||
needs_subscription = False
|
||||
remove_javascript = True
|
||||
remove_empty_feeds = True
|
||||
|
||||
keep_only_tags = [
|
||||
dict(name='div', attrs={'class':'article node'})
|
||||
,dict(name='div', attrs={'class':'label'})
|
||||
,dict(name='div', attrs={'class':'content clear-block'})
|
||||
]
|
||||
|
||||
remove_tags = [
|
||||
dict(name='li', attrs={'class':'print_html'})
|
||||
,dict(name='li', attrs={'class':'print_html first'})
|
||||
,dict(name='li', attrs={'class':'print_mail'})
|
||||
,dict(name='li', attrs={'class':'print_mail last'})
|
||||
,dict(name='div', attrs={'class':'article-sidebar'})
|
||||
,dict(name='table', attrs={'id':'attachments'})
|
||||
]
|
||||
|
||||
auto_cleanup = False
|
||||
|
||||
|
||||
conversion_options = { 'title' : custom_title,
|
||||
'comments' : description,
|
||||
'tags' : tags,
|
||||
'language' : language,
|
||||
'publisher' : publisher,
|
||||
'authors' : publisher,
|
||||
'smarten_punctuation' : True
|
||||
}
|
||||
|
||||
feeds = [
|
||||
(u'Main News', u'http://www.mb.com.ph/feed/news/main')
|
||||
# , (u'Regional', u'http://www.mb.com.ph/feed/news/regional')
|
||||
, (u'Business', u'http://www.mb.com.ph/feed/business')
|
||||
, (u'Sports', u'http://www.mb.com.ph/feed/sports')
|
||||
, (u'Entertainment', u'http://www.mb.com.ph/feed/entertainment')
|
||||
, (u'Opinion', u'http://www.mb.com.ph/feed/news/opinion')
|
||||
# , (u'Agriculture', u'http://www.mb.com.ph/feed/news/agriculture')
|
||||
# , (u'Environment', u'http://www.mb.com.ph/feed/news/environment')
|
||||
, (u'Technology', u'http://www.mb.com.ph/feed/lifestyle/technology')
|
||||
, (u'Lifestyle', u'http://www.mb.com.ph/feed/lifestyle')
|
||||
# , (u'Arts & Living', u'http://www.mb.com.ph/feed/lifestyle/arts-and-living')
|
||||
# , (u'Drive', u'http://www.mb.com.ph/feed/lifestyle/drive')
|
||||
# , (u'Food', u'http://www.mb.com.ph/feed/lifestyle/food')
|
||||
# , (u'Travel', u'http://www.mb.com.ph/feed/lifestyle/travel')
|
||||
# , (u'Picture Perfect', u'http://www.mb.com.ph/feed/lifestyle/picture-perfect')
|
||||
]
|
||||
|
||||
|
||||
# if use print version - convert url
|
||||
# http://www.mb.com.ph/articles/361252/higher-power-rate-looms
|
||||
# http://www.mb.com.ph/print/361252
|
||||
#
|
||||
# def print_version(self,url):
|
||||
# segments = url.split('/')
|
||||
# printURL = '/'.join(segments[0:3]) + '/print/' + '/'.join(segments[5])
|
||||
# return printURL
|
||||
|
||||
|
||||
|
||||
|
||||
|
55
recipes/the_manila_times.recipe
Normal file
55
recipes/the_manila_times.recipe
Normal file
@ -0,0 +1,55 @@
|
||||
import time
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
|
||||
class TheManilaTimes(BasicNewsRecipe):
|
||||
title = u'The Manila Times'
|
||||
custom_title = "The Manila Times - " + time.strftime('%d %b %Y %I:%M %p')
|
||||
__author__ = 'jde'
|
||||
__date__ = '06 June 2012'
|
||||
__version__ = '1.0'
|
||||
description = 'The Manila Times is the oldest existing English language newspaper in the Philippines.'
|
||||
language = 'en_PH'
|
||||
publisher = 'The Manila Times'
|
||||
category = 'news, Philippines'
|
||||
tags = 'news, Philippines'
|
||||
cover_url = 'http://www.manilatimes.net/images/banners/logo-mt.png'
|
||||
masthead_url = 'http://www.manilatimes.net/images/banners/logo-mt.png'
|
||||
oldest_article = 1.5 #days
|
||||
max_articles_per_feed = 25
|
||||
simultaneous_downloads = 20
|
||||
publication_type = 'newspaper'
|
||||
timefmt = ' [%a, %d %b %Y %I:%M %p]'
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
encoding = None
|
||||
recursions = 0
|
||||
needs_subscription = False
|
||||
remove_javascript = True
|
||||
remove_empty_feeds = True
|
||||
|
||||
|
||||
|
||||
remove_tags = [
|
||||
dict(name='img', attrs={'alt':'Print'})
|
||||
,dict(name='img', attrs={'alt':'Email:'})
|
||||
,dict(name='dd', attrs={'class':'hits'})
|
||||
]
|
||||
|
||||
|
||||
auto_cleanup = True
|
||||
|
||||
|
||||
conversion_options = { 'title' : custom_title,
|
||||
'comments' : description,
|
||||
'tags' : tags,
|
||||
'language' : language,
|
||||
'publisher' : publisher,
|
||||
'authors' : publisher,
|
||||
'smarten_punctuation' : True
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
feeds = [(u'Breaking News', u'http://www.manilatimes.net/index.php/news/breaking-news?format=feed&type=rss'), (u'Top Stories', u'http://www.manilatimes.net/index.php/news/top-stories?format=feed&type=rss'), (u'Headlines', u'http://www.manilatimes.net/index.php/news/headlines-mt?format=feed&type=rss'), (u'Nation', u'http://www.manilatimes.net/index.php/news/nation?format=feed&type=rss'), (u'Regions', u'http://www.manilatimes.net/index.php/news/regions?format=feed&type=rss'), (u'World', u'http://www.manilatimes.net/index.php/news/world?format=feed&type=rss'), (u'Top Business News', u'http://www.manilatimes.net/index.php/business/top-business-news?format=feed&type=rss'), (u'Business Columnist', u'http://www.manilatimes.net/index.php/business/business-columnist?format=feed&type=rss'), (u'Opinion - Editorials', u'http://www.manilatimes.net/index.php/opinion/editorials?format=feed&type=rss'), (u'Opinion - Columnist', u'http://www.manilatimes.net/index.php/opinion/columnist1?format=feed&type=rss'), (u'Opinion - Editorial Cartoon', u'http://www.manilatimes.net/index.php/opinion/editorial-cartoon?format=feed&type=rss'), (u'Top Sports News', u'http://www.manilatimes.net/index.php/sports/top-sports-news?format=feed&type=rss'), (u'Sports Columnist', u'http://www.manilatimes.net/index.php/sports/sports-columnist?format=feed&type=rss'), (u'Life & Times', u'http://www.manilatimes.net/index.php/life-and-times?format=feed&type=rss'), (u'Showtime', u'http://www.manilatimes.net/index.php/life-and-times/showtime?format=feed&type=rss'), (u'Sunday Times', u'http://www.manilatimes.net/index.php/sunday-times?format=feed&type=rss'), (u'Sunday Times Magazine', u'http://www.manilatimes.net/index.php/sunday-times/the-sunday-times-magazines?format=feed&type=rss'), (u'Motoring News', u'http://www.manilatimes.net/index.php/fast-times/motoring-news?format=feed&type=rss'), (u'Motoring Columnist', u'http://www.manilatimes.net/index.php/fast-times/motoring-columnist?format=feed&type=rss'), (u'Technology', u'http://www.manilatimes.net/index.php/technology?format=feed&type=rss')]
|
129
recipes/the_philippine_daily_inquirer.recipe
Normal file
129
recipes/the_philippine_daily_inquirer.recipe
Normal file
@ -0,0 +1,129 @@
|
||||
import time
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
|
||||
class PhilippineDailyInquirer(BasicNewsRecipe):
|
||||
title = 'The Philippine Daily Inquirer'
|
||||
custom_title = "The Philippine Daily Inquirer - " + time.strftime('%d %b %Y %I:%M %p')
|
||||
__author__ = 'jde'
|
||||
__date__ = '03 June 2012'
|
||||
__version__ = '1.0'
|
||||
description = 'The Philippine Daily Inquirer is a widely read and circulated newspaper.'
|
||||
language = 'en_PH'
|
||||
publisher = 'The Philippine Daily Inquirer'
|
||||
category = 'news, Philippines'
|
||||
tags = 'news, Philippines'
|
||||
cover_url = 'http://www.inquirer.com.ph/assets/bg/logo.jpg'
|
||||
masthead_url = 'http://www.inquirer.com.ph/assets/bg/logo.jpg'
|
||||
oldest_article = 1.5 #days
|
||||
max_articles_per_feed = 25
|
||||
simultaneous_downloads = 20
|
||||
publication_type = 'newspaper'
|
||||
timefmt = ' [%a, %d %b %Y %I:%M %p]'
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
encoding = None
|
||||
recursions = 0
|
||||
needs_subscription = False
|
||||
remove_javascript = True
|
||||
remove_empty_feeds = True
|
||||
auto_cleanup = False
|
||||
|
||||
remove_tags_after = [
|
||||
dict(name='div', attrs={'id':'entryMeta'})
|
||||
,dict(name='div', attrs={'id':'taboola-div'})
|
||||
,dict(name='br', attrs={'class':'clear'})
|
||||
]
|
||||
|
||||
remove_tags = [
|
||||
dict(name='div', attrs={'class':'recent'})
|
||||
,dict(name='div', attrs={'id':'sharefeature'})
|
||||
,dict(name='div', attrs={'id':'masthead_bg'})
|
||||
,dict(name='div', attrs={'id':'navmenu_main'})
|
||||
,dict(name='div', attrs={'id':'navmenu_channel'})
|
||||
,dict(name='div', attrs={'class':'breadcrumbs'})
|
||||
,dict(name='div', attrs={'id':'search_container'})
|
||||
,dict(name='a', attrs={'href':'http://ruby.inquirer.net/redirect/redirect.php?item_id=1143'})
|
||||
,dict(name='a', attrs={'href':'http://ruby.inquirer.net/redirect/redirect.php?item_id=1147'})
|
||||
]
|
||||
|
||||
|
||||
conversion_options = { 'title' : custom_title,
|
||||
'comments' : description,
|
||||
'tags' : tags,
|
||||
'language' : language,
|
||||
'publisher' : publisher,
|
||||
'authors' : publisher,
|
||||
'smarten_punctuation' : True
|
||||
}
|
||||
|
||||
|
||||
feeds = [
|
||||
('Headlines' , 'http://newsinfo.inquirer.net/category/inquirer-headlines/feed' )
|
||||
,('Latest Stories' , 'http://newsinfo.inquirer.net/category/latest-stories/feed' )
|
||||
,('Nation' , 'http://newsinfo.inquirer.net/category/nation/feed' )
|
||||
,('Nation - Latest Stories' , 'http://newsinfo.inquirer.net/category/latest-stories/nation-latest-stories/feed' )
|
||||
,('Metro' , 'http://newsinfo.inquirer.net/category/metro/feed' )
|
||||
,('Metro - Latest Stories' , 'http://newsinfo.inquirer.net/category/latest-stories/metro-latest-stories/feed' )
|
||||
,('Regions' , 'http://newsinfo.inquirer.net/category/regions/feed' )
|
||||
,('Regions - Latest Stories' , 'http://newsinfo.inquirer.net/category/latest-stories/regions-latest-stories/feed' )
|
||||
# ,('News' , 'http://www.inquirer.net/fullfeed' )
|
||||
# ,('More News' , 'http://newsinfo.inquirer.net/feed' )
|
||||
,('Global Nation' , 'http://globalnation.inquirer.net/feed' )
|
||||
,('Global Nation - Latest Stories' , 'http://globalnation.inquirer.net/category/latest-stories/feed' )
|
||||
,('Global Nation - Philippines' , 'http://globalnation.inquirer.net/category/news/philippines/feed' )
|
||||
,('Global Nation - Asia & Pacific' , 'http://globalnation.inquirer.net/category/news/asiaaustralia/feed' )
|
||||
,('Global Nation - Americas' , 'http://globalnation.inquirer.net/category/news/uscanada/feed' )
|
||||
,('Global Nation - Middle East & Africa' , 'http://globalnation.inquirer.net/category/news/middle-eastafrica/feed' )
|
||||
,('Global Nation - Europe' , 'http://globalnation.inquirer.net/category/news/europe/feed' )
|
||||
,('Global Nation - Global Pinoy' , 'http://globalnation.inquirer.net/category/global-pinoy/feed' )
|
||||
,('Global Nation - Events' , 'http://globalnation.inquirer.net/category/events/feed' )
|
||||
,('Business' , 'http://business.inquirer.net/feed' )
|
||||
,('Business - Latest Stories' , 'http://business.inquirer.net/category/latest-stories/feed' )
|
||||
,('Business - Money' , 'http://business.inquirer.net/category/money/feed' )
|
||||
,('Business - Science & Health' , 'http://business.inquirer.net/category/science-and-health/feed' )
|
||||
,('Business - Motoring' , 'http://business.inquirer.net/category/motoring/feed' )
|
||||
,('Business - Property Guide' , 'http://business.inquirer.net/category/property-guide/feed' )
|
||||
,('Business - Columnists' , 'http://business.inquirer.net/category/columnists/feed' )
|
||||
,('Sports' , 'http://sports.inquirer.net/feed' )
|
||||
,('Sports - Latest Stories' , 'http://sports.inquirer.net/category/latest-stories/feed' )
|
||||
,('Sports - Basketball' , 'http://sports.inquirer.net/category/section/basketball/feed' )
|
||||
,('Sports - Boxing & MMA' , 'http://sports.inquirer.net/category/section/boxing-mma/feed' )
|
||||
,('Sports - Golf' , 'http://sports.inquirer.net/category/section/golf/feed' )
|
||||
,('Sports - Football' , 'http://sports.inquirer.net/category/section/other-sports/football/feed' )
|
||||
,('Sports - Other Sports' , 'http://sports.inquirer.net/category/section/other-sports/feed' )
|
||||
,('Technology' , 'http://technology.inquirer.net/feed' )
|
||||
,('Technology Latest Stories' , 'http://technology.inquirer.net/category/latest-stories/feed' )
|
||||
,('Entertainment' , 'http://entertainment.inquirer.net/feed' )
|
||||
,('Entertainment - Headlines' , 'http://entertainment.inquirer.net/category/headlines/feed' )
|
||||
,('Entertainment - Latest Stories' , 'http://entertainment.inquirer.net/category/latest-stories/feed' )
|
||||
,('Entertainment - Movies' , 'http://movies.inquirer.net/feed' )
|
||||
,('Lifestyle' , 'http://lifestyle.inquirer.net/feed' )
|
||||
,('Lifestyle - Latest Stories' , 'http://lifestyle.inquirer.net/category/latest-stories/feed' )
|
||||
,('Lifestyle - Arts & Books' , 'http://lifestyle.inquirer.net/category/arts-and-books/feed' )
|
||||
,('Lifestyle - Wellness' , 'http://lifestyle.inquirer.net/category/wellness/feed' )
|
||||
,('Lifestyle - Home & Entertaining' , 'http://lifestyle.inquirer.net/category/home-and-entertaining/feed' )
|
||||
,('Lifestyle - Parenting' , 'http://lifestyle.inquirer.net/category/parenting/feed' )
|
||||
,('Lifestyle - Food' , 'http://lifestyle.inquirer.net/category/food/feed' )
|
||||
,('Lifestyle - Fashion & Beauty' , 'http://lifestyle.inquirer.net/category/fashion-and-beauty/feed' )
|
||||
,('Lifestyle - Super' , 'http://lifestyle.inquirer.net/category/super/feed' )
|
||||
,('Lifestyle - 2BU' , 'http://lifestyle.inquirer.net/category/2bu/feed' )
|
||||
,('Lifestyle - Sunday Lifestyle' , 'http://lifestyle.inquirer.net/category/sunday-lifestyle/feed' )
|
||||
,('Lifestyle - Wedding' , 'http://lifestyle.inquirer.net/category/sunday-lifestyle/wedding/feed' )
|
||||
,('Lifestyle - Travel' , 'http://lifestyle.inquirer.net/category/sunday-lifestyle/travel/feed' )
|
||||
,('Lifestyle - Relationship' , 'http://lifestyle.inquirer.net/category/sunday-lifestyle/relationship/feed' )
|
||||
,('Opinion' , 'http://opinion.inquirer.net/feed' )
|
||||
,('Opinion - Viewpoints' , 'http://opinion.inquirer.net/category/viewpoints/feed' )
|
||||
,('Opinion - Talk of the Town' , 'http://opinion.inquirer.net/category/inquirer-opinion/talk-of-the-town/feed' )
|
||||
,('Editorial' , 'http://opinion.inquirer.net/category/editorial/feed' )
|
||||
,('Letters to the Editor' , 'http://opinion.inquirer.net/category/letters-to-the-editor/feed' )
|
||||
,('Columns' , 'http://opinion.inquirer.net/category/columns/feed' )
|
||||
,('Citizens Journalism' , 'http://newsinfo.inquirer.net/category/citizens-journalism/feed' )
|
||||
,('Cebu - Daily News' , 'http://newsinfo.inquirer.net/category/cdn/feed' )
|
||||
,('Cebu - More News' , 'http://newsinfo.inquirer.net/category/cdn/cdn-news/feed' )
|
||||
,('Cebu - Community' , 'http://newsinfo.inquirer.net/category/cdn/cdn-community/feed' )
|
||||
,('Cebu - Metro' , 'http://newsinfo.inquirer.net/category/cdn/cdn-metro/feed' )
|
||||
,('Cebu - Business' , 'http://newsinfo.inquirer.net/category/cdn/cdn-enterprise/feed' )
|
||||
,('Cebu - Sports' , 'http://newsinfo.inquirer.net/category/cdn/cdn-sports/feed' )
|
||||
,('Cebu - Visayas' , 'http://newsinfo.inquirer.net/category/cdn/cdn-visayas/feed' )
|
||||
,('Cebu - Opinion' , 'http://newsinfo.inquirer.net/category/cdn/cdn-opinion/feed' )
|
||||
]
|
97
recipes/the_philippine_star.recipe
Normal file
97
recipes/the_philippine_star.recipe
Normal file
@ -0,0 +1,97 @@
|
||||
|
||||
'''
|
||||
www.philstar.com
|
||||
'''
|
||||
|
||||
import time
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
|
||||
class PhilippineStar(BasicNewsRecipe):
|
||||
title = 'The Philippine Star'
|
||||
custom_title = "The Philippine Star - " + time.strftime('%d %b %Y %I:%M %p')
|
||||
__author__ = 'jde'
|
||||
__date__ = '31 May 2012'
|
||||
__version__ = '1.0'
|
||||
description = 'The Philippine Star is a daily English-language broadsheet newspaper based in Manila. It has the most subscribers of any newspaper in the Philippines - philstar.com is a Philippine news and entertainment portal for the Filipino global community. It is the online presence of the STAR Group of Publications, a leading publisher of newspapers and magazines in the Philippines.'
|
||||
language = 'en_PH'
|
||||
publisher = 'The Philippine STAR'
|
||||
category = 'news, Philippines'
|
||||
tags = 'news, Philippines'
|
||||
cover_url = 'http://www.philstar.com/images/philstar-logo-white.jpg'
|
||||
masthead_url = 'http://www.philstar.com/images/philstar-logo-white.jpg'
|
||||
oldest_article = 1 #days
|
||||
max_articles_per_feed = 25
|
||||
simultaneous_downloads = 20
|
||||
publication_type = 'newspaper'
|
||||
timefmt = ' [%a, %d %b %Y %I:%M %p]'
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
encoding = None
|
||||
recursions = 0
|
||||
needs_subscription = False
|
||||
remove_javascript = True
|
||||
remove_empty_feeds = True
|
||||
auto_cleanup = False
|
||||
|
||||
remove_tags = [dict(name='img', attrs={'id':'Image1'}) #Logo
|
||||
,dict(name='span', attrs={'id':'ControlArticle1_LabelHeader'}) #Section (Headlines, Nation, Metro, ...)
|
||||
,dict(name='a', attrs={'id':'ControlArticle1_FormView1_hlComments'}) #Comments
|
||||
,dict(name='img', attrs={'src':'images/post-comments.jpg'}) #View Comments
|
||||
,dict(name='a', attrs={'id':'ControlArticle1_FormView1_ControlPhotoAndCaption1_hlImageCaption'}) #Zoom
|
||||
]
|
||||
conversion_options = { 'title' : custom_title,
|
||||
'comments' : description,
|
||||
'tags' : tags,
|
||||
'language' : language,
|
||||
'publisher' : publisher,
|
||||
'authors' : publisher,
|
||||
'smarten_punctuation' : True
|
||||
}
|
||||
|
||||
feeds = [
|
||||
('Headlines' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=63' )
|
||||
,('Breaking News' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=200' )
|
||||
,('News Feature' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=68' )
|
||||
,('Nation' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=67' )
|
||||
,('Metro' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=65' )
|
||||
,('Business' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=66' )
|
||||
,('Sports' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=69' )
|
||||
,('Entertainment' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=70' )
|
||||
,('Science & Technology' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=75' )
|
||||
,('Networks' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=71' )
|
||||
,('Business as Usual' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=78' )
|
||||
,('Banking' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=74' )
|
||||
,('Motoring' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=72' )
|
||||
,('Real Estate' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=76' )
|
||||
,('Telecoms' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=73' )
|
||||
,('Agriculture' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=77' )
|
||||
,('Arts & Culture' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=79' )
|
||||
,('Food & Leisure' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=81' )
|
||||
,('Health & Family' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=80' )
|
||||
,('Education & Home' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=442' )
|
||||
,('Travel & Tourism' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=87' )
|
||||
,('Newsmakers' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=88' )
|
||||
,('Business Life' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=82' )
|
||||
,('Fashion & Beauty' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=83' )
|
||||
,('For Men' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=446' )
|
||||
,('Gadgets' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=449' )
|
||||
,('Sunday Life' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=86' )
|
||||
,('Supreme' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=448' )
|
||||
,('Opinion' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=64' )
|
||||
,('Letters to the Editor' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=135' )
|
||||
,('Starweek Magazine' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=90' )
|
||||
,('Modern Living' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=85' )
|
||||
,('YStyle' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=451' )
|
||||
,('Allure' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=89' )
|
||||
,('Weather' , 'http://rss.philstar.com/Rss.aspx?publicationSubCategoryId=116' )
|
||||
]
|
||||
|
||||
# process the printer friendly version of article
|
||||
def print_version(self, url):
|
||||
return url.replace('/Article', '/ArticlePrinterFriendly')
|
||||
|
||||
# obtain title from printer friendly version of article; avoiding add_toc_thumbnail changing title when article has image
|
||||
def populate_article_metadata(self, article, soup, first):
|
||||
article.title = soup.find('span', {'id': 'ControlArticle1_FormView1_ArticleHeaderLabel'}).contents[0].strip()
|
||||
|
||||
|
@ -13,10 +13,11 @@ class tvn24(BasicNewsRecipe):
|
||||
remove_empty_feeds = True
|
||||
remove_javascript = True
|
||||
no_stylesheets = True
|
||||
keep_only_tags=[dict(id='tvn24_wiadomosci_detal'), dict(name='h1', attrs={'class':'standardHeader1'}), dict(attrs={'class':['date60m rd5', 'imageBackground fl rd7', 'contentFromCMS']})]
|
||||
remove_tags_after= dict(name='div', attrs={'class':'socialBoxesBottom'})
|
||||
remove_tags=[dict(attrs={'class':['tagi_detal', 'socialBoxesBottom', 'twitterBox', 'commentsInfo', 'textSize', 'obj_ukrytydruk obj_ramka1_r', 'related newsNews align-right', 'box', 'newsUserList', 'watchMaterial text']})]
|
||||
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':'standardHeader1'}), dict(attrs={'class':['date60m rd5', 'imageBackground fl rd7', 'contentFromCMS']}), dict(attrs={'class':'mainLeftColumn'})]
|
||||
remove_tags=[dict(attrs={'class':['commentsInfo', 'textSize', 'related newsNews align-right', 'box', 'watchMaterial text']})]
|
||||
#remove_tags_after= dict(attrs={'class':'articleAuthors mb30 mt5 grey_v6'})
|
||||
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):
|
||||
|
@ -1,4 +1,5 @@
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
import re
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class ViceESRecipe(BasicNewsRecipe):
|
||||
@ -7,11 +8,33 @@ class ViceESRecipe(BasicNewsRecipe):
|
||||
description = u'La página web oficial de la revista Vice España'
|
||||
category = u'noticias, fotografía, blogs, moda, arte, cine, música, literatura, tecnología'
|
||||
cover_url = 'http://www.seeklogo.com/images/V/Vice-logo-668578AC94-seeklogo.com.gif'
|
||||
oldest_article = 20
|
||||
max_articles_per_feed = 30
|
||||
auto_cleanup = True
|
||||
oldest_article = 14
|
||||
max_articles_per_feed = 100
|
||||
auto_cleanup = False
|
||||
no_stylesheets = True
|
||||
language = 'es'
|
||||
use_embedded_content = False
|
||||
remove_javascript = True
|
||||
publication_type = 'magazine'
|
||||
|
||||
recursions=10
|
||||
match_regexps = [r'/read/.*\?Contentpage=[2-9]$']
|
||||
|
||||
keep_only_tags = [
|
||||
dict(attrs={'class':['article_title','article_content','next']})
|
||||
]
|
||||
remove_tags = [
|
||||
dict(attrs={'class':['social_buttons','search','tweet','like','inline_socials'
|
||||
,'stumblebadge','plusone']})
|
||||
]
|
||||
|
||||
extra_css = '''
|
||||
.author{font-size:small}
|
||||
img{margin-bottom: 0.4em; display:block; margin-left:auto; margin-right: auto}
|
||||
'''
|
||||
|
||||
preprocess_regexps = [
|
||||
(re.compile(r'<img src="http://.*\.scorecardresearch\.com/'), lambda m: '')
|
||||
]
|
||||
|
||||
feeds = [('Vice', 'http://www.vice.com/es/rss')]
|
||||
|
||||
|
30
recipes/wirtualnemedia_pl.recipe
Normal file
30
recipes/wirtualnemedia_pl.recipe
Normal file
@ -0,0 +1,30 @@
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class WirtualneMedia(BasicNewsRecipe):
|
||||
title = u'wirtualnemedia.pl'
|
||||
oldest_article = 7
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
remove_empty_feeds = True
|
||||
__author__ = 'fenuks'
|
||||
description = u'Portal o mediach, reklamie, internecie, PR, telekomunikacji - nr 1 w Polsce - WirtualneMedia.pl - wiadomości z pierwszej ręki.'
|
||||
category = 'internet'
|
||||
language = 'pl'
|
||||
masthead_url= 'http://i.wp.pl/a/f/jpeg/8654/wirtualnemedia.jpeg'
|
||||
cover_url= 'http://static.wirtualnemedia.pl/img/logo_wirtualnemedia_newsletter.gif'
|
||||
remove_tags=[dict(id=['header', 'footer'])]
|
||||
feeds = [(u'Gospodarka', u'http://www.wirtualnemedia.pl/rss/wm_gospodarka.xml'),
|
||||
(u'Internet', u'http://www.wirtualnemedia.pl/rss/wm_internet.xml'),
|
||||
(u'Kultura', u'http://www.wirtualnemedia.pl/rss/wm_kulturarozrywka.xml'),
|
||||
(u'Badania', u'http://www.wirtualnemedia.pl/rss/wm_marketing.xml'),
|
||||
(u'Prasa', u'http://www.wirtualnemedia.pl/rss/wm_prasa.xml'),
|
||||
(u'Radio', u'http://www.wirtualnemedia.pl/rss/wm_radio.xml'),
|
||||
(u'Reklama', u'http://www.wirtualnemedia.pl/rss/wm_reklama.xml'),
|
||||
(u'PR', u'http://www.wirtualnemedia.pl/rss/wm_relations.xml'),
|
||||
(u'Technologie', u'http://www.wirtualnemedia.pl/rss/wm_telekomunikacja.xml'),
|
||||
(u'Telewizja', u'http://www.wirtualnemedia.pl/rss/wm_telewizja_rss.xml')
|
||||
]
|
||||
|
||||
def print_version(self, url):
|
||||
return url.replace('artykul', 'print')
|
BIN
resources/images/mimetypes/docx.png
Normal file
BIN
resources/images/mimetypes/docx.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
11
session.vim
11
session.vim
@ -1,8 +1,15 @@
|
||||
" Project wide builtins
|
||||
let $PYFLAKES_BUILTINS = "_,dynamic_property,__,P,I,lopen,icu_lower,icu_upper,icu_title,ngettext"
|
||||
|
||||
" Include directories for C modules
|
||||
let g:syntastic_cpp_include_dirs = [ '/usr/include/podofo']
|
||||
" Include directories for C++ modules
|
||||
let g:syntastic_cpp_include_dirs = [
|
||||
\'/usr/include/podofo',
|
||||
\'/usr/include/qt4/QtCore',
|
||||
\'/usr/include/qt4/QtGui',
|
||||
\'/usr/include/qt4',
|
||||
\'src/qtcurve/common', 'src/qtcurve',
|
||||
\]
|
||||
let g:syntastic_c_include_dirs = g:syntastic_cpp_include_dirs
|
||||
|
||||
fun! CalibreLog()
|
||||
" Setup buffers to edit the calibre changelog and version info prior to
|
||||
|
@ -8,6 +8,7 @@ __docformat__ = 'restructuredtext en'
|
||||
|
||||
import textwrap, os, shlex, subprocess, glob, shutil
|
||||
from distutils import sysconfig
|
||||
from multiprocessing import cpu_count
|
||||
|
||||
from PyQt4.pyqtconfig import QtGuiModuleMakefile
|
||||
|
||||
@ -268,6 +269,7 @@ class Build(Command):
|
||||
self.obj_dir = os.path.join(os.path.dirname(SRC), 'build', 'objects')
|
||||
if not os.path.exists(self.obj_dir):
|
||||
os.makedirs(self.obj_dir)
|
||||
self.build_style(self.j(self.SRC, 'calibre', 'plugins'))
|
||||
for ext in extensions:
|
||||
if opts.only != 'all' and opts.only != ext.name:
|
||||
continue
|
||||
@ -362,6 +364,87 @@ class Build(Command):
|
||||
print "Error while executing: %s\n" % (cmdline)
|
||||
raise
|
||||
|
||||
def build_style(self, dest):
|
||||
self.info('\n####### Building calibre style', '#'*7)
|
||||
sdir = self.j(self.SRC, 'qtcurve')
|
||||
def path(x):
|
||||
x=self.j(sdir, x)
|
||||
return ('"%s"'%x).replace(os.sep, '/')
|
||||
headers = [
|
||||
"common/colorutils.h",
|
||||
"common/common.h",
|
||||
"common/config_file.h",
|
||||
"style/blurhelper.h",
|
||||
"style/fixx11h.h",
|
||||
"style/pixmaps.h",
|
||||
"style/qtcurve.h",
|
||||
"style/shortcuthandler.h",
|
||||
"style/utils.h",
|
||||
"style/windowmanager.h",
|
||||
]
|
||||
sources = [
|
||||
"common/colorutils.c",
|
||||
"common/common.c",
|
||||
"common/config_file.c",
|
||||
"style/blurhelper.cpp",
|
||||
"style/qtcurve.cpp",
|
||||
"style/shortcuthandler.cpp",
|
||||
"style/utils.cpp",
|
||||
"style/windowmanager.cpp",
|
||||
]
|
||||
if not iswindows and not isosx:
|
||||
headers.append( "style/shadowhelper.h")
|
||||
sources.append('style/shadowhelper.cpp')
|
||||
|
||||
pro = textwrap.dedent('''
|
||||
TEMPLATE = lib
|
||||
CONFIG += qt plugin release
|
||||
CONFIG -= embed_manifest_dll
|
||||
VERSION = 1.0.0
|
||||
DESTDIR = .
|
||||
TARGET = calibre
|
||||
QT *= svg
|
||||
INCLUDEPATH *= {conf} {inc}
|
||||
win32-msvc*:DEFINES *= _CRT_SECURE_NO_WARNINGS
|
||||
|
||||
# Force C++ language
|
||||
*g++*:QMAKE_CFLAGS *= -x c++
|
||||
*msvc*:QMAKE_CFLAGS *= -TP
|
||||
*msvc*:QMAKE_CXXFLAGS += /MP
|
||||
|
||||
''').format(conf=path(''), inc=path('common'))
|
||||
if isosx:
|
||||
pro += '\nCONFIG += x86 x86_64\n'
|
||||
else:
|
||||
pro += '\nunix:QT *= dbus\n'
|
||||
|
||||
for x in headers:
|
||||
pro += 'HEADERS += %s\n'%path(x)
|
||||
for x in sources:
|
||||
pro += 'SOURCES += %s\n'%path(x)
|
||||
odir = self.j(self.d(self.SRC), 'build', 'qtcurve')
|
||||
if not os.path.exists(odir):
|
||||
os.makedirs(odir)
|
||||
ocwd = os.getcwdu()
|
||||
os.chdir(odir)
|
||||
try:
|
||||
if not os.path.exists('qtcurve.pro') or (open('qtcurve.pro',
|
||||
'rb').read() != pro):
|
||||
with open('qtcurve.pro', 'wb') as f:
|
||||
f.write(pro)
|
||||
qmc = [QMAKE, '-o', 'Makefile']
|
||||
if iswindows:
|
||||
qmc += ['-spec', 'win32-msvc2008']
|
||||
self.check_call(qmc + ['qtcurve.pro'])
|
||||
self.check_call([make]+([] if iswindows else ['-j%d'%(cpu_count()
|
||||
or 1)]))
|
||||
src = (glob.glob('*.so') + glob.glob('release/*.dll') +
|
||||
glob.glob('*.dylib'))
|
||||
ext = 'pyd' if iswindows else 'so'
|
||||
shutil.copy2(src[0], self.j(dest, 'calibre_style.'+ext))
|
||||
finally:
|
||||
os.chdir(ocwd)
|
||||
|
||||
def build_qt_objects(self, ext):
|
||||
obj_pat = 'release\\*.obj' if iswindows else '*.o'
|
||||
objects = glob.glob(obj_pat)
|
||||
|
@ -67,6 +67,7 @@ class VMInstaller(Command):
|
||||
INSTALLER_EXT = None
|
||||
VM = None
|
||||
VM_NAME = None
|
||||
VM_CHECK = None
|
||||
FREEZE_COMMAND = None
|
||||
FREEZE_TEMPLATE = 'python setup.py {freeze_command}'
|
||||
SHUTDOWN_CMD = ['sudo', 'poweroff']
|
||||
@ -117,6 +118,13 @@ class VMInstaller(Command):
|
||||
|
||||
|
||||
def run_vm(self):
|
||||
pat = '/%s/'%(self.VM_CHECK or self.VM_NAME)
|
||||
pids= [pid for pid in os.listdir('/proc') if pid.isdigit()]
|
||||
for pid in pids:
|
||||
cmdline = open(os.path.join('/proc', pid, 'cmdline'), 'rb').read()
|
||||
if 'vmware-vmx' in cmdline and pat in cmdline:
|
||||
return
|
||||
|
||||
self.__p = subprocess.Popen([self.vm])
|
||||
|
||||
def start_vm(self, sleep=75):
|
||||
|
@ -296,10 +296,6 @@ class Py2App(object):
|
||||
self.add_qt_framework(f)
|
||||
for d in glob.glob(join(SW, 'qt', 'plugins', '*')):
|
||||
shutil.copytree(d, join(self.contents_dir, 'MacOS', basename(d)))
|
||||
sty = join(self.contents_dir, 'MacOS', 'styles')
|
||||
os.mkdir(sty)
|
||||
shutil.copyfile(glob.glob(join(SW, 'build', 'QtCurve*', 'build', 'style',
|
||||
'qtcurve.so'))[-1], join(sty, 'qtcurve.dylib'))
|
||||
for l in glob.glob(join(self.contents_dir, 'MacOS', '*/*.dylib')):
|
||||
self.fix_dependencies_in_lib(l)
|
||||
x = os.path.relpath(l, join(self.contents_dir, 'MacOS'))
|
||||
|
@ -28,6 +28,7 @@ class Win32(VMInstaller):
|
||||
INSTALLER_EXT = 'exe'
|
||||
VM_NAME = 'xp_build'
|
||||
VM = '/vmware/bin/%s'%VM_NAME
|
||||
VM_CHECK = 'calibre_windows_xp_home'
|
||||
FREEZE_COMMAND = 'win32_freeze'
|
||||
FREEZE_TEMPLATE = 'python -OO setup.py {freeze_command} --no-ice'
|
||||
INSTALLER_EXT = 'msi'
|
||||
|
@ -248,13 +248,6 @@ class Win32Freeze(Command, WixMixIn):
|
||||
if os.path.exists(tg):
|
||||
shutil.rmtree(tg)
|
||||
shutil.copytree(imfd, tg)
|
||||
self.info('\nAdding QtCurve...')
|
||||
qtcurve = self.j(QTCURVE, 'qtcurve.dll')
|
||||
tg = self.j(tdir, 'styles')
|
||||
if os.path.exists(tg):
|
||||
shutil.rmtree(tg)
|
||||
os.mkdir(tg)
|
||||
shutil.copy2(qtcurve, tg)
|
||||
|
||||
for dirpath, dirnames, filenames in os.walk(tdir):
|
||||
for x in filenames:
|
||||
@ -494,7 +487,8 @@ class Win32Freeze(Command, WixMixIn):
|
||||
# Add the .pyds from python and calibre to the zip file
|
||||
for x in (self.plugins_dir, self.dll_dir):
|
||||
for pyd in os.listdir(x):
|
||||
if pyd.endswith('.pyd') and pyd != 'sqlite_custom.pyd':
|
||||
if pyd.endswith('.pyd') and pyd not in {
|
||||
'sqlite_custom.pyd', 'calibre_style.pyd'}:
|
||||
# sqlite_custom has to be a file for
|
||||
# sqlite_load_extension to work
|
||||
self.add_to_zipfile(zf, pyd, x)
|
||||
|
@ -101,14 +101,6 @@ Now, run configure and make::
|
||||
|
||||
Add the path to the bin folder inside the Qt dir to your system PATH.
|
||||
|
||||
Now build QtCurve
|
||||
cd qmake
|
||||
edit the qmake.pro file setting the TARGET to Release
|
||||
|
||||
qmake && nmake
|
||||
|
||||
The plugin will be in c:\plugins\styles
|
||||
|
||||
SIP
|
||||
-----
|
||||
|
||||
|
@ -8,14 +8,14 @@ msgstr ""
|
||||
"Project-Id-Version: calibre\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2011-11-25 14:01+0000\n"
|
||||
"PO-Revision-Date: 2012-04-27 18:24+0000\n"
|
||||
"PO-Revision-Date: 2012-06-06 17:20+0000\n"
|
||||
"Last-Translator: Jellby <Unknown>\n"
|
||||
"Language-Team: Spanish <es@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-04-28 04:54+0000\n"
|
||||
"X-Generator: Launchpad (build 15149)\n"
|
||||
"X-Launchpad-Export-Date: 2012-06-07 04:40+0000\n"
|
||||
"X-Generator: Launchpad (build 15353)\n"
|
||||
|
||||
#. name for aaa
|
||||
msgid "Ghotuo"
|
||||
@ -383,7 +383,7 @@ msgstr "Tibetano amdo"
|
||||
|
||||
#. name for ady
|
||||
msgid "Adyghe"
|
||||
msgstr "Adyghe"
|
||||
msgstr "Adigué"
|
||||
|
||||
#. name for adz
|
||||
msgid "Adzera"
|
||||
@ -951,7 +951,7 @@ msgstr "Alune"
|
||||
|
||||
#. name for alq
|
||||
msgid "Algonquin"
|
||||
msgstr "Algonquin"
|
||||
msgstr "Algonquino"
|
||||
|
||||
#. name for alr
|
||||
msgid "Alutor"
|
||||
@ -1451,7 +1451,7 @@ msgstr "Araona"
|
||||
|
||||
#. name for arp
|
||||
msgid "Arapaho"
|
||||
msgstr "Arapaho"
|
||||
msgstr "Arapajó"
|
||||
|
||||
#. name for arq
|
||||
msgid "Arabic; Algerian"
|
||||
@ -4363,7 +4363,7 @@ msgstr "Jalkunan"
|
||||
|
||||
#. name for bxm
|
||||
msgid "Buriat; Mongolia"
|
||||
msgstr "Buriat de Mongolia"
|
||||
msgstr "Buriato de Mongolia"
|
||||
|
||||
#. name for bxn
|
||||
msgid "Burduna"
|
||||
@ -4383,7 +4383,7 @@ msgstr "Beele"
|
||||
|
||||
#. name for bxr
|
||||
msgid "Buriat; Russia"
|
||||
msgstr "Buriat de Rusia"
|
||||
msgstr "Buriato de Rusia"
|
||||
|
||||
#. name for bxs
|
||||
msgid "Busam"
|
||||
@ -4391,7 +4391,7 @@ msgstr "Busam"
|
||||
|
||||
#. name for bxu
|
||||
msgid "Buriat; China"
|
||||
msgstr "Buriat de China"
|
||||
msgstr "Buriato de China"
|
||||
|
||||
#. name for bxv
|
||||
msgid "Berakou"
|
||||
@ -4999,7 +4999,7 @@ msgstr "Mari (Rusia)"
|
||||
|
||||
#. name for chn
|
||||
msgid "Chinook jargon"
|
||||
msgstr "Chinook"
|
||||
msgstr "Jerga chinook"
|
||||
|
||||
#. name for cho
|
||||
msgid "Choctaw"
|
||||
@ -6135,7 +6135,7 @@ msgstr "Slave (atabascano)"
|
||||
|
||||
#. name for dep
|
||||
msgid "Delaware; Pidgin"
|
||||
msgstr "Delaware pidyin"
|
||||
msgstr "Pidyin delaware"
|
||||
|
||||
#. name for deq
|
||||
msgid "Dendi (Central African Republic)"
|
||||
@ -6723,7 +6723,7 @@ msgstr "Darai"
|
||||
|
||||
#. name for dsb
|
||||
msgid "Sorbian; Lower"
|
||||
msgstr "Sorabo inferior"
|
||||
msgstr "Bajo sorabo"
|
||||
|
||||
#. name for dse
|
||||
msgid "Dutch Sign Language"
|
||||
@ -7831,7 +7831,7 @@ msgstr "Gabri"
|
||||
|
||||
#. name for gac
|
||||
msgid "Great Andamanese; Mixed"
|
||||
msgstr "Gran Andamanés mixto"
|
||||
msgstr "Gran andamanés mixto"
|
||||
|
||||
#. name for gad
|
||||
msgid "Gaddang"
|
||||
@ -8479,23 +8479,23 @@ msgstr "Bajo alemán medio"
|
||||
|
||||
#. name for gmm
|
||||
msgid "Gbaya-Mbodomo"
|
||||
msgstr ""
|
||||
msgstr "Gbaya-Mbodomo"
|
||||
|
||||
#. name for gmn
|
||||
msgid "Gimnime"
|
||||
msgstr ""
|
||||
msgstr "Gimnime"
|
||||
|
||||
#. name for gmu
|
||||
msgid "Gumalu"
|
||||
msgstr ""
|
||||
msgstr "Gumalu"
|
||||
|
||||
#. name for gmv
|
||||
msgid "Gamo"
|
||||
msgstr ""
|
||||
msgstr "Gamo"
|
||||
|
||||
#. name for gmx
|
||||
msgid "Magoma"
|
||||
msgstr ""
|
||||
msgstr "Magoma"
|
||||
|
||||
#. name for gmy
|
||||
msgid "Greek; Mycenaean"
|
||||
@ -8503,11 +8503,11 @@ msgstr "Griego micénico"
|
||||
|
||||
#. name for gna
|
||||
msgid "Kaansa"
|
||||
msgstr ""
|
||||
msgstr "Kaansa"
|
||||
|
||||
#. name for gnb
|
||||
msgid "Gangte"
|
||||
msgstr ""
|
||||
msgstr "Gangte"
|
||||
|
||||
#. name for gnc
|
||||
msgid "Guanche"
|
||||
@ -8515,15 +8515,15 @@ msgstr "Guanche"
|
||||
|
||||
#. name for gnd
|
||||
msgid "Zulgo-Gemzek"
|
||||
msgstr ""
|
||||
msgstr "Zulgo-Gemzek"
|
||||
|
||||
#. name for gne
|
||||
msgid "Ganang"
|
||||
msgstr ""
|
||||
msgstr "Ganang"
|
||||
|
||||
#. name for gng
|
||||
msgid "Ngangam"
|
||||
msgstr ""
|
||||
msgstr "Ngangam"
|
||||
|
||||
#. name for gnh
|
||||
msgid "Lere"
|
||||
@ -8535,7 +8535,7 @@ msgstr ""
|
||||
|
||||
#. name for gnk
|
||||
msgid "//Gana"
|
||||
msgstr ""
|
||||
msgstr "//Gana"
|
||||
|
||||
#. name for gnl
|
||||
msgid "Gangulu"
|
||||
@ -8555,7 +8555,7 @@ msgstr "Gondi septentrional"
|
||||
|
||||
#. name for gnq
|
||||
msgid "Gana"
|
||||
msgstr ""
|
||||
msgstr "Gana"
|
||||
|
||||
#. name for gnr
|
||||
msgid "Gureng Gureng"
|
||||
@ -8563,11 +8563,11 @@ msgstr ""
|
||||
|
||||
#. name for gnt
|
||||
msgid "Guntai"
|
||||
msgstr ""
|
||||
msgstr "Guntai"
|
||||
|
||||
#. name for gnu
|
||||
msgid "Gnau"
|
||||
msgstr ""
|
||||
msgstr "Gnau"
|
||||
|
||||
#. name for gnw
|
||||
msgid "Guaraní; Western Bolivian"
|
||||
@ -8575,35 +8575,35 @@ msgstr "Guaraní boliviano occidental"
|
||||
|
||||
#. name for gnz
|
||||
msgid "Ganzi"
|
||||
msgstr ""
|
||||
msgstr "Ganzi"
|
||||
|
||||
#. name for goa
|
||||
msgid "Guro"
|
||||
msgstr ""
|
||||
msgstr "Guro"
|
||||
|
||||
#. name for gob
|
||||
msgid "Playero"
|
||||
msgstr ""
|
||||
msgstr "Playero"
|
||||
|
||||
#. name for goc
|
||||
msgid "Gorakor"
|
||||
msgstr ""
|
||||
msgstr "Gorakor"
|
||||
|
||||
#. name for god
|
||||
msgid "Godié"
|
||||
msgstr ""
|
||||
msgstr "Godié"
|
||||
|
||||
#. name for goe
|
||||
msgid "Gongduk"
|
||||
msgstr ""
|
||||
msgstr "Gongduk"
|
||||
|
||||
#. name for gof
|
||||
msgid "Gofa"
|
||||
msgstr ""
|
||||
msgstr "Gofa"
|
||||
|
||||
#. name for gog
|
||||
msgid "Gogo"
|
||||
msgstr ""
|
||||
msgstr "Gogo"
|
||||
|
||||
#. name for goh
|
||||
msgid "German; Old High (ca. 750-1050)"
|
||||
@ -8611,19 +8611,19 @@ msgstr "Alto alemán antiguo (ca. 750-1050)"
|
||||
|
||||
#. name for goi
|
||||
msgid "Gobasi"
|
||||
msgstr ""
|
||||
msgstr "Gobasi"
|
||||
|
||||
#. name for goj
|
||||
msgid "Gowlan"
|
||||
msgstr ""
|
||||
msgstr "Gowlan"
|
||||
|
||||
#. name for gok
|
||||
msgid "Gowli"
|
||||
msgstr ""
|
||||
msgstr "Gowli"
|
||||
|
||||
#. name for gol
|
||||
msgid "Gola"
|
||||
msgstr ""
|
||||
msgstr "Gola"
|
||||
|
||||
#. name for gom
|
||||
msgid "Konkani; Goan"
|
||||
@ -8635,15 +8635,15 @@ msgstr "Gondi"
|
||||
|
||||
#. name for goo
|
||||
msgid "Gone Dau"
|
||||
msgstr ""
|
||||
msgstr "Gone Dau"
|
||||
|
||||
#. name for gop
|
||||
msgid "Yeretuar"
|
||||
msgstr ""
|
||||
msgstr "Yeretuar"
|
||||
|
||||
#. name for goq
|
||||
msgid "Gorap"
|
||||
msgstr ""
|
||||
msgstr "Gorap"
|
||||
|
||||
#. name for gor
|
||||
msgid "Gorontalo"
|
||||
@ -8651,7 +8651,7 @@ msgstr "Gorontalo"
|
||||
|
||||
#. name for gos
|
||||
msgid "Gronings"
|
||||
msgstr ""
|
||||
msgstr "Gronings"
|
||||
|
||||
#. name for got
|
||||
msgid "Gothic"
|
||||
@ -8659,15 +8659,15 @@ msgstr "Gótico"
|
||||
|
||||
#. name for gou
|
||||
msgid "Gavar"
|
||||
msgstr ""
|
||||
msgstr "Gavar"
|
||||
|
||||
#. name for gow
|
||||
msgid "Gorowa"
|
||||
msgstr ""
|
||||
msgstr "Gorowa"
|
||||
|
||||
#. name for gox
|
||||
msgid "Gobu"
|
||||
msgstr ""
|
||||
msgstr "Gobu"
|
||||
|
||||
#. name for goy
|
||||
msgid "Goundo"
|
||||
@ -9683,7 +9683,7 @@ msgstr ""
|
||||
|
||||
#. name for hsb
|
||||
msgid "Sorbian; Upper"
|
||||
msgstr ""
|
||||
msgstr "Alto sorabo"
|
||||
|
||||
#. name for hsh
|
||||
msgid "Hungarian Sign Language"
|
||||
@ -19291,7 +19291,7 @@ msgstr ""
|
||||
|
||||
#. name for nwc
|
||||
msgid "Newari; Old"
|
||||
msgstr "Newari antiguo"
|
||||
msgstr "Newarí antiguo"
|
||||
|
||||
#. name for nwe
|
||||
msgid "Ngwe"
|
||||
@ -19311,7 +19311,7 @@ msgstr ""
|
||||
|
||||
#. name for nwx
|
||||
msgid "Newar; Middle"
|
||||
msgstr "Newari medio"
|
||||
msgstr "Newarí medio"
|
||||
|
||||
#. name for nwy
|
||||
msgid "Nottoway-Meherrin"
|
||||
@ -23027,7 +23027,7 @@ msgstr ""
|
||||
|
||||
#. name for sia
|
||||
msgid "Sami; Akkala"
|
||||
msgstr ""
|
||||
msgstr "Sami de Akkala"
|
||||
|
||||
#. name for sib
|
||||
msgid "Sebop"
|
||||
@ -23127,11 +23127,11 @@ msgstr ""
|
||||
|
||||
#. name for sjd
|
||||
msgid "Sami; Kildin"
|
||||
msgstr ""
|
||||
msgstr "Sami de Kildin"
|
||||
|
||||
#. name for sje
|
||||
msgid "Sami; Pite"
|
||||
msgstr ""
|
||||
msgstr "Sami de Pite"
|
||||
|
||||
#. name for sjg
|
||||
msgid "Assangori"
|
||||
@ -23139,7 +23139,7 @@ msgstr ""
|
||||
|
||||
#. name for sjk
|
||||
msgid "Sami; Kemi"
|
||||
msgstr ""
|
||||
msgstr "Sami de Kemi"
|
||||
|
||||
#. name for sjl
|
||||
msgid "Sajalong"
|
||||
@ -23171,11 +23171,11 @@ msgstr ""
|
||||
|
||||
#. name for sjt
|
||||
msgid "Sami; Ter"
|
||||
msgstr ""
|
||||
msgstr "Sami de Ter"
|
||||
|
||||
#. name for sju
|
||||
msgid "Sami; Ume"
|
||||
msgstr ""
|
||||
msgstr "Sami de Ume"
|
||||
|
||||
#. name for sjw
|
||||
msgid "Shawnee"
|
||||
@ -23407,7 +23407,7 @@ msgstr ""
|
||||
|
||||
#. name for smj
|
||||
msgid "Lule Sami"
|
||||
msgstr "Sami lule"
|
||||
msgstr "Sami de Lule"
|
||||
|
||||
#. name for smk
|
||||
msgid "Bolinao"
|
||||
@ -23423,7 +23423,7 @@ msgstr ""
|
||||
|
||||
#. name for smn
|
||||
msgid "Sami; Inari"
|
||||
msgstr ""
|
||||
msgstr "Sami de Inari"
|
||||
|
||||
#. name for smo
|
||||
msgid "Samoan"
|
||||
@ -23443,7 +23443,7 @@ msgstr ""
|
||||
|
||||
#. name for sms
|
||||
msgid "Sami; Skolt"
|
||||
msgstr ""
|
||||
msgstr "Sami de Skolt"
|
||||
|
||||
#. name for smt
|
||||
msgid "Simte"
|
||||
@ -24339,7 +24339,7 @@ msgstr "Subanen central"
|
||||
|
||||
#. name for syc
|
||||
msgid "Syriac; Classical"
|
||||
msgstr ""
|
||||
msgstr "Siríaco clásico"
|
||||
|
||||
#. name for syi
|
||||
msgid "Seki"
|
||||
@ -28235,7 +28235,7 @@ msgstr ""
|
||||
|
||||
#. name for xal
|
||||
msgid "Kalmyk"
|
||||
msgstr ""
|
||||
msgstr "Calmuco"
|
||||
|
||||
#. name for xam
|
||||
msgid "/Xam"
|
||||
|
@ -4,7 +4,7 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
__appname__ = u'calibre'
|
||||
numeric_version = (0, 8, 54)
|
||||
numeric_version = (0, 8, 55)
|
||||
__version__ = u'.'.join(map(unicode, numeric_version))
|
||||
__author__ = u"Kovid Goyal <kovid@kovidgoyal.net>"
|
||||
|
||||
|
@ -276,6 +276,16 @@ class ODTMetadataReader(MetadataReaderPlugin):
|
||||
from calibre.ebooks.metadata.odt import get_metadata
|
||||
return get_metadata(stream)
|
||||
|
||||
class DocXMetadataReader(MetadataReaderPlugin):
|
||||
|
||||
name = 'Read DOCX metadata'
|
||||
file_types = set(['docx'])
|
||||
description = _('Read metadata from %s files')%'DOCX'
|
||||
|
||||
def get_metadata(self, stream, ftype):
|
||||
from calibre.ebooks.metadata.docx import get_metadata
|
||||
return get_metadata(stream)
|
||||
|
||||
class OPFMetadataReader(MetadataReaderPlugin):
|
||||
|
||||
name = 'Read OPF metadata'
|
||||
|
@ -672,6 +672,12 @@ class KindleFireOutput(KindleDXOutput):
|
||||
dpi = 169.0
|
||||
comic_screen_size = (570, 1016)
|
||||
|
||||
@classmethod
|
||||
def tags_to_string(cls, tags):
|
||||
# The idiotic fire doesn't obey the color:white directive
|
||||
from xml.sax.saxutils import escape
|
||||
return escape(', '.join(tags))
|
||||
|
||||
class IlliadOutput(OutputProfile):
|
||||
|
||||
name = 'Illiad'
|
||||
|
@ -122,9 +122,9 @@ class ANDROID(USBMS):
|
||||
|
||||
# LG
|
||||
0x1004 : {
|
||||
0x61c5 : [0x100, 0x226, 0x9999],
|
||||
0x61cc : [0x100],
|
||||
0x61ce : [0x100],
|
||||
0x61c5 : [0x100, 0x226, 0x227, 0x9999],
|
||||
0x61cc : [0x226, 0x227, 0x9999, 0x100],
|
||||
0x61ce : [0x226, 0x227, 0x9999, 0x100],
|
||||
0x618e : [0x226, 0x227, 0x9999, 0x100],
|
||||
0x6205 : [0x226, 0x227, 0x9999, 0x100],
|
||||
},
|
||||
@ -165,6 +165,9 @@ class ANDROID(USBMS):
|
||||
# Lenovo
|
||||
0x17ef : { 0x7421 : [0x0216] },
|
||||
|
||||
# Pantech
|
||||
0x10a9 : { 0x6050 : [0x227] },
|
||||
|
||||
}
|
||||
EBOOK_DIR_MAIN = ['eBooks/import', 'wordplayer/calibretransfer', 'Books',
|
||||
'sdcard/ebooks']
|
||||
|
@ -178,7 +178,13 @@ class DevicePlugin(Plugin):
|
||||
if cvid == vid:
|
||||
if pid in products:
|
||||
if hasattr(self.VENDOR_ID, 'keys'):
|
||||
cbcd = self.VENDOR_ID[vid][pid]
|
||||
try:
|
||||
cbcd = self.VENDOR_ID[vid][pid]
|
||||
except KeyError:
|
||||
# Vendor vid does not have product pid, pid
|
||||
# exists for some other vendor in this
|
||||
# device
|
||||
continue
|
||||
else:
|
||||
cbcd = self.BCD
|
||||
if self.test_bcd(bcd, cbcd):
|
||||
|
@ -224,16 +224,18 @@ class TREKSTOR(USBMS):
|
||||
FORMATS = ['epub', 'txt', 'pdf']
|
||||
|
||||
VENDOR_ID = [0x1e68]
|
||||
PRODUCT_ID = [0x0041, 0x0042, 0x0052, 0x004e,
|
||||
0x003e # This is for the EBOOK_PLAYER_5M https://bugs.launchpad.net/bugs/792091
|
||||
PRODUCT_ID = [0x0041, 0x0042, 0x0052, 0x004e, 0x0056,
|
||||
0x003e, # This is for the EBOOK_PLAYER_5M https://bugs.launchpad.net/bugs/792091
|
||||
]
|
||||
BCD = [0x0002]
|
||||
BCD = [0x0002, 0x100]
|
||||
|
||||
EBOOK_DIR_MAIN = 'Ebooks'
|
||||
|
||||
VENDOR_NAME = 'TREKSTOR'
|
||||
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = ['EBOOK_PLAYER_7',
|
||||
'EBOOK_PLAYER_5M', 'EBOOK-READER_3.0']
|
||||
'EBOOK_PLAYER_5M', 'EBOOK-READER_3.0', 'EREADER_PYRUS']
|
||||
SUPPORTS_SUB_DIRS = True
|
||||
SUPPORTS_SUB_DIRS_DEFAULT = False
|
||||
|
||||
class EEEREADER(USBMS):
|
||||
|
||||
|
@ -268,20 +268,92 @@ class PRST1(USBMS):
|
||||
collections = booklist.get_collections(collections_attributes)
|
||||
|
||||
with closing(sqlite.connect(dbpath)) as connection:
|
||||
self.remove_orphaned_records(connection, dbpath)
|
||||
self.update_device_books(connection, booklist, source_id,
|
||||
plugboard, dbpath)
|
||||
self.update_device_collections(connection, booklist, collections, source_id)
|
||||
self.update_device_collections(connection, booklist, collections, source_id, dbpath)
|
||||
|
||||
debug_print('PRST1: finished update_device_database')
|
||||
|
||||
def update_device_books(self, connection, booklist, source_id, plugboard,
|
||||
dbpath):
|
||||
def remove_orphaned_records(self, connection, dbpath):
|
||||
from sqlite3 import DatabaseError
|
||||
|
||||
opts = self.settings()
|
||||
upload_covers = opts.extra_customization[self.OPT_UPLOAD_COVERS]
|
||||
refresh_covers = opts.extra_customization[self.OPT_REFRESH_COVERS]
|
||||
use_sony_authors = opts.extra_customization[self.OPT_USE_SONY_AUTHORS]
|
||||
try:
|
||||
cursor = connection.cursor()
|
||||
|
||||
debug_print("Removing Orphaned Collection Records")
|
||||
|
||||
# Purge any collections references that point into the abyss
|
||||
query = 'DELETE FROM collections WHERE content_id NOT IN (SELECT _id FROM books)'
|
||||
cursor.execute(query)
|
||||
query = 'DELETE FROM collections WHERE collection_id NOT IN (SELECT _id FROM collection)'
|
||||
cursor.execute(query)
|
||||
|
||||
debug_print("Removing Orphaned Book Records")
|
||||
|
||||
# Purge any references to books not in this database
|
||||
# Idea is to prevent any spill-over where these wind up applying to some other book
|
||||
query = 'DELETE FROM %s WHERE content_id NOT IN (SELECT _id FROM books)'
|
||||
cursor.execute(query%'annotation')
|
||||
cursor.execute(query%'bookmark')
|
||||
cursor.execute(query%'current_position')
|
||||
cursor.execute(query%'freehand')
|
||||
cursor.execute(query%'history')
|
||||
cursor.execute(query%'layout_cache')
|
||||
cursor.execute(query%'preference')
|
||||
|
||||
cursor.close()
|
||||
except DatabaseError:
|
||||
import traceback
|
||||
tb = traceback.format_exc()
|
||||
raise DeviceError((('The SONY database is corrupted. '
|
||||
' Delete the file %s on your reader and then disconnect '
|
||||
' reconnect it. If you are using an SD card, you '
|
||||
' should delete the file on the card as well. Note that '
|
||||
' deleting this file will cause your reader to forget '
|
||||
' any notes/highlights, etc.')%dbpath)+' Underlying error:'
|
||||
'\n'+tb)
|
||||
|
||||
def get_lastrowid(self, cursor):
|
||||
# SQLite3 + Python has a fun issue on 32-bit systems with integer overflows.
|
||||
# Issue a SQL query instead, getting the value as a string, and then converting to a long python int manually.
|
||||
query = 'SELECT last_insert_rowid()'
|
||||
cursor.execute(query)
|
||||
row = cursor.fetchone()
|
||||
|
||||
return long(row[0])
|
||||
|
||||
def get_database_min_id(self, source_id):
|
||||
sequence_min = 0L
|
||||
if source_id == 1:
|
||||
sequence_min = 4294967296L
|
||||
|
||||
return sequence_min
|
||||
|
||||
def set_database_sequence_id(self, connection, table, sequence_id):
|
||||
cursor = connection.cursor()
|
||||
|
||||
# Update the sequence Id if it exists
|
||||
query = 'UPDATE sqlite_sequence SET seq = ? WHERE name = ?'
|
||||
t = (sequence_id, table,)
|
||||
cursor.execute(query, t)
|
||||
|
||||
# Insert the sequence Id if it doesn't
|
||||
query = ('INSERT INTO sqlite_sequence (name, seq) '
|
||||
'SELECT ?, ? '
|
||||
'WHERE NOT EXISTS (SELECT 1 FROM sqlite_sequence WHERE name = ?)');
|
||||
cursor.execute(query, (table, sequence_id, table,))
|
||||
|
||||
cursor.close()
|
||||
|
||||
def read_device_books(self, connection, source_id, dbpath):
|
||||
from sqlite3 import DatabaseError
|
||||
|
||||
sequence_min = self.get_database_min_id(source_id)
|
||||
sequence_max = sequence_min
|
||||
sequence_dirty = 0
|
||||
|
||||
debug_print("Book Sequence Min: %d, Source Id: %d"%(sequence_min,source_id))
|
||||
|
||||
try:
|
||||
cursor = connection.cursor()
|
||||
@ -300,27 +372,70 @@ class PRST1(USBMS):
|
||||
' any notes/highlights, etc.')%dbpath)+' Underlying error:'
|
||||
'\n'+tb)
|
||||
|
||||
# Get the books themselves, but keep track of any that are less than the minimum.
|
||||
# Record what the max id being used is as well.
|
||||
db_books = {}
|
||||
for i, row in enumerate(cursor):
|
||||
lpath = row[0].replace('\\', '/')
|
||||
db_books[lpath] = row[1]
|
||||
if row[1] < sequence_min:
|
||||
sequence_dirty = 1
|
||||
else:
|
||||
sequence_max = max(sequence_max, row[1])
|
||||
|
||||
# Work-around for Sony Bug (SD Card DB not using right SQLite sequence)
|
||||
if source_id == 1:
|
||||
# Update any existing sequence numbers in the table that aren't in the required range
|
||||
sdcard_sequence_start = '4294967296'
|
||||
query = 'UPDATE sqlite_sequence SET seq = ? WHERE seq < ?'
|
||||
t = (sdcard_sequence_start, sdcard_sequence_start,)
|
||||
cursor.execute(query, t)
|
||||
# If the database is 'dirty', then we should fix up the Ids and the sequence number
|
||||
if sequence_dirty == 1:
|
||||
debug_print("Book Sequence Dirty for Source Id: %d"%source_id)
|
||||
sequence_max = sequence_max + 1
|
||||
for book, bookId in db_books.items():
|
||||
if bookId < sequence_min:
|
||||
# Record the new Id and write it to the DB
|
||||
db_books[book] = sequence_max
|
||||
sequence_max = sequence_max + 1
|
||||
|
||||
# Insert sequence numbers for tables we will be manipulating, if they don't already exist
|
||||
query = ('INSERT INTO sqlite_sequence (name, seq) '
|
||||
'SELECT ?, ? '
|
||||
'WHERE NOT EXISTS (SELECT 1 FROM sqlite_sequence WHERE name = ?)');
|
||||
cursor.execute(query, ('books',sdcard_sequence_start,'books',))
|
||||
cursor.execute(query, ('collection',sdcard_sequence_start,'collection',))
|
||||
cursor.execute(query, ('collections',sdcard_sequence_start,'collections',))
|
||||
|
||||
# Fix the Books DB
|
||||
query = 'UPDATE books SET _id = ? WHERE file_path = ?'
|
||||
t = (db_books[book], book,)
|
||||
cursor.execute(query, t)
|
||||
|
||||
# Fix any references so that they point back to the right book
|
||||
t = (db_books[book], bookId,)
|
||||
query = 'UPDATE collections SET content_id = ? WHERE content_id = ?'
|
||||
cursor.execute(query, t)
|
||||
query = 'UPDATE annotation SET content_id = ? WHERE content_id = ?'
|
||||
cursor.execute(query, t)
|
||||
query = 'UPDATE bookmark SET content_id = ? WHERE content_id = ?'
|
||||
cursor.execute(query, t)
|
||||
query = 'UPDATE current_position SET content_id = ? WHERE content_id = ?'
|
||||
cursor.execute(query, t)
|
||||
query = 'UPDATE deleted_markups SET content_id = ? WHERE content_id = ?'
|
||||
cursor.execute(query, t)
|
||||
query = 'UPDATE dic_histories SET content_id = ? WHERE content_id = ?'
|
||||
cursor.execute(query, t)
|
||||
query = 'UPDATE freehand SET content_id = ? WHERE content_id = ?'
|
||||
cursor.execute(query, t)
|
||||
query = 'UPDATE history SET content_id = ? WHERE content_id = ?'
|
||||
cursor.execute(query, t)
|
||||
query = 'UPDATE layout_cache SET content_id = ? WHERE content_id = ?'
|
||||
cursor.execute(query, t)
|
||||
query = 'UPDATE preference SET content_id = ? WHERE content_id = ?'
|
||||
cursor.execute(query, t)
|
||||
|
||||
self.set_database_sequence_id(connection, 'books', sequence_max)
|
||||
debug_print("Book Sequence Max: %d, Source Id: %d"%(sequence_max,source_id))
|
||||
|
||||
cursor.close()
|
||||
return db_books
|
||||
|
||||
def update_device_books(self, connection, booklist, source_id, plugboard,
|
||||
dbpath):
|
||||
opts = self.settings()
|
||||
upload_covers = opts.extra_customization[self.OPT_UPLOAD_COVERS]
|
||||
refresh_covers = opts.extra_customization[self.OPT_REFRESH_COVERS]
|
||||
use_sony_authors = opts.extra_customization[self.OPT_USE_SONY_AUTHORS]
|
||||
|
||||
db_books = self.read_device_books(connection, source_id, dbpath)
|
||||
cursor = connection.cursor()
|
||||
|
||||
for book in booklist:
|
||||
# Run through plugboard if needed
|
||||
@ -365,10 +480,10 @@ class PRST1(USBMS):
|
||||
modified_date, lpath,
|
||||
os.path.basename(lpath), book.size, book.mime)
|
||||
cursor.execute(query, t)
|
||||
book.bookId = cursor.lastrowid
|
||||
book.bookId = self.get_lastrowid(cursor)
|
||||
if upload_covers:
|
||||
self.upload_book_cover(connection, book, source_id)
|
||||
debug_print('Inserted New Book: ' + book.title)
|
||||
debug_print('Inserted New Book: (%u) '%book.bookId + book.title)
|
||||
else:
|
||||
query = '''
|
||||
UPDATE books
|
||||
@ -400,26 +515,111 @@ class PRST1(USBMS):
|
||||
connection.commit()
|
||||
cursor.close()
|
||||
|
||||
def update_device_collections(self, connection, booklist, collections,
|
||||
source_id):
|
||||
cursor = connection.cursor()
|
||||
def read_device_collections(self, connection, source_id, dbpath):
|
||||
from sqlite3 import DatabaseError
|
||||
|
||||
sequence_min = self.get_database_min_id(source_id)
|
||||
sequence_max = sequence_min
|
||||
sequence_dirty = 0
|
||||
|
||||
debug_print("Collection Sequence Min: %d, Source Id: %d"%(sequence_min,source_id))
|
||||
|
||||
try:
|
||||
cursor = connection.cursor()
|
||||
|
||||
if collections:
|
||||
# Get existing collections
|
||||
query = 'SELECT _id, title FROM collection'
|
||||
cursor.execute(query)
|
||||
except DatabaseError:
|
||||
import traceback
|
||||
tb = traceback.format_exc()
|
||||
raise DeviceError((('The SONY database is corrupted. '
|
||||
' Delete the file %s on your reader and then disconnect '
|
||||
' reconnect it. If you are using an SD card, you '
|
||||
' should delete the file on the card as well. Note that '
|
||||
' deleting this file will cause your reader to forget '
|
||||
' any notes/highlights, etc.')%dbpath)+' Underlying error:'
|
||||
'\n'+tb)
|
||||
|
||||
db_collections = {}
|
||||
for i, row in enumerate(cursor):
|
||||
db_collections[row[1]] = row[0]
|
||||
db_collections = {}
|
||||
for i, row in enumerate(cursor):
|
||||
db_collections[row[1]] = row[0]
|
||||
if row[0] < sequence_min:
|
||||
sequence_dirty = 1
|
||||
else:
|
||||
sequence_max = max(sequence_max, row[0])
|
||||
|
||||
# If the database is 'dirty', then we should fix up the Ids and the sequence number
|
||||
if sequence_dirty == 1:
|
||||
debug_print("Collection Sequence Dirty for Source Id: %d"%source_id)
|
||||
sequence_max = sequence_max + 1
|
||||
for collection, collectionId in db_collections.items():
|
||||
if collectionId < sequence_min:
|
||||
# Record the new Id and write it to the DB
|
||||
db_collections[collection] = sequence_max
|
||||
sequence_max = sequence_max + 1
|
||||
|
||||
# Fix the collection DB
|
||||
query = 'UPDATE collection SET _id = ? WHERE title = ?'
|
||||
t = (db_collections[collection], collection, )
|
||||
cursor.execute(query, t)
|
||||
|
||||
# Fix any references in existing collections
|
||||
query = 'UPDATE collections SET collection_id = ? WHERE collection_id = ?'
|
||||
t = (db_collections[collection], collectionId,)
|
||||
cursor.execute(query, t)
|
||||
|
||||
self.set_database_sequence_id(connection, 'collection', sequence_max)
|
||||
debug_print("Collection Sequence Max: %d, Source Id: %d"%(sequence_max,source_id))
|
||||
|
||||
# Fix up the collections table now...
|
||||
sequence_dirty = 0
|
||||
sequence_max = sequence_min
|
||||
|
||||
debug_print("Collections Sequence Min: %d, Source Id: %d"%(sequence_min,source_id))
|
||||
|
||||
query = 'SELECT _id FROM collections'
|
||||
cursor.execute(query)
|
||||
|
||||
db_collection_pairs = []
|
||||
for i, row in enumerate(cursor):
|
||||
db_collection_pairs.append(row[0])
|
||||
if row[0] < sequence_min:
|
||||
sequence_dirty = 1
|
||||
else:
|
||||
sequence_max = max(sequence_max, row[0])
|
||||
|
||||
if sequence_dirty == 1:
|
||||
debug_print("Collections Sequence Dirty for Source Id: %d"%source_id)
|
||||
sequence_max = sequence_max + 1
|
||||
for pairId in db_collection_pairs:
|
||||
if pairId < sequence_min:
|
||||
# Record the new Id and write it to the DB
|
||||
query = 'UPDATE collections SET _id = ? WHERE _id = ?'
|
||||
t = (sequence_max, pairId,)
|
||||
cursor.execute(query, t)
|
||||
sequence_max = sequence_max + 1
|
||||
|
||||
self.set_database_sequence_id(connection, 'collections', sequence_max)
|
||||
debug_print("Collections Sequence Max: %d, Source Id: %d"%(sequence_max,source_id))
|
||||
|
||||
cursor.close()
|
||||
return db_collections
|
||||
|
||||
def update_device_collections(self, connection, booklist, collections,
|
||||
source_id, dbpath):
|
||||
|
||||
if collections:
|
||||
db_collections = self.read_device_collections(connection, source_id, dbpath)
|
||||
cursor = connection.cursor()
|
||||
|
||||
for collection, books in collections.items():
|
||||
if collection not in db_collections:
|
||||
query = 'INSERT INTO collection (title, source_id) VALUES (?,?)'
|
||||
t = (collection, source_id)
|
||||
cursor.execute(query, t)
|
||||
db_collections[collection] = cursor.lastrowid
|
||||
debug_print('Inserted New Collection: ' + collection)
|
||||
db_collections[collection] = self.get_lastrowid(cursor)
|
||||
debug_print('Inserted New Collection: (%u) '%db_collections[collection] + collection)
|
||||
|
||||
# Get existing books in collection
|
||||
query = '''
|
||||
@ -483,9 +683,8 @@ class PRST1(USBMS):
|
||||
cursor.execute(query, t)
|
||||
debug_print('Deleted Collection: ' + collection)
|
||||
|
||||
|
||||
connection.commit()
|
||||
cursor.close()
|
||||
connection.commit()
|
||||
cursor.close()
|
||||
|
||||
def rebuild_collections(self, booklist, oncard):
|
||||
debug_print('PRST1: starting rebuild_collections')
|
||||
|
@ -8,6 +8,7 @@ manner.
|
||||
import sys, os, re
|
||||
from threading import RLock
|
||||
|
||||
from calibre import prints, as_unicode
|
||||
from calibre.constants import iswindows, isosx, plugins, islinux, isfreebsd
|
||||
|
||||
osx_scanner = win_scanner = linux_scanner = None
|
||||
@ -53,7 +54,10 @@ class WinPNPScanner(object):
|
||||
ans = True
|
||||
try:
|
||||
win32file.GetDiskFreeSpaceEx(letter+':\\')
|
||||
except:
|
||||
except Exception as e:
|
||||
if debug:
|
||||
prints('Unable to get free space for drive:', letter)
|
||||
prints(as_unicode(e))
|
||||
ans = False
|
||||
return ans
|
||||
finally:
|
||||
|
@ -30,6 +30,8 @@ class DeviceConfig(object):
|
||||
SUPPORTS_SUB_DIRS = False
|
||||
SUPPORTS_SUB_DIRS_FOR_SCAN = False # This setting is used when scanning for
|
||||
# books when SUPPORTS_SUB_DIRS is False
|
||||
SUPPORTS_SUB_DIRS_DEFAULT = True
|
||||
|
||||
MUST_READ_METADATA = False
|
||||
SUPPORTS_USE_AUTHOR_SORT = False
|
||||
|
||||
@ -57,7 +59,7 @@ class DeviceConfig(object):
|
||||
c = Config('device_drivers_%s' % name, _('settings for device drivers'))
|
||||
c.add_opt('format_map', default=cls.FORMATS,
|
||||
help=_('Ordered list of formats the device will accept'))
|
||||
c.add_opt('use_subdirs', default=True,
|
||||
c.add_opt('use_subdirs', default=cls.SUPPORTS_SUB_DIRS_DEFAULT,
|
||||
help=_('Place files in sub directories if the device supports them'))
|
||||
c.add_opt('read_metadata', default=True,
|
||||
help=_('Read metadata from files on device'))
|
||||
|
@ -382,7 +382,8 @@ class USBMS(CLI, Device):
|
||||
os.makedirs(self.normalize_path(self._main_prefix))
|
||||
|
||||
def write_prefix(prefix, listid):
|
||||
if prefix is not None and isinstance(booklists[listid], self.booklist_class):
|
||||
if (prefix is not None and len(booklists) > listid and
|
||||
isinstance(booklists[listid], self.booklist_class)):
|
||||
if not os.path.exists(prefix):
|
||||
os.makedirs(self.normalize_path(prefix))
|
||||
with open(self.normalize_path(os.path.join(prefix, self.METADATA_CACHE)), 'wb') as f:
|
||||
|
@ -8,6 +8,8 @@ from itertools import cycle
|
||||
|
||||
from calibre.customize.conversion import InputFormatPlugin, OptionRecommendation
|
||||
|
||||
ADOBE_OBFUSCATION = 'http://ns.adobe.com/pdf/enc#RC'
|
||||
|
||||
class EPUBInput(InputFormatPlugin):
|
||||
|
||||
name = 'EPUB Input'
|
||||
@ -18,18 +20,24 @@ class EPUBInput(InputFormatPlugin):
|
||||
|
||||
recommendations = set([('page_breaks_before', '/', OptionRecommendation.MED)])
|
||||
|
||||
def decrypt_font(self, key, path):
|
||||
raw = open(path, 'rb').read()
|
||||
crypt = raw[:1024]
|
||||
key = cycle(iter(key))
|
||||
decrypt = ''.join([chr(ord(x)^key.next()) for x in crypt])
|
||||
def decrypt_font(self, key, path, algorithm):
|
||||
is_adobe = algorithm == ADOBE_OBFUSCATION
|
||||
crypt_len = 1024 if is_adobe else 1040
|
||||
with open(path, 'rb') as f:
|
||||
raw = f.read()
|
||||
crypt = bytearray(raw[:crypt_len])
|
||||
key = cycle(iter(bytearray(key)))
|
||||
decrypt = bytes(bytearray(x^key.next() for x in crypt))
|
||||
with open(path, 'wb') as f:
|
||||
f.write(decrypt)
|
||||
f.write(raw[1024:])
|
||||
f.write(raw[crypt_len:])
|
||||
|
||||
def process_encryption(self, encfile, opf, log):
|
||||
from lxml import etree
|
||||
import uuid
|
||||
import uuid, hashlib
|
||||
idpf_key = opf.unique_identifier
|
||||
if idpf_key:
|
||||
idpf_key = hashlib.sha1(idpf_key).digest()
|
||||
key = None
|
||||
for item in opf.identifier_iter():
|
||||
scheme = None
|
||||
@ -39,8 +47,8 @@ class EPUBInput(InputFormatPlugin):
|
||||
if (scheme and scheme.lower() == 'uuid') or \
|
||||
(item.text and item.text.startswith('urn:uuid:')):
|
||||
try:
|
||||
key = str(item.text).rpartition(':')[-1]
|
||||
key = list(map(ord, uuid.UUID(key).bytes))
|
||||
key = bytes(item.text).rpartition(':')[-1]
|
||||
key = uuid.UUID(key).bytes
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
@ -50,14 +58,16 @@ class EPUBInput(InputFormatPlugin):
|
||||
root = etree.parse(encfile)
|
||||
for em in root.xpath('descendant::*[contains(name(), "EncryptionMethod")]'):
|
||||
algorithm = em.get('Algorithm', '')
|
||||
if algorithm != 'http://ns.adobe.com/pdf/enc#RC':
|
||||
if algorithm not in {ADOBE_OBFUSCATION,
|
||||
'http://www.idpf.org/2008/embedding'}:
|
||||
return False
|
||||
cr = em.getparent().xpath('descendant::*[contains(name(), "CipherReference")]')[0]
|
||||
uri = cr.get('URI')
|
||||
path = os.path.abspath(os.path.join(os.path.dirname(encfile), '..', *uri.split('/')))
|
||||
if key is not None and os.path.exists(path):
|
||||
tkey = (key if algorithm == ADOBE_OBFUSCATION else idpf_key)
|
||||
if (tkey and os.path.exists(path)):
|
||||
self._encrypted_font_uris.append(uri)
|
||||
self.decrypt_font(key, path)
|
||||
self.decrypt_font(tkey, path, algorithm)
|
||||
return True
|
||||
except:
|
||||
import traceback
|
||||
|
@ -223,6 +223,8 @@ class MOBIOutput(OutputFormatPlugin):
|
||||
else:
|
||||
# Add rasterized SVG images
|
||||
resources.add_extra_images()
|
||||
if hasattr(self.oeb, 'inserted_metadata_jacket'):
|
||||
self.workaround_fire_bugs(self.oeb.inserted_metadata_jacket)
|
||||
mobimlizer = MobiMLizer(ignore_tables=opts.linearize_tables)
|
||||
mobimlizer(oeb, opts)
|
||||
write_page_breaks_after_item = input_plugin is not plugin_for_input_format('cbz')
|
||||
@ -236,6 +238,18 @@ class MOBIOutput(OutputFormatPlugin):
|
||||
from calibre.ebooks.mobi.writer8.cleanup import CSSCleanup
|
||||
CSSCleanup(log, opts)(item, stylizer)
|
||||
|
||||
def workaround_fire_bugs(self, jacket):
|
||||
# The idiotic Fire crashes when trying to render the table used to
|
||||
# layout the jacket
|
||||
from calibre.ebooks.oeb.base import XHTML
|
||||
for table in jacket.data.xpath('//*[local-name()="table"]'):
|
||||
table.tag = XHTML('div')
|
||||
for tr in table.xpath('descendant::*[local-name()="tr"]'):
|
||||
cols = tr.xpath('descendant::*[local-name()="td"]')
|
||||
tr.tag = XHTML('div')
|
||||
for td in cols:
|
||||
td.tag = XHTML('span' if cols else 'div')
|
||||
|
||||
class AZW3Output(OutputFormatPlugin):
|
||||
|
||||
name = 'AZW3 Output'
|
||||
|
89
src/calibre/ebooks/metadata/docx.py
Normal file
89
src/calibre/ebooks/metadata/docx.py
Normal file
@ -0,0 +1,89 @@
|
||||
#!/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'
|
||||
|
||||
from lxml import etree
|
||||
|
||||
from calibre.ebooks.metadata.book.base import Metadata
|
||||
from calibre.utils.zipfile import ZipFile
|
||||
from calibre.utils.magick.draw import identify_data
|
||||
from calibre.ebooks.oeb.base import DC11_NS
|
||||
from calibre.ebooks.oeb.parse_utils import RECOVER_PARSER
|
||||
|
||||
NSMAP = {'dc':DC11_NS,
|
||||
'cp':'http://schemas.openxmlformats.org/package/2006/metadata/core-properties'}
|
||||
|
||||
def XPath(expr):
|
||||
return etree.XPath(expr, namespaces=NSMAP)
|
||||
|
||||
def _read_doc_props(raw, mi):
|
||||
from calibre.ebooks.metadata import string_to_authors
|
||||
root = etree.fromstring(raw, parser=RECOVER_PARSER)
|
||||
titles = XPath('//dc:title')(root)
|
||||
if titles:
|
||||
title = titles[0].text
|
||||
if title and title.strip():
|
||||
mi.title = title.strip()
|
||||
tags = []
|
||||
for subject in XPath('//dc:subject')(root):
|
||||
if subject.text and subject.text.strip():
|
||||
tags.append(subject.text.strip().replace(',', '_'))
|
||||
for keywords in XPath('//cp:keywords')(root):
|
||||
if keywords.text and keywords.text.strip():
|
||||
for x in keywords.text.split():
|
||||
tags.extend(y.strip() for y in x.split(','))
|
||||
if tags:
|
||||
mi.tags = tags
|
||||
authors = XPath('//dc:creator')(root)
|
||||
aut = []
|
||||
for author in authors:
|
||||
if author.text and author.text.strip():
|
||||
aut.extend(string_to_authors(author.text))
|
||||
if aut:
|
||||
mi.authors = aut
|
||||
|
||||
desc = XPath('//dc:description')(root)
|
||||
if desc:
|
||||
raw = etree.tostring(desc[0], method='text', encoding=unicode)
|
||||
mi.comments = raw
|
||||
|
||||
def _read_app_props(raw, mi):
|
||||
root = etree.fromstring(raw, parser=RECOVER_PARSER)
|
||||
company = root.xpath('//*[local-name()="Company"]')
|
||||
if company and company[0].text and company[0].text.strip():
|
||||
mi.publisher = company[0].text.strip()
|
||||
|
||||
def get_metadata(stream):
|
||||
with ZipFile(stream, 'r') as zf:
|
||||
|
||||
mi = Metadata(_('Unknown'))
|
||||
cdata = None
|
||||
|
||||
for zi in zf.infolist():
|
||||
ext = zi.filename.rpartition('.')[-1].lower()
|
||||
if zi.filename.lower() == 'docprops/core.xml':
|
||||
_read_doc_props(zf.read(zi), mi)
|
||||
elif zi.filename.lower() == 'docprops/app.xml':
|
||||
_read_app_props(zf.read(zi), mi)
|
||||
elif cdata is None and ext in {'jpeg', 'jpg', 'png', 'gif'}:
|
||||
raw = zf.read(zi)
|
||||
try:
|
||||
width, height, fmt = identify_data(raw)
|
||||
except:
|
||||
continue
|
||||
if 0.8 <= height/width <= 1.8 and height*width >= 12000:
|
||||
cdata = (fmt, raw)
|
||||
if cdata is not None:
|
||||
mi.cover_data = cdata
|
||||
|
||||
return mi
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
with open(sys.argv[-1], 'rb') as stream:
|
||||
print (get_metadata(stream))
|
@ -991,6 +991,21 @@ class OPF(object): # {{{
|
||||
for item in self.identifier_path(self.metadata):
|
||||
yield item
|
||||
|
||||
@property
|
||||
def unique_identifier(self):
|
||||
uuid_elem = None
|
||||
for attr in self.root.attrib:
|
||||
if attr.endswith('unique-identifier'):
|
||||
uuid_elem = self.root.attrib[attr]
|
||||
break
|
||||
if uuid_elem:
|
||||
matches = self.root.xpath('//*[@id=%r]'%uuid_elem)
|
||||
if matches:
|
||||
for m in matches:
|
||||
raw = m.text
|
||||
if raw:
|
||||
return raw.rpartition(':')[-1]
|
||||
|
||||
def guess_cover(self):
|
||||
'''
|
||||
Try to guess a cover. Needed for some old/badly formed OPF files.
|
||||
|
@ -338,8 +338,15 @@ class OEBReader(object):
|
||||
href = elem.get('href')
|
||||
path = urlnormalize(urldefrag(href)[0])
|
||||
if path not in manifest.hrefs:
|
||||
self.logger.warn(u'Guide reference %r not found' % href)
|
||||
continue
|
||||
corrected_href = None
|
||||
for href in manifest.hrefs:
|
||||
if href.lower() == path.lower():
|
||||
corrected_href = href
|
||||
break
|
||||
if corrected_href is None:
|
||||
self.logger.warn(u'Guide reference %r not found' % href)
|
||||
continue
|
||||
href = corrected_href
|
||||
guide.add(elem.get('type'), elem.get('title'), href)
|
||||
|
||||
def _find_ncx(self, opf):
|
||||
|
@ -15,10 +15,10 @@ class Clean(object):
|
||||
|
||||
if 'cover' not in self.oeb.guide:
|
||||
covers = []
|
||||
for x in ('other.ms-coverimage-standard',
|
||||
for x in ('other.ms-coverimage-standard', 'coverimagestandard',
|
||||
'other.ms-titleimage-standard', 'other.ms-titleimage',
|
||||
'other.ms-coverimage', 'other.ms-thumbimage-standard',
|
||||
'other.ms-thumbimage'):
|
||||
'other.ms-thumbimage', 'thumbimagestandard'):
|
||||
if x in self.oeb.guide:
|
||||
href = self.oeb.guide[x].href
|
||||
item = self.oeb.manifest.hrefs[href]
|
||||
|
@ -72,6 +72,7 @@ class Jacket(object):
|
||||
|
||||
item = self.oeb.manifest.add(id, href, guess_type(href)[0], data=root)
|
||||
self.oeb.spine.insert(0, item, True)
|
||||
self.oeb.inserted_metadata_jacket = item
|
||||
|
||||
def remove_existing_jacket(self):
|
||||
for x in self.oeb.spine[:4]:
|
||||
|
@ -46,6 +46,7 @@ class SVGRasterizer(object):
|
||||
|
||||
def __call__(self, oeb, context):
|
||||
oeb.logger.info('Rasterizing SVG images...')
|
||||
self.stylizer_cache = {}
|
||||
self.oeb = oeb
|
||||
self.opts = context
|
||||
self.profile = context.dest
|
||||
@ -116,29 +117,35 @@ class SVGRasterizer(object):
|
||||
elem.attrib[XLINK('href')] = data
|
||||
return svg
|
||||
|
||||
def stylizer(self, item):
|
||||
ans = self.stylizer_cache.get(item, None)
|
||||
if ans is None:
|
||||
ans = Stylizer(item.data, item.href, self.oeb, self.opts,
|
||||
self.profile)
|
||||
self.stylizer_cache[item] = ans
|
||||
return ans
|
||||
|
||||
def rasterize_spine(self):
|
||||
for item in self.oeb.spine:
|
||||
html = item.data
|
||||
stylizer = Stylizer(html, item.href, self.oeb, self.opts, self.profile)
|
||||
self.rasterize_item(item, stylizer)
|
||||
self.rasterize_item(item)
|
||||
|
||||
def rasterize_item(self, item, stylizer):
|
||||
def rasterize_item(self, item):
|
||||
html = item.data
|
||||
hrefs = self.oeb.manifest.hrefs
|
||||
for elem in xpath(html, '//h:img[@src]'):
|
||||
src = urlnormalize(elem.attrib['src'])
|
||||
image = hrefs.get(item.abshref(src), None)
|
||||
if image and image.media_type == SVG_MIME:
|
||||
style = stylizer.style(elem)
|
||||
style = self.stylizer(item).style(elem)
|
||||
self.rasterize_external(elem, style, item, image)
|
||||
for elem in xpath(html, '//h:object[@type="%s" and @data]' % SVG_MIME):
|
||||
data = urlnormalize(elem.attrib['data'])
|
||||
image = hrefs.get(item.abshref(data), None)
|
||||
if image and image.media_type == SVG_MIME:
|
||||
style = stylizer.style(elem)
|
||||
style = self.stylizer(item).style(elem)
|
||||
self.rasterize_external(elem, style, item, image)
|
||||
for elem in xpath(html, '//svg:svg'):
|
||||
style = stylizer.style(elem)
|
||||
style = self.stylizer(item).style(elem)
|
||||
self.rasterize_inline(elem, style, item)
|
||||
|
||||
def rasterize_inline(self, elem, style, item):
|
||||
|
@ -13,7 +13,7 @@ from PyQt4.Qt import (QVariant, QFileInfo, QObject, SIGNAL, QBuffer, Qt,
|
||||
ORG_NAME = 'KovidsBrain'
|
||||
APP_UID = 'libprs500'
|
||||
from calibre.constants import (islinux, iswindows, isbsd, isfrozen, isosx,
|
||||
config_dir)
|
||||
config_dir, filesystem_encoding)
|
||||
from calibre.utils.config import Config, ConfigProxy, dynamic, JSONConfig
|
||||
from calibre.ebooks.metadata import MetaInformation
|
||||
from calibre.utils.date import UNDEFINED_DATE
|
||||
@ -471,6 +471,7 @@ class FileIconProvider(QFileIconProvider):
|
||||
'djvu' : 'djvu',
|
||||
'xps' : 'xps',
|
||||
'oxps' : 'xps',
|
||||
'docx' : 'docx',
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
@ -719,7 +720,7 @@ gui_thread = None
|
||||
qt_app = None
|
||||
class Application(QApplication):
|
||||
|
||||
def __init__(self, args):
|
||||
def __init__(self, args, force_calibre_style=False):
|
||||
self.file_event_hook = None
|
||||
qargs = [i.encode('utf-8') if isinstance(i, unicode) else i for i in args]
|
||||
QApplication.__init__(self, qargs)
|
||||
@ -730,6 +731,48 @@ class Application(QApplication):
|
||||
qt_app = self
|
||||
self._file_open_paths = []
|
||||
self._file_open_lock = RLock()
|
||||
self.setup_styles(force_calibre_style)
|
||||
|
||||
def load_calibre_style(self):
|
||||
# On OS X QtCurve resets the palette, so we preserve it explicitly
|
||||
orig_pal = QPalette(self.palette())
|
||||
from calibre.constants import plugins
|
||||
pi = plugins['progress_indicator'][0]
|
||||
path = os.path.join(sys.extensions_location, 'calibre_style.'+(
|
||||
'pyd' if iswindows else 'so'))
|
||||
pi.load_style(path, 'Calibre')
|
||||
self.setPalette(orig_pal)
|
||||
style = self.style()
|
||||
icon_map = {}
|
||||
pcache = {}
|
||||
for k, v in {
|
||||
'DialogYesButton': u'ok.png',
|
||||
'DialogNoButton': u'window-close.png',
|
||||
'DialogCloseButton': u'window-close.png',
|
||||
'DialogOkButton': u'ok.png',
|
||||
'DialogCancelButton': u'window-close.png',
|
||||
'DialogHelpButton': u'help.png',
|
||||
'DialogOpenButton': u'document_open.png',
|
||||
'DialogSaveButton': u'save.png',
|
||||
'DialogApplyButton': u'ok.png',
|
||||
'DialogDiscardButton': u'trash.png',
|
||||
'MessageBoxInformation': u'dialog_information.png',
|
||||
'MessageBoxWarning': u'dialog_warning.png',
|
||||
'MessageBoxCritical': u'dialog_error.png',
|
||||
'MessageBoxQuestion': u'dialog_question.png',
|
||||
}.iteritems():
|
||||
if v not in pcache:
|
||||
p = I(v)
|
||||
if isinstance(p, bytes):
|
||||
p = p.decode(filesystem_encoding)
|
||||
# if not os.path.exists(p): raise ValueError(p)
|
||||
pcache[v] = p
|
||||
v = pcache[v]
|
||||
icon_map[type('')(getattr(style, 'SP_'+k))] = v
|
||||
style.setProperty(u'calibre_icon_map', icon_map)
|
||||
self.__icon_map_memory_ = icon_map
|
||||
|
||||
def setup_styles(self, force_calibre_style):
|
||||
self.original_font = QFont(QApplication.font())
|
||||
fi = gprefs['font']
|
||||
if fi is not None:
|
||||
@ -738,14 +781,9 @@ class Application(QApplication):
|
||||
if s is not None:
|
||||
font.setStretch(s)
|
||||
QApplication.setFont(font)
|
||||
self.setup_styles()
|
||||
|
||||
def setup_styles(self):
|
||||
if gprefs['widget_style'] != 'system':
|
||||
# On OS X QtCurve resets the palette, so we preserve it explicitly
|
||||
orig_pal = QPalette(self.palette())
|
||||
QApplication.setStyle('QtCurve')
|
||||
self.setPalette(orig_pal)
|
||||
if force_calibre_style or gprefs['widget_style'] != 'system':
|
||||
self.load_calibre_style()
|
||||
else:
|
||||
st = self.style()
|
||||
if st is not None:
|
||||
@ -753,12 +791,8 @@ class Application(QApplication):
|
||||
if (islinux or isbsd) and st in ('windows', 'motif', 'cde'):
|
||||
from PyQt4.Qt import QStyleFactory
|
||||
styles = set(map(unicode, QStyleFactory.keys()))
|
||||
if 'QtCurve' in styles and os.environ.get('KDE_FULL_SESSION',
|
||||
False):
|
||||
self.setStyle('QtCurve')
|
||||
elif 'Plastique' in styles and os.environ.get('KDE_FULL_SESSION',
|
||||
False):
|
||||
self.setStyle('Plastique')
|
||||
if os.environ.get('KDE_FULL_SESSION', False):
|
||||
self.load_calibre_style()
|
||||
elif 'Cleanlooks' in styles:
|
||||
self.setStyle('Cleanlooks')
|
||||
|
||||
|
@ -116,7 +116,7 @@ class DeleteAction(InterfaceAction):
|
||||
for action in list(self.delete_menu.actions())[1:]:
|
||||
action.setEnabled(enabled)
|
||||
|
||||
def _get_selected_formats(self, msg, ids, exclude=False):
|
||||
def _get_selected_formats(self, msg, ids, exclude=False, single=False):
|
||||
from calibre.gui2.dialogs.select_formats import SelectFormats
|
||||
c = Counter()
|
||||
db = self.gui.library_view.model().db
|
||||
@ -125,7 +125,8 @@ class DeleteAction(InterfaceAction):
|
||||
if fmts_:
|
||||
for x in frozenset([x.lower() for x in fmts_.split(',')]):
|
||||
c[x] += 1
|
||||
d = SelectFormats(c, msg, parent=self.gui, exclude=exclude)
|
||||
d = SelectFormats(c, msg, parent=self.gui, exclude=exclude,
|
||||
single=single)
|
||||
if d.exec_() != d.Accepted:
|
||||
return None
|
||||
return d.selected_formats
|
||||
|
@ -8,30 +8,11 @@ __docformat__ = 'restructuredtext en'
|
||||
import os
|
||||
from functools import partial
|
||||
|
||||
from PyQt4.Qt import QMenu, pyqtSignal
|
||||
|
||||
from calibre.utils.config import prefs
|
||||
from calibre.gui2 import (error_dialog, Dispatcher, gprefs,
|
||||
choose_dir, warning_dialog, open_local_file)
|
||||
from calibre.gui2.actions import InterfaceAction
|
||||
from calibre.ebooks import BOOK_EXTENSIONS
|
||||
|
||||
class SaveMenu(QMenu): # {{{
|
||||
|
||||
save_fmt = pyqtSignal(object)
|
||||
|
||||
def __init__(self, parent):
|
||||
QMenu.__init__(self, _('Save single format to disk...'), parent)
|
||||
for ext in sorted(BOOK_EXTENSIONS):
|
||||
action = self.addAction(ext.upper())
|
||||
setattr(self, 'do_'+ext, partial(self.do, ext))
|
||||
action.triggered.connect(
|
||||
getattr(self, 'do_'+ext))
|
||||
|
||||
def do(self, ext, *args):
|
||||
self.save_fmt.emit(ext)
|
||||
|
||||
# }}}
|
||||
|
||||
class SaveToDiskAction(InterfaceAction):
|
||||
|
||||
@ -54,9 +35,8 @@ class SaveToDiskAction(InterfaceAction):
|
||||
_('Save only %s format to disk in a single directory')%
|
||||
prefs['output_format'].upper(),
|
||||
triggered=partial(self.save_single_fmt_to_single_dir, False))
|
||||
self.save_sub_menu = SaveMenu(self.gui)
|
||||
self.save_sub_menu_action = self.save_menu.addMenu(self.save_sub_menu)
|
||||
self.save_sub_menu.save_fmt.connect(self.save_specific_format_disk)
|
||||
cm('specific format', _('Save single format to disk...'),
|
||||
triggered=self.save_specific_format_disk)
|
||||
|
||||
def location_selected(self, loc):
|
||||
enabled = loc == 'library'
|
||||
@ -74,8 +54,17 @@ class SaveToDiskAction(InterfaceAction):
|
||||
def save_single_format_to_disk(self, checked):
|
||||
self.save_to_disk(checked, False, prefs['output_format'])
|
||||
|
||||
def save_specific_format_disk(self, fmt):
|
||||
self.save_to_disk(False, False, fmt)
|
||||
def save_specific_format_disk(self):
|
||||
rb = self.gui.iactions['Remove Books']
|
||||
ids = rb._get_selected_ids(err_title=
|
||||
_('Cannot save to disk'))
|
||||
if not ids: return
|
||||
fmts = rb._get_selected_formats(
|
||||
_('Choose format to save to disk'), ids,
|
||||
single=True)
|
||||
if not fmts:
|
||||
return
|
||||
self.save_to_disk(False, False, list(fmts)[0])
|
||||
|
||||
def save_to_single_dir(self, checked):
|
||||
self.save_to_disk(checked, True)
|
||||
|
@ -50,6 +50,7 @@ class SelectFormats(QDialog):
|
||||
def __init__(self, fmt_count, msg, single=False, parent=None, exclude=False):
|
||||
QDialog.__init__(self, parent)
|
||||
self._l = QVBoxLayout(self)
|
||||
self.single_fmt = single
|
||||
self.setLayout(self._l)
|
||||
self.setWindowTitle(_('Choose formats'))
|
||||
self._m = QLabel(msg)
|
||||
@ -57,6 +58,8 @@ class SelectFormats(QDialog):
|
||||
self._l.addWidget(self._m)
|
||||
self.formats = Formats(fmt_count)
|
||||
self.fview = QListView(self)
|
||||
self.fview.doubleClicked.connect(self.double_clicked,
|
||||
type=Qt.QueuedConnection)
|
||||
if exclude:
|
||||
self.fview.setStyleSheet('''
|
||||
QListView { background-color: #FAE7B5}
|
||||
@ -82,6 +85,11 @@ class SelectFormats(QDialog):
|
||||
self.selected_formats.add(self.formats.fmt(idx))
|
||||
QDialog.accept(self, *args)
|
||||
|
||||
def double_clicked(self, index):
|
||||
if self.single_fmt:
|
||||
self.accept()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from PyQt4.Qt import QApplication
|
||||
app = QApplication([])
|
||||
|
@ -13,7 +13,7 @@ from datetime import datetime
|
||||
from PyQt4.Qt import (Qt, QVBoxLayout, QHBoxLayout, QWidget, QPushButton,
|
||||
QGridLayout, pyqtSignal, QDialogButtonBox, QScrollArea, QFont,
|
||||
QTabWidget, QIcon, QToolButton, QSplitter, QGroupBox, QSpacerItem,
|
||||
QSizePolicy, QPalette, QFrame, QSize, QKeySequence, QMenu, QShortcut)
|
||||
QSizePolicy, QFrame, QSize, QKeySequence, QMenu, QShortcut)
|
||||
|
||||
from calibre.ebooks.metadata import authors_to_string, string_to_authors
|
||||
from calibre.gui2 import ResizableDialog, error_dialog, gprefs, pixmap_to_data
|
||||
@ -57,9 +57,7 @@ class MetadataSingleDialogBase(ResizableDialog):
|
||||
if sc:
|
||||
self.download_shortcut.setKey(sc[0])
|
||||
|
||||
self.button_box = QDialogButtonBox(
|
||||
QDialogButtonBox.Ok|QDialogButtonBox.Cancel, Qt.Horizontal,
|
||||
self)
|
||||
self.button_box = bb = QDialogButtonBox(self)
|
||||
self.button_box.accepted.connect(self.accept)
|
||||
self.button_box.rejected.connect(self.reject)
|
||||
self.next_button = QPushButton(QIcon(I('forward.png')), _('Next'),
|
||||
@ -70,9 +68,11 @@ class MetadataSingleDialogBase(ResizableDialog):
|
||||
self)
|
||||
self.prev_button.setShortcut(QKeySequence('Alt+Left'))
|
||||
|
||||
self.button_box.addButton(self.prev_button, self.button_box.ActionRole)
|
||||
self.button_box.addButton(self.next_button, self.button_box.ActionRole)
|
||||
self.button_box.addButton(self.prev_button, bb.ActionRole)
|
||||
self.button_box.addButton(self.next_button, bb.ActionRole)
|
||||
self.prev_button.clicked.connect(self.prev_clicked)
|
||||
bb.setStandardButtons(bb.Ok|bb.Cancel)
|
||||
bb.button(bb.Ok).setDefault(True)
|
||||
|
||||
self.scroll_area = QScrollArea(self)
|
||||
self.scroll_area.setFrameShape(QScrollArea.NoFrame)
|
||||
@ -87,7 +87,6 @@ class MetadataSingleDialogBase(ResizableDialog):
|
||||
self.l.addLayout(ll)
|
||||
ll.addSpacing(10)
|
||||
ll.addWidget(self.button_box)
|
||||
ll.addSpacing(10)
|
||||
|
||||
self.setWindowIcon(QIcon(I('edit_input.png')))
|
||||
self.setWindowTitle(BASE_TITLE)
|
||||
@ -97,7 +96,6 @@ class MetadataSingleDialogBase(ResizableDialog):
|
||||
if len(self.db.custom_column_label_map):
|
||||
self.create_custom_metadata_widgets()
|
||||
|
||||
|
||||
self.do_layout()
|
||||
geom = gprefs.get('metasingle_window_geometry3', None)
|
||||
if geom is not None:
|
||||
@ -508,15 +506,16 @@ class MetadataSingleDialogBase(ResizableDialog):
|
||||
tip = (_('Save changes and edit the metadata of %s')+
|
||||
' [Alt+Right]')%next_
|
||||
self.next_button.setToolTip(tip)
|
||||
self.next_button.setVisible(next_ is not None)
|
||||
self.next_button.setEnabled(next_ is not None)
|
||||
if prev is not None:
|
||||
tip = (_('Save changes and edit the metadata of %s')+
|
||||
' [Alt+Left]')%prev
|
||||
self.prev_button.setToolTip(tip)
|
||||
self.prev_button.setVisible(prev is not None)
|
||||
self.prev_button.setEnabled(prev is not None)
|
||||
self.button_box.button(self.button_box.Ok).setDefault(True)
|
||||
self.button_box.button(self.button_box.Ok).setFocus(Qt.OtherFocusReason)
|
||||
self(self.db.id(self.row_list[self.current_row]))
|
||||
|
||||
|
||||
def break_cycles(self):
|
||||
# Break any reference cycles that could prevent python
|
||||
# from garbage collecting this dialog
|
||||
@ -785,7 +784,6 @@ class MetadataSingleDialogAlt1(MetadataSingleDialogBase): # {{{
|
||||
gb.setLayout(gbl)
|
||||
sr = QScrollArea(tab0)
|
||||
sr.setWidgetResizable(True)
|
||||
sr.setBackgroundRole(QPalette.Base)
|
||||
sr.setFrameStyle(QFrame.NoFrame)
|
||||
sr.setWidget(w)
|
||||
gbl.addWidget(sr)
|
||||
@ -923,7 +921,6 @@ class MetadataSingleDialogAlt2(MetadataSingleDialogBase): # {{{
|
||||
sr = QScrollArea(gb)
|
||||
sr.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
||||
sr.setWidgetResizable(True)
|
||||
sr.setBackgroundRole(QPalette.Base)
|
||||
sr.setFrameStyle(QFrame.NoFrame)
|
||||
sr.setWidget(w)
|
||||
gbl.addWidget(sr)
|
||||
@ -985,7 +982,7 @@ def edit_metadata(db, row_list, current_row, parent=None, view_slot=None,
|
||||
return d.changed, d.rows_to_refresh
|
||||
|
||||
if __name__ == '__main__':
|
||||
from PyQt4.Qt import QApplication
|
||||
from calibre.gui2 import Application as QApplication
|
||||
app = QApplication([])
|
||||
from calibre.library import db as db_
|
||||
db = db_()
|
||||
|
@ -49,7 +49,9 @@ class RichTextDelegate(QStyledItemDelegate): # {{{
|
||||
doc = QTextDocument()
|
||||
if option is not None and option.state & QStyle.State_Selected:
|
||||
p = option.palette
|
||||
c = p.color(p.Active, p.HighlightedText)
|
||||
group = (p.Active if option.state & QStyle.State_Active else
|
||||
p.Inactive)
|
||||
c = p.color(group, p.HighlightedText)
|
||||
c = 'rgb(%d, %d, %d)'%c.getRgb()[:3]
|
||||
doc.setDefaultStyleSheet(' * { color: %s }'%c)
|
||||
doc.setHtml(index.data().toString())
|
||||
|
@ -6,7 +6,7 @@ __copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
from PyQt4.Qt import (QApplication, QFont, QFontInfo, QFontDialog,
|
||||
QAbstractListModel, Qt, QIcon, QKeySequence, QStyleFactory)
|
||||
QAbstractListModel, Qt, QIcon, QKeySequence)
|
||||
|
||||
from calibre.gui2.preferences import ConfigWidgetBase, test_widget, CommaSeparatedList
|
||||
from calibre.gui2.preferences.look_feel_ui import Ui_Form
|
||||
@ -104,11 +104,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
||||
r('widget_style', gprefs, restart_required=True, choices=
|
||||
[(_('System default'), 'system'), (_('Calibre style'),
|
||||
'calibre')])
|
||||
styles = set(map(unicode, QStyleFactory.keys()))
|
||||
if 'QtCurve' not in styles:
|
||||
# Can happen in linux
|
||||
for x in ('opt', 'label'):
|
||||
getattr(self, x+'_widget_style').setVisible(False)
|
||||
|
||||
r('cover_flow_queue_length', config, restart_required=True)
|
||||
|
||||
|
@ -206,12 +206,12 @@ class Preferences(QMainWindow):
|
||||
self.cw.layout().addWidget(self.stack)
|
||||
self.bb = QDialogButtonBox(QDialogButtonBox.Close)
|
||||
self.wizard_button = self.bb.addButton(_('Run welcome wizard'),
|
||||
self.bb.DestructiveRole)
|
||||
self.bb.ActionRole)
|
||||
self.wizard_button.setIcon(QIcon(I('wizard.png')))
|
||||
self.wizard_button.clicked.connect(self.run_wizard,
|
||||
type=Qt.QueuedConnection)
|
||||
self.bb.button(self.bb.Close).setDefault(True)
|
||||
self.cw.layout().addWidget(self.bb)
|
||||
self.bb.button(self.bb.Close).setDefault(True)
|
||||
self.bb.rejected.connect(self.close, type=Qt.QueuedConnection)
|
||||
self.setCentralWidget(self.cw)
|
||||
self.browser = Browser(self)
|
||||
@ -381,8 +381,8 @@ class Preferences(QMainWindow):
|
||||
return QMainWindow.closeEvent(self, *args)
|
||||
|
||||
if __name__ == '__main__':
|
||||
from PyQt4.Qt import QApplication
|
||||
app = QApplication([])
|
||||
from calibre.gui2 import Application
|
||||
app = Application([])
|
||||
app
|
||||
gui = init_gui()
|
||||
|
||||
|
@ -42,7 +42,7 @@ class ProceedQuestion(QDialog):
|
||||
ic.setMaximumHeight(100)
|
||||
ic.setScaledContents(True)
|
||||
ic.setStyleSheet('QLabel { margin-right: 10px }')
|
||||
self.bb = QDialogButtonBox(QDialogButtonBox.Yes|QDialogButtonBox.No)
|
||||
self.bb = QDialogButtonBox()
|
||||
self.bb.accepted.connect(self.accept)
|
||||
self.bb.rejected.connect(self.reject)
|
||||
self.log_button = self.bb.addButton(_('View log'), self.bb.ActionRole)
|
||||
@ -59,6 +59,7 @@ class ProceedQuestion(QDialog):
|
||||
_('Show detailed information about this error'))
|
||||
self.det_msg = QPlainTextEdit(self)
|
||||
self.det_msg.setReadOnly(True)
|
||||
self.bb.setStandardButtons(self.bb.Yes|self.bb.No)
|
||||
self.bb.button(self.bb.Yes).setDefault(True)
|
||||
|
||||
l.addWidget(ic, 0, 0, 1, 1)
|
||||
@ -121,10 +122,10 @@ class ProceedQuestion(QDialog):
|
||||
self.det_msg.setVisible(False)
|
||||
self.det_msg_toggle.setVisible(bool(question.det_msg))
|
||||
self.det_msg_toggle.setText(self.show_det_msg)
|
||||
self.bb.button(self.bb.Yes).setDefault(True)
|
||||
self.do_resize()
|
||||
self.bb.button(self.bb.Yes).setFocus(Qt.OtherFocusReason)
|
||||
self.show()
|
||||
self.bb.button(self.bb.Yes).setDefault(True)
|
||||
self.bb.button(self.bb.Yes).setFocus(Qt.OtherFocusReason)
|
||||
|
||||
def __call__(self, callback, payload, html_log, log_viewer_title, title,
|
||||
msg, det_msg='', show_copy_button=False, cancel_callback=None,
|
||||
@ -164,7 +165,14 @@ class ProceedQuestion(QDialog):
|
||||
self.log_viewer = ViewLog(q.log_viewer_title, log,
|
||||
parent=self)
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = QApplication([])
|
||||
ProceedQuestion(None).exec_()
|
||||
def main():
|
||||
from calibre.gui2 import Application
|
||||
app = Application([])
|
||||
p = ProceedQuestion(None)
|
||||
p(lambda p:None, None, 'ass', 'ass', 'testing', 'testing')
|
||||
p.exec_()
|
||||
app
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
|
@ -1,6 +1,10 @@
|
||||
#include "QProgressIndicator.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QStylePlugin>
|
||||
#include <QPluginLoader>
|
||||
#include <QStyle>
|
||||
#include <QApplication>
|
||||
|
||||
QProgressIndicator::QProgressIndicator(QWidget* parent, int size)
|
||||
: QWidget(parent),
|
||||
@ -122,3 +126,22 @@ void QProgressIndicator::paintEvent(QPaintEvent * /*event*/)
|
||||
p.restore();
|
||||
}
|
||||
}
|
||||
|
||||
int load_style(QString &path, QString &name) {
|
||||
int ret = 0;
|
||||
QStyle *s;
|
||||
QPluginLoader pl(path);
|
||||
QObject *o = pl.instance();
|
||||
if (o != 0) {
|
||||
QStylePlugin *sp = qobject_cast<QStylePlugin *>(o);
|
||||
if (sp != 0) {
|
||||
s = sp->create(name);
|
||||
if (s != 0) {
|
||||
s->setObjectName(name);
|
||||
QApplication::setStyle(s);
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -91,3 +91,12 @@ private:
|
||||
QColor m_color;
|
||||
};
|
||||
|
||||
/* Utility function that can be used to load a QStyle from a Qt plugin. This is
|
||||
* here so that there is no need to create a separate PyQt plugin just for this
|
||||
* simple functionality.
|
||||
* \param path The full path to the DLL containing the plugin
|
||||
* \param name The name of the style plugin to load
|
||||
* \return 1 if succeeds 0 otherwise. The objectName of the loaded style is set to name
|
||||
*/
|
||||
int load_style(QString &path, QString &name);
|
||||
|
||||
|
@ -6,6 +6,10 @@
|
||||
%Import QtCore/QtCoremod.sip
|
||||
%Import QtGui/QtGuimod.sip
|
||||
|
||||
%ModuleHeaderCode
|
||||
int load_style(QString &path, QString &name);
|
||||
%End
|
||||
|
||||
class QProgressIndicator : QWidget {
|
||||
|
||||
%TypeHeaderCode
|
||||
@ -50,3 +54,6 @@ protected:
|
||||
virtual void paintEvent(QPaintEvent * event);
|
||||
|
||||
};
|
||||
|
||||
int load_style(QString &path, QString &name);
|
||||
|
||||
|
@ -418,21 +418,24 @@ class TagsModel(QAbstractItemModel): # {{{
|
||||
chardict[c][1] = idx
|
||||
|
||||
# sort the ranges to facilitate detecting overlap
|
||||
ranges = sorted([(v[0], v[1], c) for c,v in chardict.items()])
|
||||
|
||||
# Create a list of 'first letters' to use for each item in
|
||||
# the category. The list is generated using the ranges. Overlaps
|
||||
# are filled with the character that first occurs.
|
||||
cl_list = list(repeat(None, len(data[key])))
|
||||
for t in ranges:
|
||||
start = t[0]
|
||||
c = t[2]
|
||||
if cl_list[start] is None:
|
||||
nc = c
|
||||
else:
|
||||
nc = cl_list[start]
|
||||
for i in range(start, t[1]+1):
|
||||
cl_list[i] = nc
|
||||
if len(chardict) == 1 and ' ' in chardict:
|
||||
# The category could not be partitioned.
|
||||
collapse_model = 'disable'
|
||||
else:
|
||||
ranges = sorted([(v[0], v[1], c) for c,v in chardict.items()])
|
||||
# Create a list of 'first letters' to use for each item in
|
||||
# the category. The list is generated using the ranges. Overlaps
|
||||
# are filled with the character that first occurs.
|
||||
cl_list = list(repeat(None, len(data[key])))
|
||||
for t in ranges:
|
||||
start = t[0]
|
||||
c = t[2]
|
||||
if cl_list[start] is None:
|
||||
nc = c
|
||||
else:
|
||||
nc = cl_list[start]
|
||||
for i in range(start, t[1]+1):
|
||||
cl_list[i] = nc
|
||||
|
||||
for idx,tag in enumerate(data[key]):
|
||||
if clear_rating:
|
||||
@ -448,13 +451,19 @@ class TagsModel(QAbstractItemModel): # {{{
|
||||
else:
|
||||
d['last'] = data[key][cat_len-1]
|
||||
name = eval_formatter.safe_format(collapse_template,
|
||||
d, 'TAG_VIEW', None)
|
||||
sub_cat = self.create_node(parent=category, data = name,
|
||||
d, '##TAG_VIEW##', None)
|
||||
if name.startswith('##TAG_VIEW##'):
|
||||
# Formatter threw an exception. Don't create subnode
|
||||
node_parent = category
|
||||
else:
|
||||
sub_cat = self.create_node(parent=category, data = name,
|
||||
tooltip = None, temporary=True,
|
||||
category_icon = category_node.icon,
|
||||
category_key=category_node.category_key,
|
||||
icon_map=self.icon_state_map)
|
||||
sub_cat.tag.is_searchable = False
|
||||
sub_cat.tag.is_searchable = False
|
||||
sub_cat.is_gst = is_gst
|
||||
node_parent = sub_cat
|
||||
else: # by 'first letter'
|
||||
cl = cl_list[idx]
|
||||
if cl != collapse_letter:
|
||||
@ -465,8 +474,8 @@ class TagsModel(QAbstractItemModel): # {{{
|
||||
tooltip = None, temporary=True,
|
||||
category_key=category_node.category_key,
|
||||
icon_map=self.icon_state_map)
|
||||
sub_cat.is_gst = is_gst
|
||||
node_parent = sub_cat
|
||||
sub_cat.is_gst = is_gst
|
||||
node_parent = sub_cat
|
||||
else:
|
||||
node_parent = category
|
||||
|
||||
@ -1178,13 +1187,16 @@ class TagsModel(QAbstractItemModel): # {{{
|
||||
for subnode in tag_item.children:
|
||||
if subnode.tag.sort:
|
||||
letters_seen[subnode.tag.sort[0]] = True
|
||||
charclass = ''.join(letters_seen)
|
||||
if k == 'author_sort':
|
||||
expr = r'%s:"~(^[%s])|(&\s*[%s])"'%(k, charclass, charclass)
|
||||
elif k == 'series':
|
||||
expr = r'series_sort:"~^[%s]"'%(charclass)
|
||||
if letters_seen:
|
||||
charclass = ''.join(letters_seen)
|
||||
if k == 'author_sort':
|
||||
expr = r'%s:"~(^[%s])|(&\s*[%s])"'%(k, charclass, charclass)
|
||||
elif k == 'series':
|
||||
expr = r'series_sort:"~^[%s]"'%(charclass)
|
||||
else:
|
||||
expr = r'%s:"~^[%s]"'%(k, charclass)
|
||||
else:
|
||||
expr = r'%s:"~^[%s]"'%(k, charclass)
|
||||
expr = r'%s:false'%(k)
|
||||
if node_searches[tag_item.tag.state] == 'true':
|
||||
ans.append(expr)
|
||||
else:
|
||||
|
@ -117,6 +117,7 @@ class TagsView(QTreeView): # {{{
|
||||
QTreeView::item:hover {
|
||||
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #e7effd, stop: 1 #cbdaf1);
|
||||
border: 1px solid #bfcde4;
|
||||
border-radius: 6px;
|
||||
}
|
||||
''')
|
||||
|
||||
|
@ -649,7 +649,7 @@ class DocumentView(QWebView): # {{{
|
||||
def current_page_image(self, overlap=-1):
|
||||
if overlap < 0:
|
||||
overlap = self.height()
|
||||
img = QImage(self.width(), overlap, QImage.Format_ARGB32)
|
||||
img = QImage(self.width(), overlap, QImage.Format_ARGB32_Premultiplied)
|
||||
painter = QPainter(img)
|
||||
self.document.mainFrame().render(painter, QRegion(0, 0, self.width(), overlap))
|
||||
painter.end()
|
||||
|
@ -28,6 +28,7 @@ from calibre.customize.ui import available_input_formats
|
||||
from calibre.gui2.viewer.dictionary import Lookup
|
||||
from calibre import as_unicode, force_unicode, isbytestring
|
||||
from calibre.ptempfile import reset_base_dir
|
||||
from calibre.utils.zipfile import BadZipfile
|
||||
|
||||
vprefs = JSONConfig('viewer')
|
||||
|
||||
@ -37,6 +38,11 @@ class Worker(Thread):
|
||||
try:
|
||||
Thread.run(self)
|
||||
self.exception = self.traceback = None
|
||||
except BadZipfile:
|
||||
self.exception = _(
|
||||
'This ebook is corrupted and cannot be opened. If you '
|
||||
'downloaded it from somewhere, try downloading it again.')
|
||||
self.traceback = ''
|
||||
except Exception as err:
|
||||
self.exception = err
|
||||
self.traceback = traceback.format_exc()
|
||||
|
@ -32,6 +32,7 @@ class TOCView(QTreeView):
|
||||
QTreeView::item:hover {
|
||||
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #e7effd, stop: 1 #cbdaf1);
|
||||
border: 1px solid #bfcde4;
|
||||
border-radius: 6px;
|
||||
}
|
||||
QHeaderView::section {
|
||||
background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1,
|
||||
|
@ -418,9 +418,17 @@ class KindlePage(QWizardPage, KindleUI):
|
||||
|
||||
def initializePage(self):
|
||||
opts = smtp_prefs().parse()
|
||||
for x in opts.accounts.keys():
|
||||
accs = []
|
||||
has_default = False
|
||||
for x, ac in opts.accounts.iteritems():
|
||||
default = ac[2]
|
||||
if x.strip().endswith('@kindle.com'):
|
||||
self.to_address.setText(x)
|
||||
accs.append((x, default))
|
||||
if default: has_default = True
|
||||
if has_default:
|
||||
accs = [x for x in accs if x[1]]
|
||||
if accs:
|
||||
self.to_address.setText(accs[0])
|
||||
def x():
|
||||
t = unicode(self.to_address.text())
|
||||
if t.strip():
|
||||
|
@ -40,7 +40,7 @@ def write_dirtied(db):
|
||||
|
||||
def get_parser(usage):
|
||||
parser = OptionParser(usage)
|
||||
go = parser.add_option_group('GLOBAL OPTIONS')
|
||||
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.'))
|
||||
|
||||
return parser
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user