mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
KG updates Qt 4.7
This commit is contained in:
commit
179f8853dc
BIN
resources/images/hotmail.png
Normal file
BIN
resources/images/hotmail.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.6 KiB |
BIN
resources/images/news/moscow_times.png
Normal file
BIN
resources/images/news/moscow_times.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.0 KiB |
@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
|
|
||||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
__copyright__ = '2010, Szing'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
'''
|
'''
|
||||||
@ -10,49 +10,52 @@ globeandmail.com
|
|||||||
|
|
||||||
from calibre.web.feeds.news import BasicNewsRecipe
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
class GlobeAndMail(BasicNewsRecipe):
|
class AdvancedUserRecipe1287083651(BasicNewsRecipe):
|
||||||
title = u'Globe and Mail'
|
title = u'Globe & Mail'
|
||||||
language = 'en_CA'
|
__license__ = 'GPL v3'
|
||||||
|
__author__ = 'Szing'
|
||||||
__author__ = 'Kovid Goyal'
|
|
||||||
oldest_article = 2
|
oldest_article = 2
|
||||||
max_articles_per_feed = 10
|
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
extra_css = '''
|
max_articles_per_feed = 100
|
||||||
h3 {font-size: 22pt; font-weight:bold; margin:0px; padding:0px 0px 8pt 0px;}
|
encoding = 'utf8'
|
||||||
h4 {margin-top: 0px;}
|
publisher = 'Globe & Mail'
|
||||||
#byline { font-family: monospace; font-weight:bold; }
|
language = 'en_CA'
|
||||||
#placeline {font-weight:bold;}
|
extra_css = 'p.meta {font-size:75%}\n .redtext {color: red;}\n .byline {font-size: 70%}'
|
||||||
#credit {margin-top:0px;}
|
|
||||||
.tag {font-size: 22pt;}'''
|
|
||||||
description = 'Canada\'s national newspaper'
|
|
||||||
keep_only_tags = [dict(name='article')]
|
|
||||||
remove_tags = [dict(name='aside'),
|
|
||||||
dict(name='footer'),
|
|
||||||
dict(name='div', attrs={'class':(lambda x: isinstance(x, (str,unicode)) and 'articlecommentcountholder' in x.split(' '))}),
|
|
||||||
dict(name='ul', attrs={'class':(lambda x: isinstance(x, (str,unicode)) and 'articletoolbar' in x.split(' '))}),
|
|
||||||
]
|
|
||||||
feeds = [
|
feeds = [
|
||||||
(u'Latest headlines', u'http://www.theglobeandmail.com/?service=rss'),
|
(u'Top National Stories', u'http://www.theglobeandmail.com/news/national/?service=rss'),
|
||||||
(u'Top stories', u'http://www.theglobeandmail.com/?service=rss&feed=topstories'),
|
|
||||||
(u'National', u'http://www.theglobeandmail.com/news/national/?service=rss'),
|
|
||||||
(u'Politics', u'http://www.theglobeandmail.com/news/politics/?service=rss'),
|
|
||||||
(u'World', u'http://www.theglobeandmail.com/news/world/?service=rss'),
|
|
||||||
(u'Business', u'http://www.theglobeandmail.com/report-on-business/?service=rss'),
|
(u'Business', u'http://www.theglobeandmail.com/report-on-business/?service=rss'),
|
||||||
(u'Opinions', u'http://www.theglobeandmail.com/news/opinions/?service=rss'),
|
(u'Commentary', u'http://www.theglobeandmail.com/report-on-business/commentary/?service=rss'),
|
||||||
(u'Columnists', u'http://www.theglobeandmail.com/news/opinions/columnists/?service=rss'),
|
(u'Blogs', u'http://www.theglobeandmail.com/blogs/?service=rss'),
|
||||||
(u'Globe Investor', u'http://www.theglobeandmail.com/globe-investor/?service=rss'),
|
(u'Facts & Arguments', u'http://www.theglobeandmail.com/life/facts-and-arguments/?service=rss'),
|
||||||
(u'Sports', u'http://www.theglobeandmail.com/sports/?service=rss'),
|
|
||||||
(u'Technology', u'http://www.theglobeandmail.com/news/technology/?service=rss'),
|
(u'Technology', u'http://www.theglobeandmail.com/news/technology/?service=rss'),
|
||||||
|
(u'Investing', u'http://www.theglobeandmail.com/globe-investor/?service=rss'),
|
||||||
|
(u'Top Polical Stories', u'http://www.theglobeandmail.com/news/politics/?service=rss'),
|
||||||
(u'Arts', u'http://www.theglobeandmail.com/news/arts/?service=rss'),
|
(u'Arts', u'http://www.theglobeandmail.com/news/arts/?service=rss'),
|
||||||
(u'Life', u'http://www.theglobeandmail.com/life/?service=rss'),
|
(u'Life', u'http://www.theglobeandmail.com/life/?service=rss'),
|
||||||
(u'Blogs', u'http://www.theglobeandmail.com/blogs/?service=rss'),
|
|
||||||
(u'Real Estate', u'http://www.theglobeandmail.com/real-estate/?service=rss'),
|
(u'Real Estate', u'http://www.theglobeandmail.com/real-estate/?service=rss'),
|
||||||
(u'Auto', u'http://www.theglobeandmail.com/auto/?service=rss')
|
(u'Auto', u'http://www.theglobeandmail.com/sports/?service=rss'),
|
||||||
|
(u'Sports', u'http://www.theglobeandmail.com/auto/?service=rss')
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_article_url(self, article):
|
keep_only_tags = [
|
||||||
url = BasicNewsRecipe.get_article_url(self, article)
|
dict(name='h1'),
|
||||||
if '/video/' not in url:
|
dict(name='h2', attrs={'id':'articletitle'}),
|
||||||
return url
|
dict(name='p', attrs={'class':['leadText', 'meta', 'leadImage', 'redtext byline', 'bodyText']}),
|
||||||
|
dict(name='div', attrs={'class':['news','articlemeta','articlecopy']}),
|
||||||
|
dict(name='id', attrs={'class':'article'}),
|
||||||
|
dict(name='table', attrs={'class':'todays-market'}),
|
||||||
|
dict(name='header', attrs={'id':'leadheader'})
|
||||||
|
]
|
||||||
|
|
||||||
|
remove_tags = [
|
||||||
|
dict(name='div', attrs={'id':['tabInside', 'ShareArticles', 'topStories']})
|
||||||
|
]
|
||||||
|
|
||||||
|
#this has to be here or the text in the article appears twice.
|
||||||
|
remove_tags_after = [dict(id='article')]
|
||||||
|
|
||||||
|
#Use the mobile version rather than the web version
|
||||||
|
def print_version(self, url):
|
||||||
|
return url + '&service=mobile'
|
||||||
|
|
||||||
|
@ -1,30 +1,32 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2008, Darko Miletic <darko.miletic at gmail.com>'
|
__copyright__ = '2008-2010, Darko Miletic <darko.miletic at gmail.com>'
|
||||||
'''
|
'''
|
||||||
moscowtimes.ru
|
www.themoscowtimes.com
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from calibre.web.feeds.news import BasicNewsRecipe
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
class Moscowtimes(BasicNewsRecipe):
|
class Moscowtimes(BasicNewsRecipe):
|
||||||
title = u'The Moscow Times'
|
title = 'The Moscow Times'
|
||||||
__author__ = 'Darko Miletic and Sujata Raman'
|
__author__ = 'Darko Miletic and Sujata Raman'
|
||||||
description = 'News from Russia'
|
description = 'The Moscow Times is a daily English-language newspaper featuring objective, reliable news on business, politics, sports and culture in Moscow, in Russia and the former Soviet Union (CIS).'
|
||||||
|
category = 'Russia, Moscow, Russian news, Moscow news, Russian newspaper, daily news, independent news, reliable news, USSR, Soviet Union, CIS, Russian politics, Russian business, Russian culture, Russian opinion, St Petersburg, Saint Petersburg'
|
||||||
|
publisher = 'The Moscow Times'
|
||||||
language = 'en'
|
language = 'en'
|
||||||
lang = 'en'
|
oldest_article = 2
|
||||||
oldest_article = 7
|
|
||||||
max_articles_per_feed = 100
|
max_articles_per_feed = 100
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
use_embedded_content = False
|
use_embedded_content = False
|
||||||
#encoding = 'utf-8'
|
remove_empty_feeds = True
|
||||||
encoding = 'cp1252'
|
encoding = 'cp1251'
|
||||||
remove_javascript = True
|
masthead_url = 'http://www.themoscowtimes.com/bitrix/templates/tmt/img/logo.gif'
|
||||||
|
publication_type = 'newspaper'
|
||||||
|
|
||||||
conversion_options = {
|
conversion_options = {
|
||||||
'comment' : description
|
'comment' : description
|
||||||
, 'language' : lang
|
, 'tags' : category
|
||||||
|
, 'publisher' : publisher
|
||||||
|
, 'language' : language
|
||||||
}
|
}
|
||||||
|
|
||||||
extra_css = '''
|
extra_css = '''
|
||||||
@ -35,39 +37,37 @@ class Moscowtimes(BasicNewsRecipe):
|
|||||||
.text{font-family:Arial,Tahoma,Verdana,Helvetica,sans-serif ; font-size:75%; }
|
.text{font-family:Arial,Tahoma,Verdana,Helvetica,sans-serif ; font-size:75%; }
|
||||||
'''
|
'''
|
||||||
feeds = [
|
feeds = [
|
||||||
(u'The Moscow Times Top Stories' , u'http://www.themoscowtimes.com/rss/top'),
|
(u'Top Stories' , u'http://www.themoscowtimes.com/rss/top' )
|
||||||
(u'The Moscow Times Current Issue' , u'http://www.themoscowtimes.com/rss/issue'),
|
,(u'Current Issue' , u'http://www.themoscowtimes.com/rss/issue' )
|
||||||
(u'The Moscow Times News' , u'http://www.themoscowtimes.com/rss/news'),
|
,(u'News' , u'http://www.themoscowtimes.com/rss/news' )
|
||||||
(u'The Moscow Times Business' , u'http://www.themoscowtimes.com/rss/business'),
|
,(u'Business' , u'http://www.themoscowtimes.com/rss/business')
|
||||||
(u'The Moscow Times Art and Ideas' , u'http://www.themoscowtimes.com/rss/art'),
|
,(u'Art and Ideas' , u'http://www.themoscowtimes.com/rss/art' )
|
||||||
(u'The Moscow Times Opinion' , u'http://www.themoscowtimes.com/rss/opinion')
|
,(u'Opinion' , u'http://www.themoscowtimes.com/rss/opinion' )
|
||||||
]
|
|
||||||
|
|
||||||
keep_only_tags = [
|
|
||||||
dict(name='div', attrs={'class':['newstextblock']})
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
keep_only_tags = [dict(name='div', attrs={'id':'content'})]
|
||||||
remove_tags = [
|
remove_tags = [
|
||||||
dict(name='div', attrs={'class':['photo_nav']})
|
dict(name='div', attrs={'class':['photo_nav','phototext']})
|
||||||
|
,dict(name=['iframe','meta','base','link','embed','object'])
|
||||||
]
|
]
|
||||||
|
|
||||||
def preprocess_html(self, soup):
|
def preprocess_html(self, soup):
|
||||||
soup.html['xml:lang'] = self.lang
|
for lnk in soup.findAll('a'):
|
||||||
soup.html['lang'] = self.lang
|
if lnk.string is not None:
|
||||||
mtag = '<meta http-equiv="Content-Type" content="text/html; charset=' + self.encoding + '">'
|
ind = self.tag_to_string(lnk)
|
||||||
soup.head.insert(0,mtag)
|
lnk.replaceWith(ind)
|
||||||
|
return soup
|
||||||
return self.adeify_images(soup)
|
|
||||||
|
|
||||||
|
def print_version(self, url):
|
||||||
|
return url.replace('.themoscowtimes.com/','.themoscowtimes.com/print/')
|
||||||
|
|
||||||
def get_cover_url(self):
|
def get_cover_url(self):
|
||||||
|
cover_url = None
|
||||||
href = 'http://www.themoscowtimes.com/pdf/'
|
href = 'http://www.themoscowtimes.com/pdf/'
|
||||||
|
|
||||||
soup = self.index_to_soup(href)
|
soup = self.index_to_soup(href)
|
||||||
div = soup.find('div',attrs={'class':'left'})
|
div = soup.find('div',attrs={'class':'left'})
|
||||||
|
if div:
|
||||||
a = div.find('a')
|
a = div.find('a')
|
||||||
print a
|
|
||||||
if a :
|
if a :
|
||||||
cover_url = a.img['src']
|
cover_url = 'http://www.themoscowtimes.com' + a.img['src']
|
||||||
return cover_url
|
return cover_url
|
||||||
|
35
resources/recipes/now_toronto.recipe
Normal file
35
resources/recipes/now_toronto.recipe
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#Based on Lars Jacob's Taz Digiabo recipe
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2010, Starson17'
|
||||||
|
|
||||||
|
import os, urllib2, zipfile
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
from calibre.ptempfile import PersistentTemporaryFile
|
||||||
|
|
||||||
|
class NowToronto(BasicNewsRecipe):
|
||||||
|
title = u'Now Toronto'
|
||||||
|
description = u'Now Toronto'
|
||||||
|
__author__ = 'Starson17'
|
||||||
|
conversion_options = {
|
||||||
|
'no_default_epub_cover' : True
|
||||||
|
}
|
||||||
|
|
||||||
|
def build_index(self):
|
||||||
|
epub_feed = "http://feeds.feedburner.com/NowEpubEditions"
|
||||||
|
soup = self.index_to_soup(epub_feed)
|
||||||
|
url = soup.find(name = 'feedburner:origlink').string
|
||||||
|
f = urllib2.urlopen(url)
|
||||||
|
tmp = PersistentTemporaryFile(suffix='.epub')
|
||||||
|
self.report_progress(0,_('downloading epub'))
|
||||||
|
tmp.write(f.read())
|
||||||
|
tmp.close()
|
||||||
|
zfile = zipfile.ZipFile(tmp.name, 'r')
|
||||||
|
self.report_progress(0,_('extracting epub'))
|
||||||
|
zfile.extractall(self.output_dir)
|
||||||
|
tmp.close()
|
||||||
|
index = os.path.join(self.output_dir, 'content.opf')
|
||||||
|
self.report_progress(1,_('epub downloaded and extracted'))
|
||||||
|
return index
|
70
resources/recipes/pc_lab.recipe
Normal file
70
resources/recipes/pc_lab.recipe
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||||
|
|
||||||
|
class PCLab(BasicNewsRecipe):
|
||||||
|
cover_url = 'http://pclab.pl/img/logo.png'
|
||||||
|
title = u"PC Lab"
|
||||||
|
__author__ = 'ravcio - rlelusz[at]gmail.com'
|
||||||
|
description = u"Articles from PC Lab website"
|
||||||
|
language = 'pl'
|
||||||
|
oldest_article = 30.0
|
||||||
|
max_articles_per_feed = 100
|
||||||
|
recursions = 0
|
||||||
|
encoding = 'iso-8859-2'
|
||||||
|
no_stylesheets = True
|
||||||
|
remove_javascript = True
|
||||||
|
use_embedded_content = False
|
||||||
|
|
||||||
|
keep_only_tags = [
|
||||||
|
dict(name='div', attrs={'class':['substance']})
|
||||||
|
]
|
||||||
|
|
||||||
|
remove_tags = [
|
||||||
|
dict(name='div', attrs={'class':['chapters']})
|
||||||
|
,dict(name='div', attrs={'id':['script_bxad_slot_display_list_bxad_slot']})
|
||||||
|
]
|
||||||
|
|
||||||
|
remove_tags_after = [
|
||||||
|
dict(name='div', attrs={'class':['navigation']})
|
||||||
|
]
|
||||||
|
|
||||||
|
#links to RSS feeds
|
||||||
|
feeds = [ ('PCLab', u'http://pclab.pl/xml/artykuly.xml') ]
|
||||||
|
|
||||||
|
#load second and subsequent page content
|
||||||
|
# in: soup - full page with 'next' button
|
||||||
|
# out: appendtag - tag to which new page is to be added
|
||||||
|
def append_page(self, soup, appendtag):
|
||||||
|
# find the 'Next' button
|
||||||
|
pager = soup.find('div', attrs={'class':'next'})
|
||||||
|
|
||||||
|
if pager:
|
||||||
|
#search for 'a' element with link to next page (exit if not found)
|
||||||
|
a = pager.find('a')
|
||||||
|
if a:
|
||||||
|
nexturl = a['href']
|
||||||
|
|
||||||
|
soup2 = self.index_to_soup('http://pclab.pl/' + nexturl)
|
||||||
|
|
||||||
|
pagetext_substance = soup2.find('div', attrs={'class':'substance'})
|
||||||
|
pagetext = pagetext_substance.find('div', attrs={'class':'data'})
|
||||||
|
pagetext.extract()
|
||||||
|
|
||||||
|
pos = len(appendtag.contents)
|
||||||
|
appendtag.insert(pos, pagetext)
|
||||||
|
pos = len(appendtag.contents)
|
||||||
|
|
||||||
|
self.append_page(soup2, appendtag)
|
||||||
|
|
||||||
|
|
||||||
|
def preprocess_html(self, soup):
|
||||||
|
|
||||||
|
# soup.body contains no title and no navigator, they are in soup
|
||||||
|
self.append_page(soup, soup.body)
|
||||||
|
|
||||||
|
# finally remove some tags
|
||||||
|
tags = soup.findAll('div',attrs={'class':['tags', 'index', 'script_bxad_slot_display_list_bxad_slot', 'index first', 'zumi', 'navigation']})
|
||||||
|
[tag.extract() for tag in tags]
|
||||||
|
|
||||||
|
return soup
|
@ -7,7 +7,7 @@ class AdvancedUserRecipe1284927619(BasicNewsRecipe):
|
|||||||
__author__ = 'noxxx'
|
__author__ = 'noxxx'
|
||||||
max_articles_per_feed = 100
|
max_articles_per_feed = 100
|
||||||
description = 'tagesanzeiger.ch: Nichts verpassen'
|
description = 'tagesanzeiger.ch: Nichts verpassen'
|
||||||
category = 'News, Politik, Nachrichten, Schweiz, Zürich'
|
category = 'News, Politik, Nachrichten, Schweiz, Zuerich'
|
||||||
language = 'de'
|
language = 'de'
|
||||||
|
|
||||||
conversion_options = {
|
conversion_options = {
|
||||||
|
@ -13,9 +13,9 @@ from PyQt4 import pyqtconfig
|
|||||||
|
|
||||||
from setup import isosx, iswindows, islinux
|
from setup import isosx, iswindows, islinux
|
||||||
|
|
||||||
OSX_SDK = '/Developer/SDKs/MacOSX10.4u.sdk'
|
OSX_SDK = '/Developer/SDKs/MacOSX10.5.sdk'
|
||||||
|
|
||||||
os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.4'
|
os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.5'
|
||||||
|
|
||||||
NMAKE = RC = msvc = MT = win_inc = win_lib = win_ddk = None
|
NMAKE = RC = msvc = MT = win_inc = win_lib = win_ddk = None
|
||||||
if iswindows:
|
if iswindows:
|
||||||
@ -124,7 +124,7 @@ elif isosx:
|
|||||||
fc_inc = '/sw/include/fontconfig'
|
fc_inc = '/sw/include/fontconfig'
|
||||||
fc_lib = '/sw/lib'
|
fc_lib = '/sw/lib'
|
||||||
poppler_inc_dirs = consolidate('POPPLER_INC_DIR',
|
poppler_inc_dirs = consolidate('POPPLER_INC_DIR',
|
||||||
'/sw/build/poppler-0.12.2/poppler:/sw/build/poppler-0.12.2')
|
'/sw/build/poppler-0.14.5/poppler:/sw/build/poppler-0.14.5')
|
||||||
popplerqt4_inc_dirs = poppler_inc_dirs + [poppler_inc_dirs[0]+'/qt4']
|
popplerqt4_inc_dirs = poppler_inc_dirs + [poppler_inc_dirs[0]+'/qt4']
|
||||||
poppler_lib_dirs = consolidate('POPPLER_LIB_DIR',
|
poppler_lib_dirs = consolidate('POPPLER_LIB_DIR',
|
||||||
'/sw/lib')
|
'/sw/lib')
|
||||||
|
@ -19,7 +19,7 @@ __all__ = [
|
|||||||
'upload_user_manual', 'upload_to_mobileread', 'upload_demo',
|
'upload_user_manual', 'upload_to_mobileread', 'upload_demo',
|
||||||
'upload_to_sourceforge', 'upload_to_google_code',
|
'upload_to_sourceforge', 'upload_to_google_code',
|
||||||
'linux32', 'linux64', 'linux', 'linux_freeze', 'linux_freeze2',
|
'linux32', 'linux64', 'linux', 'linux_freeze', 'linux_freeze2',
|
||||||
'osx32_freeze', 'osx32', 'osx', 'rsync', 'push',
|
'osx32_freeze', 'osx', 'rsync', 'push',
|
||||||
'win32_freeze', 'win32', 'win',
|
'win32_freeze', 'win32', 'win',
|
||||||
'stage1', 'stage2', 'stage3', 'stage4', 'publish'
|
'stage1', 'stage2', 'stage3', 'stage4', 'publish'
|
||||||
]
|
]
|
||||||
@ -84,9 +84,8 @@ linux_freeze = LinuxFreeze()
|
|||||||
from setup.installer.linux.freeze2 import LinuxFreeze2
|
from setup.installer.linux.freeze2 import LinuxFreeze2
|
||||||
linux_freeze2 = LinuxFreeze2()
|
linux_freeze2 = LinuxFreeze2()
|
||||||
|
|
||||||
from setup.installer.osx import OSX, OSX32
|
from setup.installer.osx import OSX
|
||||||
osx = OSX()
|
osx = OSX()
|
||||||
osx32 = OSX32()
|
|
||||||
from setup.installer.osx.app.main import OSX32_Freeze
|
from setup.installer.osx.app.main import OSX32_Freeze
|
||||||
osx32_freeze = OSX32_Freeze()
|
osx32_freeze = OSX32_Freeze()
|
||||||
|
|
||||||
|
@ -186,7 +186,7 @@ if isfreebsd:
|
|||||||
|
|
||||||
|
|
||||||
if isosx:
|
if isosx:
|
||||||
x, p = ('i386', 'ppc')
|
x, p = ('i386', 'x86_64')
|
||||||
archs = ['-arch', x, '-arch', p, '-isysroot',
|
archs = ['-arch', x, '-arch', p, '-isysroot',
|
||||||
OSX_SDK]
|
OSX_SDK]
|
||||||
cflags.append('-D_OSX')
|
cflags.append('-D_OSX')
|
||||||
@ -339,7 +339,7 @@ class Build(Command):
|
|||||||
obj_pat = 'release\\*.obj' if iswindows else '*.o'
|
obj_pat = 'release\\*.obj' if iswindows else '*.o'
|
||||||
objects = glob.glob(obj_pat)
|
objects = glob.glob(obj_pat)
|
||||||
if not objects or self.newer(objects, ext.sources+ext.headers):
|
if not objects or self.newer(objects, ext.sources+ext.headers):
|
||||||
archs = 'x86 ppc'
|
archs = 'x86 x86_64'
|
||||||
pro = textwrap.dedent('''\
|
pro = textwrap.dedent('''\
|
||||||
TARGET = %s
|
TARGET = %s
|
||||||
TEMPLATE = lib
|
TEMPLATE = lib
|
||||||
|
@ -7,25 +7,14 @@ __copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
|||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
|
||||||
from setup import Command
|
|
||||||
from setup.installer import VMInstaller
|
from setup.installer import VMInstaller
|
||||||
|
|
||||||
class OSX(Command):
|
class OSX(VMInstaller):
|
||||||
|
|
||||||
description = 'Build OS X binary installers'
|
description = 'Build OS X binary installer'
|
||||||
|
|
||||||
sub_commands = ['osx32']
|
|
||||||
|
|
||||||
def run(self, opts):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class OSX32(VMInstaller):
|
|
||||||
|
|
||||||
description = 'Build 32 bit OS X binary installer'
|
|
||||||
|
|
||||||
INSTALLER_EXT = 'dmg'
|
INSTALLER_EXT = 'dmg'
|
||||||
VM_NAME = 'leopard_build'
|
VM_NAME = 'osx_build'
|
||||||
VM = '/vmware/bin/%s'%VM_NAME
|
VM = '/vmware/bin/%s'%VM_NAME
|
||||||
FREEZE_TEMPLATE = 'python -OO setup.py {freeze_command}'
|
FREEZE_TEMPLATE = 'python -OO setup.py {freeze_command}'
|
||||||
FREEZE_COMMAND = 'osx32_freeze'
|
FREEZE_COMMAND = 'osx32_freeze'
|
||||||
|
@ -48,14 +48,14 @@ def compile_launcher_lib(contents_dir, gcc, base):
|
|||||||
fd = join(contents_dir, 'Frameworks')
|
fd = join(contents_dir, 'Frameworks')
|
||||||
dest = join(fd, 'calibre-launcher.dylib')
|
dest = join(fd, 'calibre-launcher.dylib')
|
||||||
src = join(base, 'util.c')
|
src = join(base, 'util.c')
|
||||||
cmd = [gcc] + '-Wall -arch i386 -arch ppc -dynamiclib -std=gnu99'.split() + [src] + \
|
cmd = [gcc] + '-Wall -arch i386 -arch x86_64 -dynamiclib -std=gnu99'.split() + [src] + \
|
||||||
['-I'+base] + \
|
['-I'+base] + \
|
||||||
['-I/Library/Frameworks/Python.framework/Versions/Current/Headers'] + \
|
['-I/sw/python/Python.framework/Versions/Current/Headers'] + \
|
||||||
'-current_version 1.0 -compatibility_version 1.0'.split() + \
|
'-current_version 1.0 -compatibility_version 1.0'.split() + \
|
||||||
'-fvisibility=hidden -o'.split() + [dest] + \
|
'-fvisibility=hidden -o'.split() + [dest] + \
|
||||||
['-install_name',
|
['-install_name',
|
||||||
'@executable_path/../Frameworks/'+os.path.basename(dest)] + \
|
'@executable_path/../Frameworks/'+os.path.basename(dest)] + \
|
||||||
['-framework', 'Python', '-framework', 'CoreFoundation', '-headerpad_max_install_names']
|
['-F/sw/python', '-framework', 'Python', '-framework', 'CoreFoundation', '-headerpad_max_install_names']
|
||||||
info('\t'+' '.join(cmd))
|
info('\t'+' '.join(cmd))
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
subprocess.check_call(cmd)
|
subprocess.check_call(cmd)
|
||||||
@ -88,7 +88,7 @@ def compile_launchers(contents_dir, xprograms, pyver):
|
|||||||
fsrc = '/tmp/%s.c'%program
|
fsrc = '/tmp/%s.c'%program
|
||||||
with open(fsrc, 'wb') as f:
|
with open(fsrc, 'wb') as f:
|
||||||
f.write(psrc)
|
f.write(psrc)
|
||||||
cmd = [gcc, '-Wall', '-arch', 'ppc', '-arch', 'i386',
|
cmd = [gcc, '-Wall', '-arch', 'x86_64', '-arch', 'i386',
|
||||||
'-I'+base, fsrc, lib, '-o', out,
|
'-I'+base, fsrc, lib, '-o', out,
|
||||||
'-headerpad_max_install_names']
|
'-headerpad_max_install_names']
|
||||||
info('\t'+' '.join(cmd))
|
info('\t'+' '.join(cmd))
|
||||||
@ -108,14 +108,6 @@ def flipwritable(fn, mode=None):
|
|||||||
os.chmod(fn, stat.S_IWRITE | old_mode)
|
os.chmod(fn, stat.S_IWRITE | old_mode)
|
||||||
return old_mode
|
return old_mode
|
||||||
|
|
||||||
def thin(path):
|
|
||||||
try:
|
|
||||||
subprocess.check_call(['lipo', path, '-verify_arch', 'ppc64'])
|
|
||||||
info('\tThinning', path)
|
|
||||||
except:
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
subprocess.check_call(['lipo', path, '-thin', 'x86_64', '-output', path])
|
|
||||||
|
|
||||||
STRIPCMD = ['/usr/bin/strip', '-x', '-S', '-']
|
STRIPCMD = ['/usr/bin/strip', '-x', '-S', '-']
|
||||||
def strip_files(files, argv_max=(256 * 1024)):
|
def strip_files(files, argv_max=(256 * 1024)):
|
||||||
@ -200,7 +192,6 @@ class Py2App(object):
|
|||||||
self.copy_site()
|
self.copy_site()
|
||||||
self.create_exe()
|
self.create_exe()
|
||||||
if not test_launchers:
|
if not test_launchers:
|
||||||
#self.thin_to_x86_64()
|
|
||||||
self.strip_files()
|
self.strip_files()
|
||||||
|
|
||||||
ret = self.makedmg(self.build_dir, APPNAME+'-'+VERSION)
|
ret = self.makedmg(self.build_dir, APPNAME+'-'+VERSION)
|
||||||
@ -212,19 +203,6 @@ class Py2App(object):
|
|||||||
shutil.copytree('resources', os.path.join(self.resources_dir,
|
shutil.copytree('resources', os.path.join(self.resources_dir,
|
||||||
'resources'))
|
'resources'))
|
||||||
|
|
||||||
@flush
|
|
||||||
def thin_to_x86_64(self):
|
|
||||||
info('\nThinning to x86_64')
|
|
||||||
for y in (self.frameworks_dir, join(self.resources_dir, 'Python')):
|
|
||||||
for x in os.walk(y):
|
|
||||||
for f in x[-1]:
|
|
||||||
f = join(x[0], f)
|
|
||||||
if not os.path.isfile(f): continue
|
|
||||||
for t in ('.so', '.dylib', '/Python'):
|
|
||||||
if f.endswith(t):
|
|
||||||
thin(f)
|
|
||||||
break
|
|
||||||
|
|
||||||
@flush
|
@flush
|
||||||
def strip_files(self):
|
def strip_files(self):
|
||||||
info('\nStripping files...')
|
info('\nStripping files...')
|
||||||
@ -270,10 +248,10 @@ class Py2App(object):
|
|||||||
continue
|
continue
|
||||||
for y in (SW+'/lib/', '/usr/local/lib/', SW+'/qt/lib/',
|
for y in (SW+'/lib/', '/usr/local/lib/', SW+'/qt/lib/',
|
||||||
'/opt/local/lib/',
|
'/opt/local/lib/',
|
||||||
'/Library/Frameworks/Python.framework/', SW+'/freetype/lib/'):
|
SW+'/python/Python.framework/', SW+'/freetype/lib/'):
|
||||||
if x.startswith(y):
|
if x.startswith(y):
|
||||||
if y == '/Library/Frameworks/Python.framework/':
|
if y == SW+'/python/Python.framework/':
|
||||||
y = '/Library/Frameworks/'
|
y = SW+'/python/'
|
||||||
yield x, x[len(y):]
|
yield x, x[len(y):]
|
||||||
break
|
break
|
||||||
|
|
||||||
@ -299,7 +277,7 @@ class Py2App(object):
|
|||||||
@flush
|
@flush
|
||||||
def add_python_framework(self):
|
def add_python_framework(self):
|
||||||
info('\nAdding Python framework')
|
info('\nAdding Python framework')
|
||||||
src = join('/Library/Frameworks', 'Python.framework')
|
src = join('/sw/python', 'Python.framework')
|
||||||
x = join(self.frameworks_dir, 'Python.framework')
|
x = join(self.frameworks_dir, 'Python.framework')
|
||||||
curr = os.path.realpath(join(src, 'Versions', 'Current'))
|
curr = os.path.realpath(join(src, 'Versions', 'Current'))
|
||||||
currd = join(x, 'Versions', basename(curr))
|
currd = join(x, 'Versions', basename(curr))
|
||||||
@ -314,7 +292,7 @@ class Py2App(object):
|
|||||||
def add_qt_frameworks(self):
|
def add_qt_frameworks(self):
|
||||||
info('\nAdding Qt Framework')
|
info('\nAdding Qt Framework')
|
||||||
for f in ('QtCore', 'QtGui', 'QtXml', 'QtNetwork', 'QtSvg', 'QtWebKit',
|
for f in ('QtCore', 'QtGui', 'QtXml', 'QtNetwork', 'QtSvg', 'QtWebKit',
|
||||||
'QtXmlPatterns', 'phonon'):
|
'QtXmlPatterns'):
|
||||||
self.add_qt_framework(f)
|
self.add_qt_framework(f)
|
||||||
for d in glob.glob(join(SW, 'qt', 'plugins', '*')):
|
for d in glob.glob(join(SW, 'qt', 'plugins', '*')):
|
||||||
shutil.copytree(d, join(self.contents_dir, 'MacOS', basename(d)))
|
shutil.copytree(d, join(self.contents_dir, 'MacOS', basename(d)))
|
||||||
@ -353,8 +331,8 @@ class Py2App(object):
|
|||||||
shutil.copy2(f, dest)
|
shutil.copy2(f, dest)
|
||||||
self.fix_dependencies_in_lib(join(dest, basename(f)))
|
self.fix_dependencies_in_lib(join(dest, basename(f)))
|
||||||
if 'podofo' in f:
|
if 'podofo' in f:
|
||||||
self.change_dep('libpodofo.0.6.99.dylib',
|
self.change_dep('libpodofo.0.8.4.dylib',
|
||||||
self.FID+'/'+'libpodofo.0.6.99.dylib', join(dest, basename(f)))
|
self.FID+'/'+'libpodofo.0.8.4.dylib', join(dest, basename(f)))
|
||||||
|
|
||||||
|
|
||||||
@flush
|
@flush
|
||||||
@ -401,25 +379,27 @@ class Py2App(object):
|
|||||||
@flush
|
@flush
|
||||||
def add_podofo(self):
|
def add_podofo(self):
|
||||||
info('\nAdding PoDoFo')
|
info('\nAdding PoDoFo')
|
||||||
pdf = join(SW, 'lib', 'libpodofo.0.8.2.dylib')
|
pdf = join(SW, 'lib', 'libpodofo.0.8.4.dylib')
|
||||||
self.install_dylib(pdf)
|
self.install_dylib(pdf)
|
||||||
|
|
||||||
@flush
|
@flush
|
||||||
def add_poppler(self):
|
def add_poppler(self):
|
||||||
info('\nAdding poppler')
|
info('\nAdding poppler')
|
||||||
for x in ('libpoppler.5.dylib', 'libpoppler-qt4.3.dylib'):
|
for x in ('libpoppler.7.dylib',):
|
||||||
self.install_dylib(os.path.join(SW, 'lib', x))
|
self.install_dylib(os.path.join(SW, 'lib', x))
|
||||||
self.install_dylib(os.path.join(SW, 'bin', 'pdftohtml'), False)
|
self.install_dylib(os.path.join(SW, 'bin', 'pdftohtml'), False)
|
||||||
|
|
||||||
@flush
|
@flush
|
||||||
def add_libjpeg(self):
|
def add_libjpeg(self):
|
||||||
info('\nAdding libjpeg')
|
info('\nAdding libjpeg')
|
||||||
self.install_dylib(os.path.join(SW, 'lib', 'libjpeg.7.dylib'))
|
self.install_dylib(os.path.join(SW, 'lib', 'libjpeg.8.dylib'))
|
||||||
|
|
||||||
@flush
|
@flush
|
||||||
def add_libpng(self):
|
def add_libpng(self):
|
||||||
info('\nAdding libpng')
|
info('\nAdding libpng')
|
||||||
self.install_dylib(os.path.join(SW, 'lib', 'libpng12.0.dylib'))
|
self.install_dylib(os.path.join(SW, 'lib', 'libpng12.0.dylib'))
|
||||||
|
self.install_dylib(os.path.join(SW, 'lib', 'libpng.3.dylib'))
|
||||||
|
|
||||||
|
|
||||||
@flush
|
@flush
|
||||||
def add_fontconfig(self):
|
def add_fontconfig(self):
|
||||||
@ -449,7 +429,7 @@ class Py2App(object):
|
|||||||
def add_imagemagick(self):
|
def add_imagemagick(self):
|
||||||
info('\nAdding ImageMagick')
|
info('\nAdding ImageMagick')
|
||||||
for x in ('Wand', 'Core'):
|
for x in ('Wand', 'Core'):
|
||||||
self.install_dylib(os.path.join(SW, 'lib', 'libMagick%s.2.dylib'%x))
|
self.install_dylib(os.path.join(SW, 'lib', 'libMagick%s.4.dylib'%x))
|
||||||
idir = glob.glob(os.path.join(SW, 'lib', 'ImageMagick-*'))[-1]
|
idir = glob.glob(os.path.join(SW, 'lib', 'ImageMagick-*'))[-1]
|
||||||
dest = os.path.join(self.frameworks_dir, 'ImageMagick')
|
dest = os.path.join(self.frameworks_dir, 'ImageMagick')
|
||||||
if os.path.exists(dest):
|
if os.path.exists(dest):
|
||||||
@ -463,7 +443,8 @@ class Py2App(object):
|
|||||||
|
|
||||||
@flush
|
@flush
|
||||||
def add_misc_libraries(self):
|
def add_misc_libraries(self):
|
||||||
for x in ('usb', 'unrar', 'readline.6.0', 'wmflite-0.2.7', 'chm.0'):
|
for x in ('usb', 'unrar', 'readline.6.1', 'wmflite-0.2.7', 'chm.0',
|
||||||
|
'sqlite3.0'):
|
||||||
info('\nAdding', x)
|
info('\nAdding', x)
|
||||||
x = 'lib%s.dylib'%x
|
x = 'lib%s.dylib'%x
|
||||||
shutil.copy2(join(SW, 'lib', x), self.frameworks_dir)
|
shutil.copy2(join(SW, 'lib', x), self.frameworks_dir)
|
||||||
@ -551,7 +532,7 @@ class Py2App(object):
|
|||||||
@flush
|
@flush
|
||||||
def add_stdlib(self):
|
def add_stdlib(self):
|
||||||
info('\nAdding python stdlib')
|
info('\nAdding python stdlib')
|
||||||
src = '/Library/Frameworks/Python.framework/Versions/Current/lib/python'
|
src = '/sw/python/Python.framework/Versions/Current/lib/python'
|
||||||
src += self.version_info
|
src += self.version_info
|
||||||
dest = join(self.resources_dir, 'Python', 'lib', 'python')
|
dest = join(self.resources_dir, 'Python', 'lib', 'python')
|
||||||
dest += self.version_info
|
dest += self.version_info
|
||||||
|
@ -28,7 +28,7 @@ If there are no windows binaries already compiled for the version of python you
|
|||||||
|
|
||||||
Run the following command to install python dependencies::
|
Run the following command to install python dependencies::
|
||||||
|
|
||||||
easy_install --always-unzip -U ipython mechanize pyreadline python-dateutil dnspython
|
easy_install --always-unzip -U ipython mechanize pyreadline python-dateutil dnspython cssutils clientform
|
||||||
|
|
||||||
Install BeautifulSoup 3.0.x manually into site-packages (3.1.x parses broken HTML very poorly)
|
Install BeautifulSoup 3.0.x manually into site-packages (3.1.x parses broken HTML very poorly)
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ Qt
|
|||||||
|
|
||||||
Extract Qt sourcecode to C:\Qt\4.x.x. Run configure and make::
|
Extract Qt sourcecode to C:\Qt\4.x.x. Run configure and make::
|
||||||
|
|
||||||
configure -opensource -release -qt-zlib -qt-gif -qt-libmng -qt-libpng -qt-libtiff -qt-libjpeg -release -platform win32-msvc2008 -no-qt3support -webkit -xmlpatterns -no-phonon -no-style-plastique -no-style-cleanlooks -no-style-motif -no-style-cde -no-declarative -no-scripttools -no-audio-backend -no-multimedia -no-dbus -no-openvg -no-opengl -no-qt3support -confirm-license && nmake
|
configure -opensource -release -qt-zlib -qt-gif -qt-libmng -qt-libpng -qt-libtiff -qt-libjpeg -release -platform win32-msvc2008 -no-qt3support -webkit -xmlpatterns -no-phonon -no-style-plastique -no-style-cleanlooks -no-style-motif -no-style-cde -no-declarative -no-scripttools -no-audio-backend -no-multimedia -no-dbus -no-openvg -no-opengl -no-qt3support -confirm-license -nomake examples -nomake demos -nomake docs && nmake
|
||||||
|
|
||||||
SIP
|
SIP
|
||||||
-----
|
-----
|
||||||
|
@ -503,7 +503,11 @@ class KOBO(USBMS):
|
|||||||
ContentType = self.get_content_type_from_extension(extension) if extension != '' else self.get_content_type_from_path(book.path)
|
ContentType = self.get_content_type_from_extension(extension) if extension != '' else self.get_content_type_from_path(book.path)
|
||||||
|
|
||||||
ContentID = self.contentid_from_path(book.path, ContentType)
|
ContentID = self.contentid_from_path(book.path, ContentType)
|
||||||
datelastread = time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime())
|
|
||||||
|
t = (ContentID,)
|
||||||
|
cursor.execute('select DateLastRead from Content where BookID is Null and ContentID = ?', t)
|
||||||
|
result = cursor.fetchone()
|
||||||
|
datelastread = result[0] if result[0] is not None else '1970-01-01T00:00:00'
|
||||||
|
|
||||||
t = (datelastread,ContentID,)
|
t = (datelastread,ContentID,)
|
||||||
|
|
||||||
|
@ -199,6 +199,8 @@ class PRS505(USBMS):
|
|||||||
thumbnail_dir = os.path.join(prefix, *thumbnail_dir.split('/'))
|
thumbnail_dir = os.path.join(prefix, *thumbnail_dir.split('/'))
|
||||||
|
|
||||||
relpath = os.path.relpath(filepath, prefix)
|
relpath = os.path.relpath(filepath, prefix)
|
||||||
|
if relpath.startswith('..\\'):
|
||||||
|
relpath = relpath[3:]
|
||||||
thumbnail_dir = os.path.join(thumbnail_dir, relpath)
|
thumbnail_dir = os.path.join(thumbnail_dir, relpath)
|
||||||
if not os.path.exists(thumbnail_dir):
|
if not os.path.exists(thumbnail_dir):
|
||||||
os.makedirs(thumbnail_dir)
|
os.makedirs(thumbnail_dir)
|
||||||
|
@ -53,8 +53,8 @@
|
|||||||
|
|
||||||
#define NUKE(x) Py_XDECREF(x); x = NULL;
|
#define NUKE(x) Py_XDECREF(x); x = NULL;
|
||||||
|
|
||||||
/* This function only works on 10.5 and later
|
/* This function only works on 10.5 and later. Pass in a unicode object as path */
|
||||||
static PyObject* send2trash(PyObject *self, PyObject *args)
|
static PyObject* usbobserver_send2trash(PyObject *self, PyObject *args)
|
||||||
{
|
{
|
||||||
UInt8 *utf8_chars;
|
UInt8 *utf8_chars;
|
||||||
FSRef fp;
|
FSRef fp;
|
||||||
@ -73,7 +73,7 @@ static PyObject* send2trash(PyObject *self, PyObject *args)
|
|||||||
}
|
}
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
usbobserver_get_iokit_string_property(io_service_t dev, CFStringRef prop) {
|
usbobserver_get_iokit_string_property(io_service_t dev, CFStringRef prop) {
|
||||||
@ -323,6 +323,9 @@ static PyMethodDef usbobserver_methods[] = {
|
|||||||
{"get_mounted_filesystems", usbobserver_get_mounted_filesystems, METH_VARARGS,
|
{"get_mounted_filesystems", usbobserver_get_mounted_filesystems, METH_VARARGS,
|
||||||
"Get mapping of mounted filesystems. Mapping is from BSD name to mount point."
|
"Get mapping of mounted filesystems. Mapping is from BSD name to mount point."
|
||||||
},
|
},
|
||||||
|
{"send2trash", usbobserver_send2trash, METH_VARARGS,
|
||||||
|
"send2trash(unicode object) -> Send specified file/dir to trash"
|
||||||
|
},
|
||||||
|
|
||||||
{NULL, NULL, 0, NULL}
|
{NULL, NULL, 0, NULL}
|
||||||
};
|
};
|
||||||
|
@ -363,11 +363,15 @@ class MobiMLizer(object):
|
|||||||
if value == getattr(self.profile, prop):
|
if value == getattr(self.profile, prop):
|
||||||
result = '100%'
|
result = '100%'
|
||||||
else:
|
else:
|
||||||
|
# Amazon's renderer does not support
|
||||||
|
# img sizes in units other than px
|
||||||
|
# See #7520 for test case
|
||||||
try:
|
try:
|
||||||
ems = int(round(float(value) / self.profile.fbase))
|
pixs = int(round(float(value) / \
|
||||||
|
(72./self.profile.dpi)))
|
||||||
except:
|
except:
|
||||||
continue
|
continue
|
||||||
result = "%dem" % ems
|
result = "%d"%pixs
|
||||||
istate.attrib[prop] = result
|
istate.attrib[prop] = result
|
||||||
elif tag == 'hr' and asfloat(style['width']) > 0:
|
elif tag == 'hr' and asfloat(style['width']) > 0:
|
||||||
prop = style['width'] / self.profile.width
|
prop = style['width'] / self.profile.width
|
||||||
|
@ -620,6 +620,7 @@ static string get_link_dest(LinkAction *link, PDFDoc *doc) {
|
|||||||
case actionSound: break;
|
case actionSound: break;
|
||||||
case actionJavaScript: break;
|
case actionJavaScript: break;
|
||||||
case actionUnknown: break;
|
case actionUnknown: break;
|
||||||
|
default: break;
|
||||||
}
|
}
|
||||||
return oss.str();
|
return oss.str();
|
||||||
}
|
}
|
||||||
|
@ -223,7 +223,6 @@ class MessageBox(QMessageBox):
|
|||||||
if default_button is not None:
|
if default_button is not None:
|
||||||
self.setDefaultButton(default_button)
|
self.setDefaultButton(default_button)
|
||||||
|
|
||||||
|
|
||||||
def copy_to_clipboard(self):
|
def copy_to_clipboard(self):
|
||||||
QApplication.clipboard().setText('%s: %s\n\n%s' %
|
QApplication.clipboard().setText('%s: %s\n\n%s' %
|
||||||
(self.title, self.msg, self.det_msg))
|
(self.title, self.msg, self.det_msg))
|
||||||
|
@ -212,9 +212,9 @@ class BookInfo(QWebView):
|
|||||||
def _show_data(self, rows, comments):
|
def _show_data(self, rows, comments):
|
||||||
f = QFontInfo(QApplication.font(self.parent())).pixelSize()
|
f = QFontInfo(QApplication.font(self.parent())).pixelSize()
|
||||||
p = unicode(QApplication.palette().color(QPalette.Normal,
|
p = unicode(QApplication.palette().color(QPalette.Normal,
|
||||||
QPalette.Base).name())
|
QPalette.Window).name())
|
||||||
c = unicode(QApplication.palette().color(QPalette.Normal,
|
c = unicode(QApplication.palette().color(QPalette.Normal,
|
||||||
QPalette.Text).name())
|
QPalette.WindowText).name())
|
||||||
templ = u'''\
|
templ = u'''\
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
|
@ -14,7 +14,7 @@ from PyQt4.QtGui import QDialog, QItemSelectionModel
|
|||||||
from calibre.gui2.dialogs.fetch_metadata_ui import Ui_FetchMetadata
|
from calibre.gui2.dialogs.fetch_metadata_ui import Ui_FetchMetadata
|
||||||
from calibre.gui2 import error_dialog, NONE, info_dialog, config
|
from calibre.gui2 import error_dialog, NONE, info_dialog, config
|
||||||
from calibre.gui2.widgets import ProgressIndicator
|
from calibre.gui2.widgets import ProgressIndicator
|
||||||
from calibre import strftime
|
from calibre import strftime, force_unicode
|
||||||
from calibre.customize.ui import get_isbndb_key, set_isbndb_key
|
from calibre.customize.ui import get_isbndb_key, set_isbndb_key
|
||||||
|
|
||||||
_hung_fetchers = set([])
|
_hung_fetchers = set([])
|
||||||
@ -179,7 +179,7 @@ class FetchMetadata(QDialog, Ui_FetchMetadata):
|
|||||||
self.terminate()
|
self.terminate()
|
||||||
return self.queue_reject.emit()
|
return self.queue_reject.emit()
|
||||||
self.model = Matches(self.fetcher.results)
|
self.model = Matches(self.fetcher.results)
|
||||||
warnings = [(x[0], unicode(x[1])) for x in \
|
warnings = [(x[0], force_unicode(x[1])) for x in \
|
||||||
self.fetcher.exceptions if x[1] is not None]
|
self.fetcher.exceptions if x[1] is not None]
|
||||||
if warnings:
|
if warnings:
|
||||||
warnings='<br>'.join(['<b>%s</b>: %s'%(name, exc) for name,exc in warnings])
|
warnings='<br>'.join(['<b>%s</b>: %s'%(name, exc) for name,exc in warnings])
|
||||||
|
@ -98,7 +98,7 @@ class MyBlockingBusy(QDialog):
|
|||||||
return self.accept()
|
return self.accept()
|
||||||
|
|
||||||
def do_one(self, id):
|
def do_one(self, id):
|
||||||
remove, add, au, aus, do_aus, rating, pub, do_series, \
|
remove_all, remove, add, au, aus, do_aus, rating, pub, do_series, \
|
||||||
do_autonumber, do_remove_format, remove_format, do_swap_ta, \
|
do_autonumber, do_remove_format, remove_format, do_swap_ta, \
|
||||||
do_remove_conv, do_auto_author, series, do_series_restart, \
|
do_remove_conv, do_auto_author, series, do_series_restart, \
|
||||||
series_start_value, do_title_case, clear_series = self.args
|
series_start_value, do_title_case, clear_series = self.args
|
||||||
@ -168,6 +168,8 @@ class MyBlockingBusy(QDialog):
|
|||||||
# both of these are fast enough to just do them all
|
# both of these are fast enough to just do them all
|
||||||
for w in self.cc_widgets:
|
for w in self.cc_widgets:
|
||||||
w.commit(self.ids)
|
w.commit(self.ids)
|
||||||
|
if remove_all:
|
||||||
|
self.db.remove_all_tags(self.ids)
|
||||||
self.db.bulk_modify_tags(self.ids, add=add, remove=remove,
|
self.db.bulk_modify_tags(self.ids, add=add, remove=remove,
|
||||||
notify=False)
|
notify=False)
|
||||||
self.current_index = len(self.ids)
|
self.current_index = len(self.ids)
|
||||||
@ -640,9 +642,9 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
|||||||
for w in getattr(self, 'custom_column_widgets', []):
|
for w in getattr(self, 'custom_column_widgets', []):
|
||||||
w.gui_val
|
w.gui_val
|
||||||
|
|
||||||
if self.remove_all_tags.isChecked():
|
remove_all = self.remove_all_tags.isChecked()
|
||||||
remove = self.db.all_tags()
|
remove = []
|
||||||
else:
|
if not remove_all:
|
||||||
remove = unicode(self.remove_tags.text()).strip().split(',')
|
remove = unicode(self.remove_tags.text()).strip().split(',')
|
||||||
add = unicode(self.tags.text()).strip().split(',')
|
add = unicode(self.tags.text()).strip().split(',')
|
||||||
au = unicode(self.authors.text())
|
au = unicode(self.authors.text())
|
||||||
@ -663,7 +665,7 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
|||||||
do_auto_author = self.auto_author_sort.isChecked()
|
do_auto_author = self.auto_author_sort.isChecked()
|
||||||
do_title_case = self.change_title_to_title_case.isChecked()
|
do_title_case = self.change_title_to_title_case.isChecked()
|
||||||
|
|
||||||
args = (remove, add, au, aus, do_aus, rating, pub, do_series,
|
args = (remove_all, remove, add, au, aus, do_aus, rating, pub, do_series,
|
||||||
do_autonumber, do_remove_format, remove_format, do_swap_ta,
|
do_autonumber, do_remove_format, remove_format, do_swap_ta,
|
||||||
do_remove_conv, do_auto_author, series, do_series_restart,
|
do_remove_conv, do_auto_author, series, do_series_restart,
|
||||||
series_start_value, do_title_case, clear_series)
|
series_start_value, do_title_case, clear_series)
|
||||||
|
@ -308,6 +308,9 @@ from the value in the box</string>
|
|||||||
<property name="minimum">
|
<property name="minimum">
|
||||||
<number>1</number>
|
<number>1</number>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>990000</number>
|
||||||
|
</property>
|
||||||
<property name="value">
|
<property name="value">
|
||||||
<number>1</number>
|
<number>1</number>
|
||||||
</property>
|
</property>
|
||||||
@ -660,8 +663,8 @@ nothing should be put between the original text and the inserted text</string>
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>726</width>
|
<width>122</width>
|
||||||
<height>334</height>
|
<height>38</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="testgrid">
|
<layout class="QGridLayout" name="testgrid">
|
||||||
|
@ -8,13 +8,15 @@ __docformat__ = 'restructuredtext en'
|
|||||||
|
|
||||||
import cStringIO, sys
|
import cStringIO, sys
|
||||||
from binascii import hexlify, unhexlify
|
from binascii import hexlify, unhexlify
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
from PyQt4.Qt import QWidget, pyqtSignal, QDialog, Qt
|
from PyQt4.Qt import QWidget, pyqtSignal, QDialog, Qt, QLabel, \
|
||||||
|
QLineEdit, QDialogButtonBox, QGridLayout, QCheckBox
|
||||||
|
|
||||||
from calibre.gui2.wizard.send_email_ui import Ui_Form
|
from calibre.gui2.wizard.send_email_ui import Ui_Form
|
||||||
from calibre.utils.smtp import config as smtp_prefs
|
from calibre.utils.smtp import config as smtp_prefs
|
||||||
from calibre.gui2.dialogs.test_email_ui import Ui_Dialog as TE_Dialog
|
from calibre.gui2.dialogs.test_email_ui import Ui_Dialog as TE_Dialog
|
||||||
from calibre.gui2 import error_dialog, info_dialog
|
from calibre.gui2 import error_dialog
|
||||||
|
|
||||||
class TestEmail(QDialog, TE_Dialog):
|
class TestEmail(QDialog, TE_Dialog):
|
||||||
|
|
||||||
@ -74,8 +76,9 @@ class SendEmail(QWidget, Ui_Form):
|
|||||||
(self.relay_tls if opts.encryption == 'TLS' else self.relay_ssl).setChecked(True)
|
(self.relay_tls if opts.encryption == 'TLS' else self.relay_ssl).setChecked(True)
|
||||||
self.relay_tls.toggled.connect(self.changed)
|
self.relay_tls.toggled.connect(self.changed)
|
||||||
|
|
||||||
self.relay_use_gmail.clicked.connect(
|
for x in ('gmail', 'hotmail'):
|
||||||
self.create_gmail_relay)
|
button = getattr(self, 'relay_use_'+x)
|
||||||
|
button.clicked.connect(partial(self.create_service_relay, x))
|
||||||
self.relay_show_password.stateChanged.connect(
|
self.relay_show_password.stateChanged.connect(
|
||||||
lambda state : self.relay_password.setEchoMode(
|
lambda state : self.relay_password.setEchoMode(
|
||||||
self.relay_password.Password if
|
self.relay_password.Password if
|
||||||
@ -114,19 +117,79 @@ class SendEmail(QWidget, Ui_Form):
|
|||||||
sys.stdout, sys.stderr = oout, oerr
|
sys.stdout, sys.stderr = oout, oerr
|
||||||
return tb
|
return tb
|
||||||
|
|
||||||
def create_gmail_relay(self, *args):
|
def create_service_relay(self, service, *args):
|
||||||
self.relay_username.setText('@gmail.com')
|
service = {
|
||||||
self.relay_password.setText('')
|
'gmail': {
|
||||||
self.relay_host.setText('smtp.gmail.com')
|
'name': 'Gmail',
|
||||||
self.relay_port.setValue(587)
|
'relay': 'smtp.gmail.com',
|
||||||
|
'port': 587,
|
||||||
|
'username': '@gmail.com',
|
||||||
|
'url': 'www.gmail.com',
|
||||||
|
'extra': ''
|
||||||
|
},
|
||||||
|
'hotmail': {
|
||||||
|
'name': 'Hotmail',
|
||||||
|
'relay': 'smtp.live.com',
|
||||||
|
'port': 587,
|
||||||
|
'username': '',
|
||||||
|
'url': 'www.hotmail.com',
|
||||||
|
'extra': _('If you are setting up a new'
|
||||||
|
' hotmail account, you must log in to it '
|
||||||
|
' once before you will be able to send mails.'),
|
||||||
|
}
|
||||||
|
}[service]
|
||||||
|
d = QDialog(self)
|
||||||
|
l = QGridLayout()
|
||||||
|
d.setLayout(l)
|
||||||
|
bb = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel)
|
||||||
|
bb.accepted.connect(d.accept)
|
||||||
|
bb.rejected.connect(d.reject)
|
||||||
|
d.tl = QLabel('<p>'+_('You can sign up for a free {name} email '
|
||||||
|
'account at <a href="http://{url}">http://{url}</a>. {extra}').format(
|
||||||
|
**service))
|
||||||
|
l.addWidget(d.tl, 0, 0, 3, 0)
|
||||||
|
d.tl.setWordWrap(True)
|
||||||
|
d.tl.setOpenExternalLinks(True)
|
||||||
|
for name, label in (
|
||||||
|
['from_', _('Your %s &email address:')],
|
||||||
|
['username', _('Your %s &username:')],
|
||||||
|
['password', _('Your %s &password:')],
|
||||||
|
):
|
||||||
|
la = QLabel(label%service['name'])
|
||||||
|
le = QLineEdit(d)
|
||||||
|
setattr(d, name, le)
|
||||||
|
setattr(d, name+'_label', la)
|
||||||
|
r = l.rowCount()
|
||||||
|
l.addWidget(la, r, 0)
|
||||||
|
l.addWidget(le, r, 1)
|
||||||
|
la.setBuddy(le)
|
||||||
|
if name == 'password':
|
||||||
|
d.ptoggle = QCheckBox(_('&Show password'), d)
|
||||||
|
l.addWidget(d.ptoggle, r, 2)
|
||||||
|
d.ptoggle.stateChanged.connect(
|
||||||
|
lambda s: d.password.setEchoMode(d.password.Normal if s
|
||||||
|
== Qt.Checked else d.password.Password))
|
||||||
|
d.username.setText(service['username'])
|
||||||
|
d.password.setEchoMode(d.password.Password)
|
||||||
|
d.bl = QLabel('<p>' + _(
|
||||||
|
'If you plan to use email to send books to your Kindle, remember to'
|
||||||
|
' add the your %s email address to the allowed email addresses in your '
|
||||||
|
'Amazon.com Kindle management page.')%service['name'])
|
||||||
|
d.bl.setWordWrap(True)
|
||||||
|
l.addWidget(d.bl, l.rowCount(), 0, 3, 0)
|
||||||
|
l.addWidget(bb, l.rowCount(), 0, 3, 0)
|
||||||
|
d.setWindowTitle(_('Setup') + ' ' + service['name'])
|
||||||
|
d.resize(d.sizeHint())
|
||||||
|
bb.setVisible(True)
|
||||||
|
if d.exec_() != d.Accepted:
|
||||||
|
return
|
||||||
|
self.relay_username.setText(d.username.text())
|
||||||
|
self.relay_password.setText(d.password.text())
|
||||||
|
self.email_from.setText(d.from_.text())
|
||||||
|
self.relay_host.setText(service['relay'])
|
||||||
|
self.relay_port.setValue(service['port'])
|
||||||
self.relay_tls.setChecked(True)
|
self.relay_tls.setChecked(True)
|
||||||
|
|
||||||
info_dialog(self, _('Finish gmail setup'),
|
|
||||||
_('Dont forget to enter your gmail username and password. '
|
|
||||||
'You can sign up for a free gmail account at http://gmail.com')).exec_()
|
|
||||||
self.relay_username.setFocus(Qt.OtherFocusReason)
|
|
||||||
self.relay_username.setCursorPosition(0)
|
|
||||||
|
|
||||||
def set_email_settings(self, to_set):
|
def set_email_settings(self, to_set):
|
||||||
from_ = unicode(self.email_from.text()).strip()
|
from_ = unicode(self.email_from.text()).strip()
|
||||||
if to_set and not from_:
|
if to_set and not from_:
|
||||||
|
@ -216,6 +216,26 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="relay_use_hotmail">
|
||||||
|
<property name="text">
|
||||||
|
<string>Use Hotmail</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="../../../../resources/images.qrc">
|
||||||
|
<normaloff>:/images/hotmail.png</normaloff>:/images/hotmail.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="iconSize">
|
||||||
|
<size>
|
||||||
|
<width>48</width>
|
||||||
|
<height>48</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="toolButtonStyle">
|
||||||
|
<enum>Qt::ToolButtonTextUnderIcon</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="test_email_button">
|
<widget class="QPushButton" name="test_email_button">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
@ -36,33 +36,8 @@ from calibre.utils.config import prefs, tweaks
|
|||||||
from calibre.utils.search_query_parser import saved_searches, set_saved_searches
|
from calibre.utils.search_query_parser import saved_searches, set_saved_searches
|
||||||
from calibre.ebooks import BOOK_EXTENSIONS, check_ebook_format
|
from calibre.ebooks import BOOK_EXTENSIONS, check_ebook_format
|
||||||
from calibre.utils.magick.draw import save_cover_data_to
|
from calibre.utils.magick.draw import save_cover_data_to
|
||||||
|
from calibre.utils.recycle_bin import delete_file, delete_tree
|
||||||
|
|
||||||
if iswindows:
|
|
||||||
import calibre.utils.winshell as winshell
|
|
||||||
|
|
||||||
def delete_file(path):
|
|
||||||
try:
|
|
||||||
winshell.delete_file(path, silent=True, no_confirm=True)
|
|
||||||
except:
|
|
||||||
os.remove(path)
|
|
||||||
|
|
||||||
def delete_tree(path, permanent=False):
|
|
||||||
if permanent:
|
|
||||||
try:
|
|
||||||
# For completely mysterious reasons, sometimes a file is left open
|
|
||||||
# leading to access errors. If we get an exception, wait and hope
|
|
||||||
# that whatever has the file (the O/S?) lets go of it.
|
|
||||||
shutil.rmtree(path)
|
|
||||||
except:
|
|
||||||
traceback.print_exc()
|
|
||||||
time.sleep(1)
|
|
||||||
shutil.rmtree(path)
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
if not permanent:
|
|
||||||
winshell.delete_file(path, silent=True, no_confirm=True)
|
|
||||||
except:
|
|
||||||
delete_tree(path, permanent=True)
|
|
||||||
|
|
||||||
copyfile = os.link if hasattr(os, 'link') else shutil.copyfile
|
copyfile = os.link if hasattr(os, 'link') else shutil.copyfile
|
||||||
|
|
||||||
@ -983,9 +958,6 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
path = None
|
path = None
|
||||||
self.data.remove(id)
|
self.data.remove(id)
|
||||||
if path and os.path.exists(path):
|
if path and os.path.exists(path):
|
||||||
try:
|
|
||||||
winshell.delete_file(path, no_confirm=True, silent=True)
|
|
||||||
except:
|
|
||||||
self.rmtree(path)
|
self.rmtree(path)
|
||||||
parent = os.path.dirname(path)
|
parent = os.path.dirname(path)
|
||||||
if len(os.listdir(parent)) == 0:
|
if len(os.listdir(parent)) == 0:
|
||||||
@ -1759,6 +1731,18 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
ans.append(tag)
|
ans.append(tag)
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
def remove_all_tags(self, ids, notify=False, commit=True):
|
||||||
|
self.conn.executemany(
|
||||||
|
'DELETE FROM books_tags_link WHERE book=?', [(x,) for x in ids])
|
||||||
|
self.dirtied(ids, commit=False)
|
||||||
|
if commit:
|
||||||
|
self.conn.commit()
|
||||||
|
|
||||||
|
for x in ids:
|
||||||
|
self.data.set(x, self.FIELD_MAP['tags'], '', row_is_id=True)
|
||||||
|
if notify:
|
||||||
|
self.notify('metadata', ids)
|
||||||
|
|
||||||
def bulk_modify_tags(self, ids, add=[], remove=[], notify=False):
|
def bulk_modify_tags(self, ids, add=[], remove=[], notify=False):
|
||||||
add = self.cleanup_tags(add)
|
add = self.cleanup_tags(add)
|
||||||
remove = self.cleanup_tags(remove)
|
remove = self.cleanup_tags(remove)
|
||||||
|
59
src/calibre/utils/recycle_bin.py
Normal file
59
src/calibre/utils/recycle_bin.py
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
import os, shutil, time
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
|
from calibre import isbytestring
|
||||||
|
from calibre.constants import iswindows, isosx, plugins, filesystem_encoding
|
||||||
|
|
||||||
|
recycle = None
|
||||||
|
|
||||||
|
if iswindows:
|
||||||
|
import calibre.utils.winshell as winshell
|
||||||
|
recycle = partial(winshell.delete_file, silent=True, no_confirm=True)
|
||||||
|
elif isosx:
|
||||||
|
u = plugins['usbobserver'][0]
|
||||||
|
if hasattr(u, 'send2trash'):
|
||||||
|
def recycle(path):
|
||||||
|
if isbytestring(path):
|
||||||
|
path = path.decode(filesystem_encoding)
|
||||||
|
u.send2trash(path)
|
||||||
|
|
||||||
|
|
||||||
|
def delete_file(path):
|
||||||
|
if callable(recycle):
|
||||||
|
try:
|
||||||
|
recycle(path)
|
||||||
|
return
|
||||||
|
except:
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
os.remove(path)
|
||||||
|
|
||||||
|
def delete_tree(path, permanent=False):
|
||||||
|
if permanent:
|
||||||
|
try:
|
||||||
|
# For completely mysterious reasons, sometimes a file is left open
|
||||||
|
# leading to access errors. If we get an exception, wait and hope
|
||||||
|
# that whatever has the file (the O/S?) lets go of it.
|
||||||
|
shutil.rmtree(path)
|
||||||
|
except:
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
time.sleep(1)
|
||||||
|
shutil.rmtree(path)
|
||||||
|
else:
|
||||||
|
if callable(recycle):
|
||||||
|
try:
|
||||||
|
recycle(path)
|
||||||
|
return
|
||||||
|
except:
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
delete_tree(path, permanent=True)
|
||||||
|
|
@ -101,8 +101,12 @@ def sendmail(msg, from_, to, localhost=None, verbose=0, timeout=30,
|
|||||||
if encryption == 'SSL':
|
if encryption == 'SSL':
|
||||||
s.sock = s.file.sslobj
|
s.sock = s.file.sslobj
|
||||||
s.login(username, password)
|
s.login(username, password)
|
||||||
|
ret = None
|
||||||
|
try:
|
||||||
s.sendmail(from_, to, msg)
|
s.sendmail(from_, to, msg)
|
||||||
return s.quit()
|
finally:
|
||||||
|
ret = s.quit()
|
||||||
|
return ret
|
||||||
|
|
||||||
def option_parser():
|
def option_parser():
|
||||||
try:
|
try:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user