Merge from trunk

This commit is contained in:
Charles Haley 2010-06-12 10:58:43 +01:00
commit 615f9b8e8f
43 changed files with 25037 additions and 11605 deletions

View File

@ -4,6 +4,65 @@
# for important features/bug fixes. # for important features/bug fixes.
# Also, each release can have new and improved recipes. # Also, each release can have new and improved recipes.
- version: 0.7.2
date: 2010-06-11
new features:
- title: "The Cover Browser can now be freely resized."
description: >
"You can now resize the Cover Browser just like the other areas of the user interface by dragging the edge. The Cover Browser now
also emphasizes the cetral book cover, making it larger than the others. Also on widescreen monitors the cover browser is now automatically placed
to the side of the book list instead of below it."
- title: "Added tweak to control how titles and series names are sorted"
- title: "Clicking on row numbers no longer open the viewer. Instead you have to double click"
bug fixes:
- title: "SONY driver: The regression causing slow perfomance has been corrected. Various bug fixes to deal with corner cases"
- title: "iPad driver: Various bugfixes, should now work much more seamlessly."
- title: "Fix regression causing calibre to not start if the library path is invalid, because say a drive has been removed"
tickets: [5787]
- title: "Fix regression in 0.7.1 that broke searching in the e-book viewers and for news sources"
- title: "Fix regressions that caused the Publisher to change when updating Series and floating point custom columns to be rounded to integers."
tickets: [5788]
- title: "Fix regression that broke check database integrity in the presence of custom coulmns or user categories"
tickets: [5779]
- title: "Fix regresison that broke the Email to submenu in the send to device menu"
- title: "Fix Tag browser re-opening closed tree after editing metadata"
tickets: [5744]
- title: "Conversion pipeline: Handle missing/obsolete input/output profiles gracefully"
- title: "Fix Adding/Deleting Search does not refresh Left Pane Correctly"
tickets: [5751]
- title: "Content server: Fix serving of CBZ/CBR files to stanza"
new recipes:
- title: Repantes, Haaretz
author: Darko Miletic
- title: CBC Canada
author: rty
improved recipes:
- The Sun
- The Economist
- Boston Globe
- Honolulu Star Advertiser
- SMH
- Sueddeutsche
- Our Daily Bread
- version: 0.7.1 - version: 0.7.1
date: 2010-06-04 date: 2010-06-04

Binary file not shown.

Before

Width:  |  Height:  |  Size: 983 B

After

Width:  |  Height:  |  Size: 983 B

View File

@ -1,5 +1,5 @@
__license__ = 'GPL v3' __license__ = 'GPL v3'
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>' __copyright__ = '2009-2010, Darko Miletic <darko.miletic at gmail.com>'
''' '''
www.boston.com www.boston.com
''' '''
@ -7,10 +7,10 @@ www.boston.com
from calibre.web.feeds.recipes import BasicNewsRecipe from calibre.web.feeds.recipes import BasicNewsRecipe
class BusinessStandard(BasicNewsRecipe): class BusinessStandard(BasicNewsRecipe):
title = 'Boston' title = 'The Boston Globe'
__author__ = 'Darko Miletic' __author__ = 'Darko Miletic'
description = 'News from Boston' description = 'News from Boston'
oldest_article = 7 oldest_article = 2
max_articles_per_feed = 100 max_articles_per_feed = 100
no_stylesheets = True no_stylesheets = True
delay = 1 delay = 1
@ -19,6 +19,9 @@ class BusinessStandard(BasicNewsRecipe):
publisher = 'Boston' publisher = 'Boston'
category = 'news, boston, usa, world' category = 'news, boston, usa, world'
language = 'en' language = 'en'
publication_type = 'newspaper'
masthead_url = 'http://cache.boston.com/images/globe/grslider/the_boston_globe.gif'
extra_css = ' body{font-family: Georgia, serif} div#articleBodyTop{display:block} '
conversion_options = { conversion_options = {
'comments' : description 'comments' : description
@ -27,8 +30,11 @@ class BusinessStandard(BasicNewsRecipe):
,'publisher' : publisher ,'publisher' : publisher
} }
keep_only_tags = [dict(name='div', attrs={'class':'story'})] keep_only_tags = [dict(attrs={'id':['INDblogEntry','blogEntry','articleHeader','articleGraphs','galleryShell']})]
remove_tags = [dict(name=['object','link','script','iframe'])] remove_tags = [
dict(name=['object','link','script','iframe'])
,dict(attrs={'id':['blogheadTools','bdc_emailWidget','tools','relatedContent']})
]
feeds = [ feeds = [
(u'Top Stories' , u'http://feeds.boston.com/boston/topstories' ) (u'Top Stories' , u'http://feeds.boston.com/boston/topstories' )
@ -38,12 +44,9 @@ class BusinessStandard(BasicNewsRecipe):
] ]
def print_version(self, url): def print_version(self, url):
return url + '?mode=PF' return url + '?page=full'
def get_article_url(self, article): def get_article_url(self, article):
rawarticle = article.get('pheedo_origlink', None) rawarticle = article.get('guid', None)
artls, sep, rsep = rawarticle.rpartition('/?') return rawarticle.rpartition('?')[0]
if artls == '':
artls = rawarticle.rpartition('?')[0]
return artls

View File

@ -88,7 +88,9 @@ class Economist(BasicNewsRecipe):
continue continue
a = tag.find('a', href=True) a = tag.find('a', href=True)
if a is not None: if a is not None:
url=a['href'].split('?')[0]+'/print' url=a['href']
id_ = re.search(r'story_id=(\d+)', url).group(1)
url = 'http://www.economist.com/node/%s/print'%id_
if url.startswith('Printer'): if url.startswith('Printer'):
url = '/'+url url = '/'+url
if url.startswith('/'): if url.startswith('/'):

View File

@ -1,96 +0,0 @@
#!/usr/bin/env python
# -*- coding: cp1252 -*-
__license__ = 'GPL v3'
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>'
'''
honoluluadvertiser.com
'''
from calibre.web.feeds.news import BasicNewsRecipe
class Honoluluadvertiser(BasicNewsRecipe):
title = 'Honolulu Advertiser'
__author__ = 'Darko Miletic and Sujata Raman'
description = "Latest national and local Hawaii sports news from The Honolulu Advertiser."
publisher = 'Honolulu Advertiser'
category = 'news, Honolulu, Hawaii'
oldest_article = 2
language = 'en'
max_articles_per_feed = 100
no_stylesheets = True
use_embedded_content = False
encoding = 'cp1252'
remove_javascript = True
cover_url = 'http://www.honoluluadvertiser.com/graphics/frontpage/frontpage.jpg'
html2lrf_options = [
'--comment' , description
, '--category' , category
, '--publisher' , publisher
]
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"'
keep_only_tags = [dict(name='div', attrs={'class':["hon_article_top","article-bodytext","hon_article_photo","storyphoto","article"]}),
dict(name='div', attrs={'id':["storycontentleft","article"]})
]
remove_tags = [dict(name=['object','link','embed']),
dict(name='div', attrs={'class':["article-tools","titleBar","invisiblespacer","articleflex-container","hon_newslist","categoryheader","columnframe","subHeadline","poster-container"]}),
dict(name='div', attrs={'align':["right"]}),
dict(name='div', attrs={'id':["pluckcomments"]}),
dict(name='td', attrs={'class':["prepsfacts"]}),
dict(name='img', attrs={'height':["1"]}),
dict(name='img', attrs={'alt':["Advertisement"]}),
dict(name='img', attrs={'src':["/gcicommonfiles/sr/graphics/common/adlabel_horz.gif","/gcicommonfiles/sr/graphics/common/icon_whatsthis.gif",]}),
]
extra_css = '''
h1{font-family:Arial,Helvetica,sans-serif; font-size:large; color:#000000; }
.hon_article_timestamp{font-family:Arial,Helvetica,sans-serif; font-size:70%; }
.postedStoryDate{font-family:Arial,Helvetica,sans-serif; font-size:30%; }
.postedDate{font-family:Arial,Helvetica,sans-serif; font-size:30%; }
.credit{font-family:Arial,Helvetica,sans-serif; font-size:30%; }
.hon_article_top{font-family:Arial,Helvetica,sans-serif; color:#666666; font-size:30%; font-weight:bold;}
.grayBackground{font-family:Arial,Helvetica,sans-serif; color:#666666; font-size:30%;}
.hon_photocaption{font-family:Arial,Helvetica,sans-serif; font-size:30%; }
.photoCaption{font-family:Arial,Helvetica,sans-serif; font-size:30%; }
.hon_photocredit{font-family:Arial,Helvetica,sans-serif; font-size:30%; color:#666666;}
.storyphoto{font-family:Arial,Helvetica,sans-serif; font-size:30%; color:#666666;}
.article-bodytext{font-family:Arial,Helvetica,sans-serif; font-size:xx-small; }
.storycontentleft{font-family:Arial,Helvetica,sans-serif; font-size:xx-small; }
#article{font-family:Arial,Helvetica,sans-serif; font-size:xx-small; }
.contentarea{font-family:Arial,Helvetica,sans-serif; font-size:xx-small; }
.storytext{font-family:Verdana,Arial,Helvetica,sans-serif; font-size:xx-small;}
.storyHeadline{font-family:Arial,Helvetica,sans-serif; font-size:large; color:#000000; font-weight:bold;}
.source{font-family:Arial,Helvetica,sans-serif; color:#333333; font-style: italic; font-weight:bold; }
'''
feeds = [
(u'Breaking news', u'http://www.honoluluadvertiser.com/apps/pbcs.dll/section?Category=RSS01&MIME=XML' )
,(u'Local news', u'http://www.honoluluadvertiser.com/apps/pbcs.dll/section?Category=RSS02&MIME=XML' )
,(u'Sports', u'http://www.honoluluadvertiser.com/apps/pbcs.dll/section?Category=RSS03&MIME=XML' )
,(u'Island life', u'http://www.honoluluadvertiser.com/apps/pbcs.dll/section?Category=RSS05&MIME=XML' )
,(u'Entertainment', u'http://www.honoluluadvertiser.com/apps/pbcs.dll/section?Category=RSS06&MIME=XML' )
,(u'Business', u'http://www.honoluluadvertiser.com/apps/pbcs.dll/section?Category=RSS04&MIME=XML' )
]
def preprocess_html(self, soup):
for item in soup.findAll(style=True):
del item['style']
mtag = '\n<meta http-equiv="Content-Language" content="en"/>\n'
soup.head.insert(0,mtag)
for tag in soup.findAll(name=['span','table','font']):
tag.name = 'div'
return soup
# def print_version(self, url):
# ubody, sep, rest = url.rpartition('/-1/')
# root, sep2, article_id = ubody.partition('/article/')
# return u'http://www.honoluluadvertiser.com/apps/pbcs.dll/article?AID=/' + article_id + '&template=printart'

View File

@ -51,6 +51,7 @@ class LeMondeDiplomatiqueEn(BasicNewsRecipe):
, dict(name='div',attrs={'class':'notes surlignable'}) , dict(name='div',attrs={'class':'notes surlignable'})
] ]
remove_tags = [dict(name=['object','link','script','iframe','base'])] remove_tags = [dict(name=['object','link','script','iframe','base'])]
remove_attributes = ['height','width']
def parse_index(self): def parse_index(self):
articles = [] articles = []
@ -72,5 +73,5 @@ class LeMondeDiplomatiqueEn(BasicNewsRecipe):
,'url' :url ,'url' :url
,'description':description ,'description':description
}) })
return [(soup.head.title.string, articles)] return [(self.title, articles)]

View File

@ -0,0 +1,47 @@
__license__ = 'GPL v3'
__copyright__ = '2009-2010, Darko Miletic <darko.miletic at gmail.com>'
'''
staradvertiser.com
'''
from calibre.web.feeds.news import BasicNewsRecipe
class Starbulletin(BasicNewsRecipe):
title = 'Honolulu Star Advertiser'
__author__ = 'Darko Miletic'
description = "Latest national and local Hawaii sports news"
publisher = 'Honolulu Star-Advertiser'
category = 'news, Honolulu, Hawaii'
oldest_article = 2
max_articles_per_feed = 100
language = 'en'
no_stylesheets = True
use_embedded_content = False
encoding = 'utf8'
publication_type = 'newspaper'
extra_css = ' body{font-family: Verdana,Arial,Helvetica,sans-serif} h1,.brown,.postCredit{color: #663300} .storyDeck{font-size: 1.2em; font-weight: bold} '
conversion_options = {
'comment' : description
, 'tags' : category
, 'publisher' : publisher
, 'language' : language
, 'linearize_tables' : True
}
remove_tags_before = dict(attrs={'id':'storyTitle'})
remove_tags_after = dict(name='div', attrs={'class':'storytext'})
remove_tags = [
dict(name=['object','link'])
,dict(attrs={'class':'insideStoryImage'})
]
feeds = [
(u'Headlines' , u'http://www.staradvertiser.com/staradvertiser_headlines.rss' )
,(u'News' , u'http://www.staradvertiser.com/news/index.rss' )
,(u'Sports' , u'http://www.staradvertiser.com/sports/index.rss' )
,(u'Features' , u'http://www.staradvertiser.com/features/index.rss' )
,(u'Editorials', u'http://www.staradvertiser.com/editorials/index.rss' )
,(u'Business' , u'http://www.staradvertiser.com/business/index.rss' )
,(u'Travel' , u'http://www.staradvertiser.com/travel/index.rss' )
]

View File

@ -1,60 +0,0 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>'
'''
starbulletin.com
'''
from calibre.web.feeds.news import BasicNewsRecipe
class Starbulletin(BasicNewsRecipe):
title = 'Honolulu Star-Bulletin'
__author__ = 'Darko Miletic'
description = "Latest national and local Hawaii sports news"
publisher = 'Honolulu Star-Bulletin'
category = 'news, Honolulu, Hawaii'
oldest_article = 2
max_articles_per_feed = 100
language = 'en'
no_stylesheets = True
use_embedded_content = False
encoding = 'utf8'
remove_javascript = True
cover_url = 'http://media.starbulletin.com/designimages/spacer.gif'
html2lrf_options = [
'--comment' , description
, '--category' , category
, '--publisher' , publisher
]
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"'
keep_only_tags = [ dict(name='div', attrs={'id':'storyColoumn'}) ]
remove_tags = [
dict(name=['object','link'])
,dict(name='span', attrs={'id':'printdesc'})
,dict(name='div' , attrs={'class':'lightGreyBox storyTools clearAll'})
,dict(name='div' , attrs={'id':'breadcrumbs'})
]
feeds = [
(u'Headlines', u'http://www.starbulletin.com/starbulletin_headlines.rss' )
,(u'News', u'http://www.starbulletin.com/news/index.rss' )
,(u'Sports', u'http://www.starbulletin.com/sports/index.rss' )
,(u'Features', u'http://www.starbulletin.com/features/index.rss' )
,(u'Editorials', u'http://www.starbulletin.com/editorials/index.rss' )
,(u'Business', u'http://www.starbulletin.com/business/index.rss' )
,(u'Travel', u'http://www.starbulletin.com/travel/index.rss' )
]
def preprocess_html(self, soup):
for item in soup.findAll(style=True):
del item['style']
mtag = '\n<meta http-equiv="Content-Language" content="en"/>\n'
soup.head.insert(0,mtag)
return soup

View File

@ -1,5 +1,6 @@
import re import re
from calibre.web.feeds.news import BasicNewsRecipe from calibre.web.feeds.news import BasicNewsRecipe
from calibre.ebooks.BeautifulSoup import Tag
class AdvancedUserRecipe1268409464(BasicNewsRecipe): class AdvancedUserRecipe1268409464(BasicNewsRecipe):
title = u'The Sun' title = u'The Sun'
@ -14,24 +15,27 @@ class AdvancedUserRecipe1268409464(BasicNewsRecipe):
remove_javascript = True remove_javascript = True
keep_only_tags = [ keep_only_tags = [
dict(name='div', attrs={'class':'medium-centered'}) dict(id='column-print')
,dict(name='div', attrs={'class':'article'})
,dict(name='div', attrs={'class':'clear-left'})
,dict(name='div', attrs={'class':'text-center'})
] ]
remove_tags = [ remove_tags = [
dict(name='div', attrs={'class':'slideshow'}) dict(name='div', attrs={'class':[
,dict(name='div', attrs={'class':'float-left'}) 'clear text-center small padding-left-right-5 text-999 padding-top-5 padding-bottom-10 grey-solid-line',
,dict(name='div', attrs={'class':'ltbx-slideshow ltbx-btn-ss'}) 'clear width-625 bg-fff padding-top-10'
,dict(name='a', attrs={'class':'add_a_comment'}) ]}),
,dict(name='div', attrs={'id':'vxFlashPlayerContent'}) dict(name='video'),
,dict(name='div', attrs={'id':'k1006094r1c1t5w380h529'})
,dict(name='div', attrs={'id':'tum_login_form_container'})
,dict(name='div', attrs={'class':'discHeader'})
,dict(name='div', attrs={'class':'margin-bottom-neg-2'})
] ]
def preprocess_html(self, soup):
h1 = soup.find('h1')
if h1 is not None:
text = self.tag_to_string(h1)
nh = Tag(soup, 'h1')
nh.insert(0, text)
h1.replaceWith(nh)
return soup
feeds = [(u'News', u'http://www.thesun.co.uk/sol/homepage/feeds/rss/article312900.ece') feeds = [(u'News', u'http://www.thesun.co.uk/sol/homepage/feeds/rss/article312900.ece')
,(u'Sport', u'http://www.thesun.co.uk/sol/homepage/feeds/rss/article247732.ece') ,(u'Sport', u'http://www.thesun.co.uk/sol/homepage/feeds/rss/article247732.ece')

View File

@ -2,7 +2,7 @@ __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net' __copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
__appname__ = 'calibre' __appname__ = 'calibre'
__version__ = '0.7.1' __version__ = '0.7.2'
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>" __author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
import re import re

View File

@ -445,7 +445,7 @@ from calibre.devices.nook.driver import NOOK
from calibre.devices.prs505.driver import PRS505 from calibre.devices.prs505.driver import PRS505
from calibre.devices.android.driver import ANDROID, S60 from calibre.devices.android.driver import ANDROID, S60
from calibre.devices.nokia.driver import N770, N810, E71X from calibre.devices.nokia.driver import N770, N810, E71X
from calibre.devices.eslick.driver import ESLICK from calibre.devices.eslick.driver import ESLICK, EBK52
from calibre.devices.nuut2.driver import NUUT2 from calibre.devices.nuut2.driver import NUUT2
from calibre.devices.iriver.driver import IRIVER_STORY from calibre.devices.iriver.driver import IRIVER_STORY
from calibre.devices.binatone.driver import README from calibre.devices.binatone.driver import README
@ -519,6 +519,7 @@ plugins += [
N810, N810,
COOL_ER, COOL_ER,
ESLICK, ESLICK,
EBK52,
NUUT2, NUUT2,
IRIVER_STORY, IRIVER_STORY,
GER2, GER2,

View File

@ -14,7 +14,6 @@ from calibre.devices.interface import DevicePlugin
from calibre.ebooks.BeautifulSoup import BeautifulSoup from calibre.ebooks.BeautifulSoup import BeautifulSoup
from calibre.ebooks.metadata import MetaInformation from calibre.ebooks.metadata import MetaInformation
from calibre.library.server.utils import strftime from calibre.library.server.utils import strftime
from calibre.ptempfile import PersistentTemporaryFile
from calibre.utils.config import Config, config_dir from calibre.utils.config import Config, config_dir
from calibre.utils.date import parse_date from calibre.utils.date import parse_date
from calibre.utils.logging import Log from calibre.utils.logging import Log
@ -147,6 +146,7 @@ class ITUNES(DevicePlugin):
ejected = False ejected = False
iTunes= None iTunes= None
iTunes_media = None iTunes_media = None
library_orphans = None
log = Log() log = Log()
manual_sync_mode = False manual_sync_mode = False
path_template = 'iTunes/%s - %s.epub' path_template = 'iTunes/%s - %s.epub'
@ -244,14 +244,13 @@ class ITUNES(DevicePlugin):
# Fetch a list of books from iPod device connected to iTunes # Fetch a list of books from iPod device connected to iTunes
# Fetch Library|Books
library_books = self._get_library_books()
if 'iPod' in self.sources: if 'iPod' in self.sources:
booklist = BookList(self.log) booklist = BookList(self.log)
cached_books = {} cached_books = {}
if isosx: if isosx:
library_books = self._get_library_books()
device_books = self._get_device_books() device_books = self._get_device_books()
book_count = float(len(device_books)) book_count = float(len(device_books))
for (i,book) in enumerate(device_books): for (i,book) in enumerate(device_books):
@ -281,11 +280,13 @@ class ITUNES(DevicePlugin):
if self.report_progress is not None: if self.report_progress is not None:
self.report_progress(i+1/book_count, _('%d of %d') % (i+1, book_count)) self.report_progress(i+1/book_count, _('%d of %d') % (i+1, book_count))
self._purge_orphans(cached_books)
elif iswindows: elif iswindows:
try: try:
pythoncom.CoInitialize() pythoncom.CoInitialize()
self.iTunes = win32com.client.Dispatch("iTunes.Application") self.iTunes = win32com.client.Dispatch("iTunes.Application")
library_books = self._get_library_books()
device_books = self._get_device_books() device_books = self._get_device_books()
book_count = float(len(device_books)) book_count = float(len(device_books))
for (i,book) in enumerate(device_books): for (i,book) in enumerate(device_books):
@ -315,6 +316,7 @@ class ITUNES(DevicePlugin):
if self.report_progress is not None: if self.report_progress is not None:
self.report_progress(i+1/book_count, self.report_progress(i+1/book_count,
_('%d of %d') % (i+1, book_count)) _('%d of %d') % (i+1, book_count))
self._purge_orphans(cached_books)
finally: finally:
pythoncom.CoUninitialize() pythoncom.CoUninitialize()
@ -985,7 +987,6 @@ class ITUNES(DevicePlugin):
connected_device = self.sources['iPod'] connected_device = self.sources['iPod']
device = self.iTunes.sources.ItemByName(connected_device) device = self.iTunes.sources.ItemByName(connected_device)
dev_books = None
added = None added = None
for pl in device.Playlists: for pl in device.Playlists:
if pl.Kind == self.PlaylistKind.index('User') and \ if pl.Kind == self.PlaylistKind.index('User') and \
@ -1638,7 +1639,6 @@ class ITUNES(DevicePlugin):
connected_device = self.sources['iPod'] connected_device = self.sources['iPod']
device = self.iTunes.sources.ItemByName(connected_device) device = self.iTunes.sources.ItemByName(connected_device)
dev_books = None
for pl in device.Playlists: for pl in device.Playlists:
if pl.Kind == self.PlaylistKind.index('User') and \ if pl.Kind == self.PlaylistKind.index('User') and \
pl.SpecialKind == self.PlaylistSpecialKind.index('Books'): pl.SpecialKind == self.PlaylistSpecialKind.index('Books'):
@ -1671,11 +1671,13 @@ class ITUNES(DevicePlugin):
def _get_library_books(self): def _get_library_books(self):
''' '''
Populate a dict of paths from iTunes Library|Books Populate a dict of paths from iTunes Library|Books
Windows assumes pythoncom wrapper
''' '''
if DEBUG: if DEBUG:
self.log.info("\n ITUNES._get_library_books()") self.log.info("\n ITUNES._get_library_books()")
library_books = {} library_books = {}
library_orphans = {}
lib = None lib = None
if isosx: if isosx:
@ -1708,15 +1710,14 @@ class ITUNES(DevicePlugin):
if DEBUG: if DEBUG:
self.log.info(" ignoring '%s' of type '%s'" % (book.name(), book.kind())) self.log.info(" ignoring '%s' of type '%s'" % (book.name(), book.kind()))
else: else:
# Remove calibre orphans # Collect calibre orphans - remnants of recipe uploads
path = self.path_template % (book.name(), book.artist())
if str(book.description()).startswith(self.description_prefix): if str(book.description()).startswith(self.description_prefix):
if book.location() == appscript.k.missing_value: if book.location() == appscript.k.missing_value:
library_orphans[path] = book
if DEBUG: if DEBUG:
self.log.info(" found calibre orphan '%s' in Library|Books" % book.name()) self.log.info(" found calibre orphan '%s' in Library|Books" % book.name())
#book.delete()
#continue
path = self.path_template % (book.name(), book.artist())
library_books[path] = book library_books[path] = book
if DEBUG: if DEBUG:
self.log.info(" adding %-30.30s [%s]" % (book.name(), book.kind())) self.log.info(" adding %-30.30s [%s]" % (book.name(), book.kind()))
@ -1729,9 +1730,9 @@ class ITUNES(DevicePlugin):
elif iswindows: elif iswindows:
lib = None lib = None
try: # try:
pythoncom.CoInitialize() # pythoncom.CoInitialize()
self.iTunes = win32com.client.Dispatch("iTunes.Application") # self.iTunes = win32com.client.Dispatch("iTunes.Application")
for source in self.iTunes.sources: for source in self.iTunes.sources:
if source.Kind == self.Sources.index('Library'): if source.Kind == self.Sources.index('Library'):
lib = source lib = source
@ -1764,24 +1765,24 @@ class ITUNES(DevicePlugin):
if DEBUG: if DEBUG:
self.log.info(" ignoring %-30.30s of type '%s'" % (book.Name, book.KindAsString)) self.log.info(" ignoring %-30.30s of type '%s'" % (book.Name, book.KindAsString))
else: else:
# Remove calibre orphans path = self.path_template % (book.Name, book.Artist)
# Collect calibre orphans
if book.Description.startswith(self.description_prefix): if book.Description.startswith(self.description_prefix):
if not book.Location: if not book.Location:
library_orphans[path] = book
if DEBUG: if DEBUG:
self.log.info(" found calibre orphan '%s' in Library|Books" % book.Name) self.log.info(" found calibre orphan '%s' in Library|Books" % book.Name)
#book.Delete()
#continue
path = self.path_template % (book.Name, book.Artist)
library_books[path] = book library_books[path] = book
if DEBUG: if DEBUG:
self.log.info(" adding %-30.30s [%s]" % (book.Name, book.KindAsString)) self.log.info(" adding %-30.30s [%s]" % (book.Name, book.KindAsString))
except: except:
if DEBUG: if DEBUG:
self.log.info(" no books in library") self.log.info(" no books in library")
finally: # finally:
pythoncom.CoUninitialize() # pythoncom.CoUninitialize()
self.library_orphans = library_orphans
return library_books return library_books
def _get_purchased_book_ids(self): def _get_purchased_book_ids(self):
@ -1904,6 +1905,45 @@ class ITUNES(DevicePlugin):
self.version[0],self.version[1],self.version[2])) self.version[0],self.version[1],self.version[2]))
self.log.info(" iTunes_media: %s" % self.iTunes_media) self.log.info(" iTunes_media: %s" % self.iTunes_media)
def _purge_orphans(self,cached_books):
'''
Scan self.library_orphans for any paths not on device
Remove any true orphans from iTunes
This occurs when recipes are uploaded in a previous session
and the book has since been deleted on the device
'''
if DEBUG:
self.log.info(" ITUNES._purge_orphans")
#self.log.info(" cached_books:\n %s" % "\n ".join(cached_books.keys()))
orphan_paths = {}
if isosx:
for orphan in self.library_orphans:
path = self.path_template % (self.library_orphans[orphan].name(),
self.library_orphans[orphan].artist())
orphan_paths[path] = self.library_orphans[orphan]
# Scan orphan_paths for paths not found in cached_books
for orphan in orphan_paths.keys():
if orphan not in cached_books:
if DEBUG:
self.log.info(" '%s' not found on device, removing from iTunes" % orphan)
self.iTunes.delete(orphan_paths[orphan])
elif iswindows:
for orphan in self.library_orphans:
path = self.path_template % (self.library_orphans[orphan].Name,
self.library_orphans[orphan].Artist)
orphan_paths[path] = self.library_orphans[orphan]
# Scan orphan_paths for paths not found in cached_books
for orphan in orphan_paths.keys():
if orphan not in cached_books:
if DEBUG:
self.log.info(" '%s' not found on device, removing from iTunes" % orphan)
orphan_paths[orphan].Delete()
def _remove_existing_copies(self,path,file,metadata): def _remove_existing_copies(self,path,file,metadata):
''' '''
''' '''
@ -1945,7 +1985,7 @@ class ITUNES(DevicePlugin):
if isosx: if isosx:
if DEBUG: if DEBUG:
self.log.info(" deleting %s" % cached_book['dev_book']) self.log.info(" deleting %s" % cached_book['dev_book'])
result = cached_book['dev_book'].delete() cached_book['dev_book'].delete()
elif iswindows: elif iswindows:
dev_pl = self._get_device_books_playlist() dev_pl = self._get_device_books_playlist()
@ -1957,7 +1997,7 @@ class ITUNES(DevicePlugin):
if hit.Name == cached_book['title']: if hit.Name == cached_book['title']:
if DEBUG: if DEBUG:
self.log.info(" deleting '%s' by %s" % (hit.Name, hit.Artist)) self.log.info(" deleting '%s' by %s" % (hit.Name, hit.Artist))
results = hit.Delete() hit.Delete()
break break
def _remove_from_iTunes(self, cached_book): def _remove_from_iTunes(self, cached_book):

View File

@ -36,4 +36,29 @@ class ESLICK(USBMS):
SUPPORTS_SUB_DIRS = True SUPPORTS_SUB_DIRS = True
@classmethod
def can_handle(cls, dev, debug=False):
return (dev[3], dev[4]) != ('philips', 'Philips d')
class EBK52(ESLICK):
name = 'EBK-52 Device Interface'
gui_name = 'Sigmatek EBK'
description = _('Communicate with the Sigmatek eBook reader.')
FORMATS = ['epub', 'fb2', 'pdf', 'txt']
VENDOR_NAME = ''
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = 'EBOOK_READER'
MAIN_MEMORY_VOLUME_LABEL = 'Sigmatek Main Memory'
STORAGE_CARD_VOLUME_LABEL = 'Sigmatek Storage Card'
@classmethod
def can_handle(cls, dev, debug=False):
return (dev[3], dev[4]) == ('philips', 'Philips d')

View File

@ -12,7 +12,7 @@ from uuid import uuid4
from lxml import etree from lxml import etree
from calibre import prints, guess_type, iswindows from calibre import prints, guess_type
from calibre.devices.errors import DeviceError from calibre.devices.errors import DeviceError
from calibre.devices.usbms.driver import debug_print from calibre.devices.usbms.driver import debug_print
from calibre.constants import DEBUG from calibre.constants import DEBUG
@ -47,7 +47,7 @@ def strptime(src):
src[2] = str(MONTH_MAP[src[2]]) src[2] = str(MONTH_MAP[src[2]])
return time.strptime(' '.join(src), '%w, %d %m %Y %H:%M:%S %Z') return time.strptime(' '.join(src), '%w, %d %m %Y %H:%M:%S %Z')
def strftime(epoch, zone=time.gmtime): def strftime(epoch, zone=time.localtime):
src = time.strftime("%w, %d %m %Y %H:%M:%S GMT", zone(epoch)).split() src = time.strftime("%w, %d %m %Y %H:%M:%S GMT", zone(epoch)).split()
src[0] = INVERSE_DAY_MAP[int(src[0][:-1])]+',' src[0] = INVERSE_DAY_MAP[int(src[0][:-1])]+','
src[2] = INVERSE_MONTH_MAP[int(src[2])] src[2] = INVERSE_MONTH_MAP[int(src[2])]
@ -424,9 +424,6 @@ class XMLCache(object):
def update_text_record(self, record, book, path, bl_index): def update_text_record(self, record, book, path, bl_index):
timestamp = os.path.getmtime(path) timestamp = os.path.getmtime(path)
# Correct for MS DST time 'adjustment'
if iswindows and time.daylight:
timestamp -= time.altzone - time.timezone
date = strftime(timestamp) date = strftime(timestamp)
if date != record.get('date', None): if date != record.get('date', None):
record.set('date', date) record.set('date', date)

View File

@ -687,7 +687,7 @@ class DeviceMixin(object):
self.emailer.send_mails(jobnames, self.emailer.send_mails(jobnames,
Dispatcher(partial(self.emails_sent, remove=remove)), Dispatcher(partial(self.emails_sent, remove=remove)),
attachments, to_s, subjects, texts, attachment_names) attachments, to_s, subjects, texts, attachment_names)
self.status_bar.showMessage(_('Sending email to')+' '+to, 3000) self.status_bar.show_message(_('Sending email to')+' '+to, 3000)
auto = [] auto = []
if _auto_ids != []: if _auto_ids != []:
@ -748,7 +748,7 @@ class DeviceMixin(object):
'%s'%errors, show=True '%s'%errors, show=True
) )
else: else:
self.status_bar.showMessage(_('Sent by email:') + ', '.join(good), self.status_bar.show_message(_('Sent by email:') + ', '.join(good),
5000) 5000)
def cover_to_thumbnail(self, data): def cover_to_thumbnail(self, data):
@ -787,7 +787,7 @@ class DeviceMixin(object):
attachments, to_s, subjects, texts, attachment_names) attachments, to_s, subjects, texts, attachment_names)
sent_mails.append(to_s[0]) sent_mails.append(to_s[0])
if sent_mails: if sent_mails:
self.status_bar.showMessage(_('Sent news to')+' '+\ self.status_bar.show_message(_('Sent news to')+' '+\
', '.join(sent_mails), 3000) ', '.join(sent_mails), 3000)
def sync_catalogs(self, send_ids=None, do_auto_convert=True): def sync_catalogs(self, send_ids=None, do_auto_convert=True):
@ -846,7 +846,7 @@ class DeviceMixin(object):
self.upload_books(files, names, metadata, self.upload_books(files, names, metadata,
on_card=on_card, on_card=on_card,
memory=[files, remove]) memory=[files, remove])
self.status_bar.showMessage(_('Sending catalogs to device.'), 5000) self.status_bar.show_message(_('Sending catalogs to device.'), 5000)
@ -909,7 +909,7 @@ class DeviceMixin(object):
self.upload_books(files, names, metadata, self.upload_books(files, names, metadata,
on_card=on_card, on_card=on_card,
memory=[files, remove]) memory=[files, remove])
self.status_bar.showMessage(_('Sending news to device.'), 5000) self.status_bar.show_message(_('Sending news to device.'), 5000)
def sync_to_device(self, on_card, delete_from_library, def sync_to_device(self, on_card, delete_from_library,
@ -963,7 +963,7 @@ class DeviceMixin(object):
names.append('%s_%d%s'%(prefix, id, os.path.splitext(f)[1])) names.append('%s_%d%s'%(prefix, id, os.path.splitext(f)[1]))
remove = remove_ids if delete_from_library else [] remove = remove_ids if delete_from_library else []
self.upload_books(gf, names, good, on_card, memory=(_files, remove)) self.upload_books(gf, names, good, on_card, memory=(_files, remove))
self.status_bar.showMessage(_('Sending books to device.'), 5000) self.status_bar.show_message(_('Sending books to device.'), 5000)
auto = [] auto = []
if _auto_ids != []: if _auto_ids != []:

View File

@ -49,12 +49,12 @@ class BookInfo(QDialog, Ui_BookInfo):
def open_book_path(self, path): def open_book_path(self, path):
if os.sep in unicode(path): if os.sep in unicode(path):
QDesktopServices.openUrl(QUrl('file:'+path)) QDesktopServices.openUrl(QUrl.fromLocalFile(path))
else: else:
format = unicode(path) format = unicode(path)
path = self.view.model().db.format_abspath(self.current_row, format) path = self.view.model().db.format_abspath(self.current_row, format)
if path is not None: if path is not None:
QDesktopServices.openUrl(QUrl('file:'+path)) QDesktopServices.openUrl(QUrl.fromLocalFile(path))
def next(self): def next(self):

View File

@ -132,6 +132,7 @@ class ToolbarMixin(object): # {{{
self.action_open_containing_folder.setShortcut(Qt.Key_O) self.action_open_containing_folder.setShortcut(Qt.Key_O)
self.addAction(self.action_open_containing_folder) self.addAction(self.action_open_containing_folder)
self.action_open_containing_folder.triggered.connect(self.view_folder)
self.action_sync.setShortcut(Qt.Key_D) self.action_sync.setShortcut(Qt.Key_D)
self.action_sync.setEnabled(True) self.action_sync.setEnabled(True)
self.create_device_menu() self.create_device_menu()
@ -231,7 +232,7 @@ class LibraryViewMixin(object): # {{{
('connect_to_search_box', (self.search, ('connect_to_search_box', (self.search,
self.search_done)), self.search_done)),
('connect_to_book_display', ('connect_to_book_display',
(self.status_bar.book_info.show_data,)), (self.book_details.show_data,)),
]: ]:
for view in (self.library_view, self.memory_view, self.card_a_view, self.card_b_view): for view in (self.library_view, self.memory_view, self.card_a_view, self.card_b_view):
getattr(view, func)(*args) getattr(view, func)(*args)
@ -360,7 +361,7 @@ class LayoutMixin(object): # {{{
if config['gui_layout'] == 'narrow': if config['gui_layout'] == 'narrow':
from calibre.gui2.status import StatusBar from calibre.gui2.status import StatusBar
self.status_bar = StatusBar(self) self.status_bar = self.book_details = StatusBar(self)
self.stack = Stack(self) self.stack = Stack(self)
self.bd_splitter = Splitter('book_details_splitter', self.bd_splitter = Splitter('book_details_splitter',
_('Book Details'), I('book.svg'), _('Book Details'), I('book.svg'),

View File

@ -292,7 +292,8 @@ class BooksView(QTableView): # {{{
old_state['column_positions'][name] = i old_state['column_positions'][name] = i
if name != 'ondevice': if name != 'ondevice':
old_state['column_sizes'][name] = \ old_state['column_sizes'][name] = \
max(self.sizeHintForColumn(i), h.sectionSizeHint(i)) min(350, max(self.sizeHintForColumn(i),
h.sectionSizeHint(i)))
if name == 'timestamp': if name == 'timestamp':
old_state['column_sizes'][name] += 12 old_state['column_sizes'][name] += 12
return old_state return old_state

View File

@ -53,7 +53,24 @@ def init_qt(args):
app.setWindowIcon(QIcon(I('library.png'))) app.setWindowIcon(QIcon(I('library.png')))
return app, opts, args, actions return app, opts, args, actions
def get_library_path():
def get_default_library_path():
fname = _('Calibre Library')
if isinstance(fname, unicode):
try:
fname = fname.encode(filesystem_encoding)
except:
fname = 'Calibre Library'
x = os.path.expanduser('~'+os.sep+fname)
if not os.path.exists(x):
try:
os.makedirs(x)
except:
x = os.path.expanduser('~')
return x
def get_library_path(parent=None):
library_path = prefs['library_path'] library_path = prefs['library_path']
if library_path is None: # Need to migrate to new database layout if library_path is None: # Need to migrate to new database layout
base = os.path.expanduser('~') base = os.path.expanduser('~')
@ -73,10 +90,12 @@ def get_library_path():
try: try:
os.makedirs(library_path) os.makedirs(library_path)
except: except:
error_dialog(None, _('Failed to create library'), error_dialog(parent, _('Failed to create library'),
_('Failed to create calibre library at: %r. Aborting.')%library_path, _('Failed to create calibre library at: %r.')%library_path,
det_msg=traceback.format_exc(), show=True) det_msg=traceback.format_exc(), show=True)
library_path = None library_path = choose_dir(parent, 'choose calibre library',
_('Choose a location for your new calibre e-book library'),
default_dir=get_default_library_path())
return library_path return library_path
class DBRepair(QThread): class DBRepair(QThread):
@ -159,22 +178,9 @@ class GuiRunner(QObject):
'a new empty library.'), 'a new empty library.'),
det_msg=tb, show=True) det_msg=tb, show=True)
if db is None: if db is None:
fname = _('Calibre Library')
if isinstance(fname, unicode):
try:
fname = fname.encode(filesystem_encoding)
except:
fname = 'Calibre Library'
x = os.path.expanduser('~'+os.sep+fname)
if not os.path.exists(x):
try:
os.makedirs(x)
except:
x = os.path.expanduser('~')
candidate = choose_dir(self.splash_screen, 'choose calibre library', candidate = choose_dir(self.splash_screen, 'choose calibre library',
_('Choose a location for your new calibre e-book library'), _('Choose a location for your new calibre e-book library'),
default_dir=x) default_dir=get_default_library_path())
if not candidate: if not candidate:
self.initialization_failed() self.initialization_failed()
@ -236,8 +242,8 @@ class GuiRunner(QObject):
if gprefs.get('show_splash_screen', True): if gprefs.get('show_splash_screen', True):
self.show_splash_screen() self.show_splash_screen()
self.library_path = get_library_path() self.library_path = get_library_path(parent=self.splash_screen)
if self.library_path is None: if not self.library_path:
self.initialization_failed() self.initialization_failed()
self.initialize_db() self.initialize_db()

View File

@ -7,8 +7,8 @@ __copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
from PyQt4.Qt import QComboBox, Qt, QLineEdit, QStringList, pyqtSlot, \ from PyQt4.Qt import QComboBox, Qt, QLineEdit, QStringList, pyqtSlot, \
pyqtSignal, SIGNAL, QObject, QDialog pyqtSignal, SIGNAL, QObject, QDialog, QCompleter, \
from PyQt4.QtGui import QCompleter QAction, QKeySequence
from calibre.gui2 import config from calibre.gui2 import config
from calibre.gui2.dialogs.confirm_delete import confirm from calibre.gui2.dialogs.confirm_delete import confirm
@ -348,8 +348,14 @@ class SearchBoxMixin(object):
self.do_advanced_search) self.do_advanced_search)
self.search.clear() self.search.clear()
self.search.setFocus(Qt.OtherFocusReason)
self.search.setMaximumWidth(self.width()-150) self.search.setMaximumWidth(self.width()-150)
self.action_focus_search = QAction(self)
shortcuts = QKeySequence.keyBindings(QKeySequence.Find)
shortcuts = list(shortcuts) + [QKeySequence('/')]
self.action_focus_search.setShortcuts(shortcuts)
self.action_focus_search.triggered.connect(lambda x:
self.search.setFocus(Qt.OtherFocusReason))
self.addAction(self.action_focus_search)
def search_box_cleared(self): def search_box_cleared(self):
self.tags_view.clear() self.tags_view.clear()

View File

@ -61,7 +61,7 @@ class BookInfoDisplay(QWidget):
pixmap = self.pixmap() pixmap = self.pixmap()
pwidth, pheight = pixmap.width(), pixmap.height() pwidth, pheight = pixmap.width(), pixmap.height()
width, height = fit_image(pwidth, pheight, width, height = fit_image(pwidth, pheight,
pwidth, self.statusbar_height-12)[1:] pwidth, self.statusbar_height-20)[1:]
self.setMaximumHeight(height) self.setMaximumHeight(height)
try: try:
aspect_ratio = pwidth/float(pheight) aspect_ratio = pwidth/float(pheight)
@ -162,17 +162,48 @@ class BookInfoDisplay(QWidget):
self.updateGeometry() self.updateGeometry()
self.setVisible(True) self.setVisible(True)
class StatusBarInterface(object):
class StatusBar(QStatusBar):
resized = pyqtSignal(object)
files_dropped = pyqtSignal(object, object)
show_book_info = pyqtSignal()
def initialize(self, systray=None): def initialize(self, systray=None):
self.systray = systray self.systray = systray
self.notifier = get_notifier(systray) self.notifier = get_notifier(systray)
self.book_info = BookInfoDisplay(self.clearMessage)
def show_message(self, msg, timeout=0):
QStatusBar.showMessage(self, msg, timeout)
if self.notifier is not None and not config['disable_tray_notification']:
if isosx and isinstance(msg, unicode):
try:
msg = msg.encode(preferred_encoding)
except UnicodeEncodeError:
msg = msg.encode('utf-8')
self.notifier(msg)
def clear_message(self):
QStatusBar.clearMessage(self)
class BookDetailsInterface(object):
# These signals must be defined in the class implementing this interface
files_dropped = None
show_book_info = None
def reset_info(self):
raise NotImplementedError()
def show_data(self, data):
raise NotImplementedError()
class StatusBar(QStatusBar, StatusBarInterface, BookDetailsInterface):
files_dropped = pyqtSignal(object, object)
show_book_info = pyqtSignal()
resized = pyqtSignal(object)
def initialize(self, systray=None):
StatusBarInterface.initialize(self, systray=systray)
self.book_info = BookInfoDisplay(self.clear_message)
self.book_info.setAcceptDrops(True) self.book_info.setAcceptDrops(True)
self.scroll_area = QScrollArea() self.scroll_area = QScrollArea()
self.scroll_area.setWidget(self.book_info) self.scroll_area.setWidget(self.book_info)
@ -192,15 +223,6 @@ class StatusBar(QStatusBar):
def reset_info(self): def reset_info(self):
self.book_info.show_data({}) self.book_info.show_data({})
def showMessage(self, msg, timeout=0): def show_data(self, data):
ret = QStatusBar.showMessage(self, msg, timeout) self.book_info.show_data(data)
if self.notifier is not None and not config['disable_tray_notification']:
if isosx and isinstance(msg, unicode):
try:
msg = msg.encode(preferred_encoding)
except UnicodeEncodeError:
msg = msg.encode('utf-8')
self.notifier(msg)
return ret

View File

@ -223,7 +223,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin,
####################### Setup device detection ######################## ####################### Setup device detection ########################
self.device_manager = DeviceManager(Dispatcher(self.device_detected), self.device_manager = DeviceManager(Dispatcher(self.device_detected),
self.job_manager, Dispatcher(self.status_bar.showMessage)) self.job_manager, Dispatcher(self.status_bar.show_message))
self.device_manager.start() self.device_manager.start()
@ -256,8 +256,8 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin,
####################### Status Bar ##################### ####################### Status Bar #####################
self.status_bar.initialize(self.system_tray_icon) self.status_bar.initialize(self.system_tray_icon)
self.status_bar.show_book_info.connect(self.show_book_info) self.book_details.show_book_info.connect(self.show_book_info)
self.status_bar.files_dropped.connect(self.files_dropped_on_book) self.book_details.files_dropped.connect(self.files_dropped_on_book)
####################### Setup Toolbar ##################### ####################### Setup Toolbar #####################
ToolbarMixin.__init__(self) ToolbarMixin.__init__(self)
@ -482,7 +482,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin,
Dispatcher(self.info_read)) Dispatcher(self.info_read))
self.set_default_thumbnail(\ self.set_default_thumbnail(\
self.device_manager.device.THUMBNAIL_HEIGHT) self.device_manager.device.THUMBNAIL_HEIGHT)
self.status_bar.showMessage(_('Device: ')+\ self.status_bar.show_message(_('Device: ')+\
self.device_manager.device.__class__.get_gui_name()+\ self.device_manager.device.__class__.get_gui_name()+\
_(' detected.'), 3000) _(' detected.'), 3000)
self.device_connected = 'device' if not is_folder_device else 'folder' self.device_connected = 'device' if not is_folder_device else 'folder'
@ -503,7 +503,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin,
dict(version=self.latest_version, device=' ')) dict(version=self.latest_version, device=' '))
self.device_info = ' ' self.device_info = ' '
if self.current_view() != self.library_view: if self.current_view() != self.library_view:
self.status_bar.reset_info() self.book_details.reset_info()
self.location_view.setCurrentIndex(self.location_view.model().index(0)) self.location_view.setCurrentIndex(self.location_view.model().index(0))
self.eject_action.setEnabled(False) self.eject_action.setEnabled(False)
self.refresh_ondevice_info (device_connected = False) self.refresh_ondevice_info (device_connected = False)
@ -861,7 +861,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin,
to_device = allow_device and self.stack.currentIndex() != 0 to_device = allow_device and self.stack.currentIndex() != 0
self._add_books(books, to_device) self._add_books(books, to_device)
if to_device: if to_device:
self.status_bar.showMessage(\ self.status_bar.show_message(\
_('Uploading books to device.'), 2000) _('Uploading books to device.'), 2000)
@ -912,7 +912,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin,
self.upload_books(paths, self.upload_books(paths,
list(map(ascii_filename, names)), list(map(ascii_filename, names)),
infos, on_card=on_card) infos, on_card=on_card)
self.status_bar.showMessage( self.status_bar.show_message(
_('Uploading books to device.'), 2000) _('Uploading books to device.'), 2000)
if getattr(self._adder, 'number_of_books_added', 0) > 0: if getattr(self._adder, 'number_of_books_added', 0) > 0:
self.library_view.model().books_added(self._adder.number_of_books_added) self.library_view.model().books_added(self._adder.number_of_books_added)
@ -1058,7 +1058,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin,
job = self.remove_paths(paths) job = self.remove_paths(paths)
self.delete_memory[job] = (paths, view.model()) self.delete_memory[job] = (paths, view.model())
view.model().mark_for_deletion(job, rows) view.model().mark_for_deletion(job, rows)
self.status_bar.showMessage(_('Deleting books from device.'), 1000) self.status_bar.show_message(_('Deleting books from device.'), 1000)
def remove_paths(self, paths): def remove_paths(self, paths):
return self.device_manager.delete_books(\ return self.device_manager.delete_books(\
@ -1424,7 +1424,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin,
job.catalog_file_path = out job.catalog_file_path = out
job.fmt = fmt job.fmt = fmt
job.catalog_sync, job.catalog_title = sync, title job.catalog_sync, job.catalog_title = sync, title
self.status_bar.showMessage(_('Generating %s catalog...')%fmt) self.status_bar.show_message(_('Generating %s catalog...')%fmt)
def catalog_generated(self, job): def catalog_generated(self, job):
if job.result: if job.result:
@ -1440,7 +1440,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin,
sync = dynamic.get('catalogs_to_be_synced', set([])) sync = dynamic.get('catalogs_to_be_synced', set([]))
sync.add(id) sync.add(id)
dynamic.set('catalogs_to_be_synced', sync) dynamic.set('catalogs_to_be_synced', sync)
self.status_bar.showMessage(_('Catalog generated.'), 3000) self.status_bar.show_message(_('Catalog generated.'), 3000)
self.sync_catalogs() self.sync_catalogs()
if job.fmt not in ['EPUB','MOBI']: if job.fmt not in ['EPUB','MOBI']:
export_dir = choose_dir(self, _('Export Catalog Directory'), export_dir = choose_dir(self, _('Export Catalog Directory'),
@ -1458,7 +1458,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin,
Dispatcher(self.scheduled_recipe_fetched), func, args=args, Dispatcher(self.scheduled_recipe_fetched), func, args=args,
description=desc) description=desc)
self.conversion_jobs[job] = (temp_files, fmt, arg) self.conversion_jobs[job] = (temp_files, fmt, arg)
self.status_bar.showMessage(_('Fetching news from ')+arg['title'], 2000) self.status_bar.show_message(_('Fetching news from ')+arg['title'], 2000)
def scheduled_recipe_fetched(self, job): def scheduled_recipe_fetched(self, job):
temp_files, fmt, arg = self.conversion_jobs.pop(job) temp_files, fmt, arg = self.conversion_jobs.pop(job)
@ -1472,7 +1472,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin,
sync.add(id) sync.add(id)
dynamic.set('news_to_be_synced', sync) dynamic.set('news_to_be_synced', sync)
self.scheduler.recipe_downloaded(arg) self.scheduler.recipe_downloaded(arg)
self.status_bar.showMessage(arg['title'] + _(' fetched.'), 3000) self.status_bar.show_message(arg['title'] + _(' fetched.'), 3000)
self.email_news(id) self.email_news(id)
self.sync_news() self.sync_news()
@ -1552,7 +1552,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin,
num = len(jobs) num = len(jobs)
if num > 0: if num > 0:
self.status_bar.showMessage(_('Starting conversion of %d book(s)') % self.status_bar.show_message(_('Starting conversion of %d book(s)') %
num, 2000) num, 2000)
def queue_convert_jobs(self, jobs, changed, bad, rows, previous, def queue_convert_jobs(self, jobs, changed, bad, rows, previous,
@ -1599,7 +1599,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin,
self.library_view.model().db.add_format(book_id, \ self.library_view.model().db.add_format(book_id, \
fmt, data, index_is_id=True) fmt, data, index_is_id=True)
data.close() data.close()
self.status_bar.showMessage(job.description + \ self.status_bar.show_message(job.description + \
(' completed'), 2000) (' completed'), 2000)
finally: finally:
for f in temp_files: for f in temp_files:
@ -1802,9 +1802,9 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin,
self.library_view.set_database(db) self.library_view.set_database(db)
self.tags_view.set_database(db, self.tag_match, self.popularity) self.tags_view.set_database(db, self.tag_match, self.popularity)
self.library_view.model().set_book_on_device_func(self.book_on_device) self.library_view.model().set_book_on_device_func(self.book_on_device)
self.status_bar.clearMessage() self.status_bar.clear_message()
self.search.clear_to_help() self.search.clear_to_help()
self.status_bar.reset_info() self.book_details.reset_info()
self.library_view.model().count_changed() self.library_view.model().count_changed()
prefs['library_path'] = self.library_path prefs['library_path'] = self.library_path
@ -1831,7 +1831,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin,
''' '''
page = 0 if location == 'library' else 1 if location == 'main' else 2 if location == 'carda' else 3 page = 0 if location == 'library' else 1 if location == 'main' else 2 if location == 'carda' else 3
self.stack.setCurrentIndex(page) self.stack.setCurrentIndex(page)
self.status_bar.reset_info() self.book_details.reset_info()
for x in ('tb', 'cb'): for x in ('tb', 'cb'):
splitter = getattr(self, x+'_splitter') splitter = getattr(self, x+'_splitter')
splitter.button.setEnabled(location == 'library') splitter.button.setEnabled(location == 'library')

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -31,7 +31,7 @@ class SafeLocalTimeZone(tzlocal):
def compute_locale_info_for_parse_date(): def compute_locale_info_for_parse_date():
try: try:
dt = datetime.strptime('1/5/2000', "%x") dt = datetime.strptime('1/5/2000', "%x")
except ValueError: except:
try: try:
dt = datetime.strptime('1/5/01', '%x') dt = datetime.strptime('1/5/01', '%x')
except: except: