KG updates Qt 4.7

This commit is contained in:
GRiker 2010-11-16 15:30:36 -07:00
commit 179f8853dc
28 changed files with 438 additions and 213 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -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 = [
(u'Latest headlines', u'http://www.theglobeandmail.com/?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'Opinions', u'http://www.theglobeandmail.com/news/opinions/?service=rss'),
(u'Columnists', u'http://www.theglobeandmail.com/news/opinions/columnists/?service=rss'),
(u'Globe Investor', u'http://www.theglobeandmail.com/globe-investor/?service=rss'),
(u'Sports', u'http://www.theglobeandmail.com/sports/?service=rss'),
(u'Technology', u'http://www.theglobeandmail.com/news/technology/?service=rss'),
(u'Arts', u'http://www.theglobeandmail.com/news/arts/?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'Auto', u'http://www.theglobeandmail.com/auto/?service=rss')
]
def get_article_url(self, article): feeds = [
url = BasicNewsRecipe.get_article_url(self, article) (u'Top National Stories', u'http://www.theglobeandmail.com/news/national/?service=rss'),
if '/video/' not in url: (u'Business', u'http://www.theglobeandmail.com/report-on-business/?service=rss'),
return url (u'Commentary', u'http://www.theglobeandmail.com/report-on-business/commentary/?service=rss'),
(u'Blogs', u'http://www.theglobeandmail.com/blogs/?service=rss'),
(u'Facts & Arguments', u'http://www.theglobeandmail.com/life/facts-and-arguments/?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'Life', u'http://www.theglobeandmail.com/life/?service=rss'),
(u'Real Estate', u'http://www.theglobeandmail.com/real-estate/?service=rss'),
(u'Auto', u'http://www.theglobeandmail.com/sports/?service=rss'),
(u'Sports', u'http://www.theglobeandmail.com/auto/?service=rss')
]
keep_only_tags = [
dict(name='h1'),
dict(name='h2', attrs={'id':'articletitle'}),
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'

View File

@ -1,31 +1,33 @@
#!/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).'
language = 'en' 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'
lang = 'en' publisher = 'The Moscow Times'
oldest_article = 7 language = 'en'
oldest_article = 2
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 = '''
h1{ color:#0066B3; font-family: Georgia,serif ; font-size: large} h1{ color:#0066B3; font-family: Georgia,serif ; font-size: large}
@ -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 = [ keep_only_tags = [dict(name='div', attrs={'id':'content'})]
dict(name='div', attrs={'class':['newstextblock']})
]
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'})
a = div.find('a') if div:
print a a = div.find('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

View 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

View 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

View File

@ -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 = {

View File

@ -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')

View File

@ -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()

View File

@ -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

View File

@ -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'

View File

@ -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

View File

@ -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
----- -----

View File

@ -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,)

View File

@ -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)

View File

@ -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}
}; };

View File

@ -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

View File

@ -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();
} }

View File

@ -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))

View File

@ -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>

View File

@ -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])

View File

@ -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)

View File

@ -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">

View File

@ -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_:

View File

@ -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">

View File

@ -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,10 +958,7 @@ 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: self.rmtree(path)
winshell.delete_file(path, no_confirm=True, silent=True)
except:
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:
self.rmtree(parent) self.rmtree(parent)
@ -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)

View 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)

View File

@ -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)
s.sendmail(from_, to, msg) ret = None
return s.quit() try:
s.sendmail(from_, to, msg)
finally:
ret = s.quit()
return ret
def option_parser(): def option_parser():
try: try: