sync with Kovid's branch

This commit is contained in:
Tomasz Długosz 2012-10-27 23:36:58 +02:00
commit 9fa0715276
14 changed files with 204 additions and 141 deletions

View File

@ -19,6 +19,54 @@
# new recipes:
# - title:
- version: 0.9.4
date: 2012-10-26
new features:
- title: "Conversion: Add an option to embed a font family into the book."
description: "The embedded font is used as the base font for all text that does not specify its own font family in the input document. Works only with output formats that support font embedding, principally EPUB/AZW3. Option is found under Look & Feel in the conversion dialog. You can ensure that the font is used for all text, regardless of the input document's styles by filtering out font family styles via the Filter Style Information option in the Conversion dialog."
type: major
- title: "When changing the title/author of a book, use hard links instead of copying the books' files, for a large speedup. Only works on filesystems that support hardlinks."
- title: "Linux installer: Resume interrupted downloads and verify the SHA-512 signature of the downloaded file before installing it."
bug fixes:
- title: "Windows: Check if any of the files of a book are in use before changing the title/author, this prevents the creation of duplicate files if one of the files is open in another program"
- title: "Kobo driver: Fix the ondevice status for some books getting lost."
tickets: [1069403]
- title: "Catalogs: Fix regression that broke use of prefix rules."
tickets: [1070086]
- title: "Tag Browser: Fix sorting incorrect for accented letters"
tickets: [1069835]
- title: "Make the bundled Liberation fonts available on all platforms for embedding"
- title: "Use mimetype for fonts from the EPUB 3 specification"
- title: "Get Books: Handle website change that broke the SONY Store plugin"
- title: "Generate cover: If the default font cannot render characters in the metadata (for example for east asian languages) try to automatically find a font on the system that is capable of rendering the characters"
- title: "Fix regression that broke certain types of CSS selectors."
tickets: [1068937]
- title: "Use font-weight:bold instead of font-weight:bolder for the <b> and <strong> tags as ADE cant handle bolder when embedded fonts are used"
improved recipes:
- New York Post
- PC World
- TIME Magazine
- Associated Press
new recipes:
- title: Yazihane
author: A Erdogan
- version: 0.9.3
date: 2012-10-19

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

BIN
manual/images/sg_pref.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

View File

@ -65,7 +65,7 @@ You create the custom column in the usual way, using Preferences -> Add your own
Then after restarting |app|, you must tell |app| that the column is to be treated as a hierarchy. Go to Preferences -> Look and Feel -> Tag Browser and enter the lookup name "#genre" into the "Categories with hierarchical items" box. Press Apply, and you are done with setting up.
.. image:: images/sg_pref.jpg
.. image:: images/sg_pref.png
:align: center
At the point there are no genres in the column. We are left with the last step: how to apply a genre to a book. A genre does not exist in |app| until it appears on at least one book. To learn how to apply a genre for the first time, we must go into some detail about what a genre looks like in the metadata for a book.

View File

@ -38,8 +38,10 @@ class TheAtlantic(BasicNewsRecipe):
self.timefmt = ' [%s]'%ds
cover = soup.find('img', src=True, attrs={'class':'cover'})
if cover is not None:
self.cover_url = cover['src'].replace(' ', '%20')
self.cover_url = re.sub('\s','%20',re.sub('jpg.*','jpg',cover['src']))
self.log(self.cover_url)
feeds = []
seen_titles = set([])
@ -47,18 +49,16 @@ class TheAtlantic(BasicNewsRecipe):
section_title = self.tag_to_string(section.find('h2'))
self.log('Found section:', section_title)
articles = []
for post in section.findAll('div', attrs={'class':lambda x : x and
'post' in x}):
h = post.find(['h3', 'h4'])
title = self.tag_to_string(h)
for post in section.findAll('h3', attrs={'class':'headline'}):
a = post.find('a', href=True)
title = self.tag_to_string(a)
if title in seen_titles:
continue
seen_titles.add(title)
a = post.find('a', href=True)
url = a['href']
if url.startswith('/'):
url = 'http://www.theatlantic.com'+url
p = post.find('p', attrs={'class':'dek'})
p = post.parent.find('p', attrs={'class':'dek'})
desc = None
self.log('\tFound article:', title, 'at', url)
if p is not None:
@ -69,19 +69,29 @@ class TheAtlantic(BasicNewsRecipe):
if articles:
feeds.append((section_title, articles))
poems = []
self.log('Found section: Poems')
pd = soup.find('h2', text='Poetry').parent.parent
for poem in pd.findAll('h4'):
title = self.tag_to_string(poem)
url = poem.find('a')['href']
rightContent=soup.find('div', attrs = {'class':'rightContent'})
for module in rightContent.findAll('div', attrs={'class':'module'}):
section_title = self.tag_to_string(module.find('h2'))
articles = []
for post in module.findAll('div', attrs={'class':'post'}):
a = post.find('a', href=True)
title = self.tag_to_string(a)
if title in seen_titles:
continue
seen_titles.add(title)
url = a['href']
if url.startswith('/'):
url = 'http://www.theatlantic.com'+url
p = post.parent.find('p', attrs={'class':'dek'})
desc = None
self.log('\tFound article:', title, 'at', url)
poems.append({'title':title, 'url':url, 'description':'',
'date':''})
if poems:
feeds.append(('Poems', poems))
if p is not None:
desc = self.tag_to_string(p)
self.log('\t\t', desc)
articles.append({'title':title, 'url':url, 'description':desc, 'date':''})
if articles:
feeds.append((section_title, articles))
return feeds
@ -100,4 +110,3 @@ class TheAtlantic(BasicNewsRecipe):
table.replaceWith(div)
return soup

View File

@ -1,5 +1,5 @@
__license__ = 'GPL v3'
__copyright__ = '2010-2012, Darko Miletic <darko.miletic at gmail.com>'
__copyright__ = '2010-2011, Darko Miletic <darko.miletic at gmail.com>'
'''
www.ft.com/uk-edition
'''
@ -42,8 +42,7 @@ class FinancialTimes(BasicNewsRecipe):
def get_browser(self):
br = BasicNewsRecipe.get_browser()
br.open(self.INDEX)
if self.username is not None and self.password is not None:
br.open(self.LOGIN2)
br.open(self.LOGIN)
br.select_form(name='loginForm')
br['username'] = self.username
br['password'] = self.password
@ -55,11 +54,6 @@ class FinancialTimes(BasicNewsRecipe):
,dict(name='div', attrs={'class':'standfirst'})
,dict(name='div', attrs={'id' :'storyContent'})
,dict(name='div', attrs={'class':['ft-story-body','index-detail']})
,dict(name='div' , attrs={'class':['ft-story-body','index-detail']})
,dict(name='h2' , attrs={'class':'entry-title'} )
,dict(name='span', attrs={'class':lambda x: x and 'posted-on' in x.split()} )
,dict(name='span', attrs={'class':'author_byline'} )
,dict(name='div' , attrs={'class':'entry-content'} )
]
remove_tags = [
dict(name='div', attrs={'id':'floating-con'})
@ -88,10 +82,14 @@ class FinancialTimes(BasicNewsRecipe):
if self.test and count > 2:
return articles
rawlink = item['href']
if rawlink.startswith('http://'):
url = rawlink
if not rawlink.startswith('http://'):
else:
url = self.PREFIX + rawlink
try:
urlverified = self.browser.open_novisit(url).geturl() # resolve redirect.
except:
continue
title = self.tag_to_string(item)
date = strftime(self.timefmt)
articles.append({
@ -110,20 +108,21 @@ class FinancialTimes(BasicNewsRecipe):
wide = soup.find('div',attrs={'class':'wide'})
if not wide:
return feeds
allsections = wide.findAll(attrs={'class':lambda x: x and 'footwell' in x.split()})
if not allsections:
strest = wide.findAll('h3', attrs={'class':'section'})
if not strest:
return feeds
st = wide.findAll('h4',attrs={'class':'section-no-arrow'})
if st:
st.extend(strest)
count = 0
for item in allsections:
for item in st:
count = count + 1
if self.test and count > 2:
return feeds
fitem = item.h3
if not fitem:
fitem = item.h4
ftitle = self.tag_to_string(fitem)
ftitle = self.tag_to_string(item)
self.report_progress(0, _('Fetching feed')+' %s...'%(ftitle))
feedarts = self.get_artlinks(item.ul)
if item.parent.ul is not None:
feedarts = self.get_artlinks(item.parent.ul)
feeds.append((ftitle,feedarts))
return feeds
@ -170,8 +169,10 @@ class FinancialTimes(BasicNewsRecipe):
except:
print "Retrying download..."
count += 1
tfile = PersistentTemporaryFile('_fa.html')
tfile.write(html)
tfile.close()
self.temp_files.append(tfile)
return tfile.name
self.temp_files.append(PersistentTemporaryFile('_fa.html'))
self.temp_files[-1].write(html)
self.temp_files[-1].close()
return self.temp_files[-1].name
def cleanup(self):
self.browser.open('https://registration.ft.com/registration/login/logout?location=')

View File

@ -1,5 +1,4 @@
from calibre.web.feeds.news import BasicNewsRecipe
from calibre.ebooks.BeautifulSoup import BeautifulSoup
class NewYorkTimesBookReview(BasicNewsRecipe):
title = u'New York Times Book Review'
@ -7,50 +6,16 @@ class NewYorkTimesBookReview(BasicNewsRecipe):
__author__ = 'Krittika Goyal'
oldest_article = 8 #days
max_articles_per_feed = 1000
recursions = 2
#recursions = 2
#encoding = 'latin1'
use_embedded_content = False
no_stylesheets = True
auto_cleanup = True
remove_stylesheets = True
#remove_tags_before = dict(name='h1', attrs={'class':'heading'})
remove_tags_after = dict(name='div', attrs={'id':'authorId'})
remove_tags = [
dict(name='iframe'),
dict(name=['div', 'a'], attrs={'class':['enlargeThis', 'jumpLink']}),
dict(name='div', attrs={'id':['sidebarArticles', 'toolsRight']}),
#dict(name='ul', attrs={'class':'article-tools'}),
#dict(name='ul', attrs={'class':'articleTools'}),
]
match_regexps = [
r'http://www.nytimes.com/.+pagewanted=[2-9]+'
]
feeds = [
('New York Times Sunday Book Review',
'http://feeds.nytimes.com/nyt/rss/SundayBookReview'),
]
def preprocess_html(self, soup):
story = soup.find(name='div', attrs={'id':'article'})
#td = heading.findParent(name='td')
#td.extract()
soup = BeautifulSoup('<html><head><title>t</title></head><body></body></html>')
body = soup.find(name='body')
body.insert(0, story)
#for x in soup.findAll(name='p', text=lambda x:x and '--&gt;' in x):
#p = x.findParent('p')
#if p is not None:
#p.extract()
return soup
def postprocess_html(self, soup, first):
for div in soup.findAll(id='pageLinks'):
div.extract()
if not first:
h1 = soup.find('h1')
if h1 is not None:
h1.extract()
t = soup.find(attrs={'class':'timestamp'})
if t is not None:
t.extract()
return soup

View File

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

View File

@ -184,6 +184,11 @@ class CSSFlattener(object):
faces = fontconfig.fonts_for_family(family)
if not faces or not u'normal' in faces:
msg = (u'No embeddable fonts found for family: %r'%self.opts.embed_font_family)
if faces:
msg = (u'The selected font %s has no Regular typeface, only'
' %s faces, it cannot be used.')%(
self.opts.embed_font_family,
', '.join(faces.iterkeys()))
if failure_critical:
raise ValueError(msg)
self.oeb.log.warn(msg)

View File

@ -504,7 +504,11 @@ from the value in the box</string>
<item>
<widget class="QCheckBox" name="restore_original">
<property name="toolTip">
<string>When doing a same format to same format conversion, for e.g., EPUB to EPUB, calibre saves the original EPUB as ORIGINAL_EPUB. This option tells calibre to restore the EPUB from ORIGINAL_EPUB. Useful if you did a bulk conversion of a large number of books and something went wrong.</string>
<string>When doing a same format to same format conversion,
for e.g., EPUB to EPUB, calibre saves the original EPUB
as ORIGINAL_EPUB. This option tells calibre to restore
the EPUB from ORIGINAL_EPUB. Useful if you did a bulk
conversion of a large number of books and something went wrong.</string>
</property>
<property name="text">
<string>Restore pre conversion &amp;originals, if available</string>

View File

@ -13,6 +13,7 @@ from PyQt4.Qt import (QFontInfo, QFontMetrics, Qt, QFont, QFontDatabase, QPen,
QToolButton, QGridLayout, QListView, QWidget, QDialogButtonBox, QIcon,
QHBoxLayout, QLabel, QModelIndex)
from calibre.gui2 import error_dialog
from calibre.utils.icu import sort_key
def writing_system_for_font(font):
@ -173,6 +174,20 @@ class FontFamilyDialog(QDialog):
if idx == 0: return None
return self.families[idx]
def accept(self):
ff = self.font_family
if ff:
from calibre.utils.fonts import fontconfig
faces = fontconfig.fonts_for_family(ff) or {}
faces = frozenset(faces.iterkeys())
if 'normal' not in faces:
error_dialog(self, _('Not a useable font'),
_('The %s font family does not have a Regular typeface, so it'
' cannot be used. It has only the "%s" face(s).')%(
ff, ', '.join(faces)), show=True)
return
QDialog.accept(self)
class FontFamilyChooser(QWidget):
family_changed = pyqtSignal(object)

View File

@ -646,12 +646,13 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
spath = os.path.join(self.library_path, *current_path.split('/'))
tpath = os.path.join(self.library_path, *path.split('/'))
wam = WindowsAtomicFolderMove(spath) if iswindows and current_path else None
source_ok = current_path and os.path.exists(spath)
wam = WindowsAtomicFolderMove(spath) if iswindows and source_ok else None
try:
if not os.path.exists(tpath):
os.makedirs(tpath)
if current_path and os.path.exists(spath): # Migrate existing files
if source_ok: # Migrate existing files
self.copy_cover_to(id, os.path.join(tpath, 'cover.jpg'),
index_is_id=True, windows_atomic_move=wam,
use_hardlink=True)
@ -669,7 +670,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
self.conn.commit()
self.data.set(id, self.FIELD_MAP['path'], path, row_is_id=True)
# Delete not needed directories
if current_path and os.path.exists(spath):
if source_ok:
if not samefile(spath, tpath):
if wam is not None:
wam.delete_originals()
@ -1432,7 +1433,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
if use_hardlink:
try:
hardlink_file(path, dest)
return
return True
except:
pass
with lopen(dest, 'wb') as d:

View File

@ -4,9 +4,9 @@
#
msgid ""
msgstr ""
"Project-Id-Version: calibre 0.9.3\n"
"POT-Creation-Date: 2012-10-24 09:36+IST\n"
"PO-Revision-Date: 2012-10-24 09:36+IST\n"
"Project-Id-Version: calibre 0.9.4\n"
"POT-Creation-Date: 2012-10-26 09:57+IST\n"
"PO-Revision-Date: 2012-10-26 09:57+IST\n"
"Last-Translator: Automatically generated\n"
"Language-Team: LANGUAGE\n"
"MIME-Version: 1.0\n"
@ -173,12 +173,12 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/database2.py:586
#: /home/kovid/work/calibre/src/calibre/library/database2.py:594
#: /home/kovid/work/calibre/src/calibre/library/database2.py:605
#: /home/kovid/work/calibre/src/calibre/library/database2.py:2273
#: /home/kovid/work/calibre/src/calibre/library/database2.py:2427
#: /home/kovid/work/calibre/src/calibre/library/database2.py:2857
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3504
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3506
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3643
#: /home/kovid/work/calibre/src/calibre/library/database2.py:2274
#: /home/kovid/work/calibre/src/calibre/library/database2.py:2428
#: /home/kovid/work/calibre/src/calibre/library/database2.py:2858
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3505
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3507
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3644
#: /home/kovid/work/calibre/src/calibre/library/server/content.py:250
#: /home/kovid/work/calibre/src/calibre/library/server/content.py:251
#: /home/kovid/work/calibre/src/calibre/library/server/mobile.py:247
@ -882,26 +882,26 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/book/base.py:666
#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:67
#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:668
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1061
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1062
#: /home/kovid/work/calibre/src/calibre/utils/formatter_functions.py:887
#: /home/kovid/work/calibre/src/calibre/utils/formatter_functions.py:910
msgid "Yes"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/db/fields.py:163
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1216
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1217
msgid "Main"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/db/fields.py:165
#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:77
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1218
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1219
msgid "Card A"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/db/fields.py:167
#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:79
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1220
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1221
msgid "Card B"
msgstr ""
@ -1048,14 +1048,14 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/tag_browser/model.py:1199
#: /home/kovid/work/calibre/src/calibre/library/database2.py:371
#: /home/kovid/work/calibre/src/calibre/library/database2.py:384
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3361
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3362
#: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:187
msgid "News"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:2770
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3317
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3335
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3318
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3336
msgid "Catalog"
msgstr ""
@ -1099,10 +1099,10 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:128
#: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:131
#: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:348
#: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:1307
#: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:1311
#: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:1315
#: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:1635
#: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:1319
#: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:1323
#: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:1642
#: /home/kovid/work/calibre/src/calibre/devices/prst1/driver.py:155
#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:144
#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:147
@ -11001,6 +11001,10 @@ msgstr ""
msgid "Choose &font family"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/font_family_chooser.py:196
msgid "Clear the font family"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/init.py:108
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/look_feel_ui.py:296
msgid "Cover Browser"
@ -12216,7 +12220,7 @@ msgstr ""
msgid ""
"If set, this option will causes calibre to check if a file\n"
" being auto-added is already in the calibre library.\n"
" If it is, a meesage will pop up asking you whether\n"
" If it is, a message will pop up asking you whether\n"
" you want to add it anyway."
msgstr ""
@ -15287,7 +15291,7 @@ msgid "Options to customize the ebook viewer"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/config.py:30
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1106
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1107
msgid "Remember last used window size"
msgstr ""
@ -15854,35 +15858,35 @@ msgstr ""
msgid "Loading ebook..."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:977
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:978
msgid "Could not open ebook"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:978
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:979
msgid "Unknown error"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1093
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1094
msgid "Options to control the ebook viewer"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1100
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1101
msgid "If specified, viewer window will try to come to the front when started."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1103
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1104
msgid "If specified, viewer window will try to open full screen when started."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1108
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1109
msgid "Print javascript alert and console messages to the console"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1110
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1111
msgid "The position at which to open the specified book. The position is a location as displayed in the top left corner of the viewer."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1117
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1118
msgid ""
"%prog [options] file\n"
"\n"
@ -17358,17 +17362,17 @@ msgstr ""
msgid "creating custom column "
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3669
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3670
#, python-format
msgid "<p>Migrating old database to ebook library in %s<br><center>"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3698
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3699
#, python-format
msgid "Copying <b>%s</b>"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3715
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3716
msgid "Compacting database"
msgstr ""

View File

@ -314,6 +314,17 @@ class WindowsAtomicFolderMove(object):
break
f.write(raw)
def release_file(self, path):
key = None
for p, h in self.handle_map.iteritems():
if samefile_windows(path, p):
key = (p, h)
break
if key is not None:
import win32file
win32file.CloseHandle(key[1])
self.handle_map.pop(key[0])
def close_handles(self):
import win32file
for h in self.handle_map.itervalues():