Auto-adding: Do not add incomplete files when files are downloaded directly into the auto add folder. Fixes #926578 (Automatic adding produces duplicate entries for downloaded files)

This commit is contained in:
Kovid Goyal 2012-02-04 23:14:42 +05:30
parent c8c3bbb76f
commit 4297aa3c86
3 changed files with 64 additions and 8 deletions

View File

@ -222,6 +222,11 @@ def forked_read_metadata(path, tdir):
from calibre.ebooks.metadata.opf2 import metadata_to_opf from calibre.ebooks.metadata.opf2 import metadata_to_opf
with open(path, 'rb') as f: with open(path, 'rb') as f:
fmt = os.path.splitext(path)[1][1:].lower() fmt = os.path.splitext(path)[1][1:].lower()
f.seek(0, 2)
sz = f.tell()
with open(os.path.join(tdir, 'size.txt'), 'wb') as s:
s.write(str(sz).encode('ascii'))
f.seek(0)
mi = get_metadata(f, fmt) mi = get_metadata(f, fmt)
if mi.cover_data and mi.cover_data[1]: if mi.cover_data and mi.cover_data[1]:
with open(os.path.join(tdir, 'cover.jpg'), 'wb') as f: with open(os.path.join(tdir, 'cover.jpg'), 'wb') as f:

View File

@ -7,7 +7,7 @@ __license__ = 'GPL v3'
__copyright__ = '2012, Kovid Goyal <kovid@kovidgoyal.net>' __copyright__ = '2012, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
import os, tempfile, shutil import os, tempfile, shutil, time
from threading import Thread, Event from threading import Thread, Event
from PyQt4.Qt import (QFileSystemWatcher, QObject, Qt, pyqtSignal, QTimer) from PyQt4.Qt import (QFileSystemWatcher, QObject, Qt, pyqtSignal, QTimer)
@ -41,25 +41,58 @@ class Worker(Thread):
traceback.print_exc() traceback.print_exc()
def auto_add(self): def auto_add(self):
from calibre.utils.ipc.simple_worker import fork_job from calibre.utils.ipc.simple_worker import fork_job, WorkerError
from calibre.ebooks.metadata.opf2 import metadata_to_opf from calibre.ebooks.metadata.opf2 import metadata_to_opf
from calibre.ebooks.metadata.meta import metadata_from_filename from calibre.ebooks.metadata.meta import metadata_from_filename
files = [x for x in os.listdir(self.path) if x not in self.staging files = [x for x in os.listdir(self.path) if
and os.path.isfile(os.path.join(self.path, x)) and # Must not be in the process of being added to the db
os.access(os.path.join(self.path, x), os.R_OK|os.W_OK) and x not in self.staging
os.path.splitext(x)[1][1:].lower() in self.be] # Firefox creates 0 byte placeholder files when downloading
and os.stat(os.path.join(self.path, x)).st_size > 0
# Must be a file
and os.path.isfile(os.path.join(self.path, x))
# Must have read and write permissions
and os.access(os.path.join(self.path, x), os.R_OK|os.W_OK)
# Must be a known ebook file type
and os.path.splitext(x)[1][1:].lower() in self.be
]
data = {} data = {}
# Give any in progress copies time to complete
time.sleep(2)
for fname in files: for fname in files:
f = os.path.join(self.path, fname) f = os.path.join(self.path, fname)
# Try opening the file for reading, if the OS prevents us, then at
# least on windows, it means the file is open in another
# application for writing. We will get notified by
# QFileSystemWatcher when writing is completed, so ignore for now.
try:
open(f, 'rb').close()
except:
continue
tdir = tempfile.mkdtemp(dir=self.tdir) tdir = tempfile.mkdtemp(dir=self.tdir)
try: try:
fork_job('calibre.ebooks.metadata.meta', fork_job('calibre.ebooks.metadata.meta',
'forked_read_metadata', (f, tdir), no_output=True) 'forked_read_metadata', (f, tdir), no_output=True)
except WorkerError as e:
prints('Failed to read metadata from:', fname)
prints(e.orig_tb)
except: except:
import traceback import traceback
traceback.print_exc() traceback.print_exc()
# Ensure that the pre-metadata file size is present. If it isn't,
# write 0 so that the file is rescanned
szpath = os.path.join(tdir, 'size.txt')
try:
with open(szpath, 'rb') as f:
int(f.read())
except:
with open(szpath, 'wb') as f:
f.write(b'0')
opfpath = os.path.join(tdir, 'metadata.opf') opfpath = os.path.join(tdir, 'metadata.opf')
try: try:
if os.stat(opfpath).st_size < 30: if os.stat(opfpath).st_size < 30:
@ -125,8 +158,23 @@ class AutoAdder(QObject):
m = gui.library_view.model() m = gui.library_view.model()
count = 0 count = 0
needs_rescan = False
for fname, tdir in data.iteritems(): for fname, tdir in data.iteritems():
paths = [os.path.join(self.worker.path, fname)] paths = [os.path.join(self.worker.path, fname)]
sz = os.path.join(tdir, 'size.txt')
if not os.access(sz, os.R_OK):
continue
try:
with open(sz, 'rb') as f:
sz = int(f.read())
if sz != os.stat(paths[0]).st_size:
raise Exception('Looks like the file was written to after'
' we tried to read metadata')
except:
needs_rescan = True
continue
mi = os.path.join(tdir, 'metadata.opf') mi = os.path.join(tdir, 'metadata.opf')
if not os.access(mi, os.R_OK): if not os.access(mi, os.R_OK):
continue continue
@ -135,7 +183,7 @@ class AutoAdder(QObject):
m.add_books(paths, [os.path.splitext(fname)[1][1:].upper()], mi, m.add_books(paths, [os.path.splitext(fname)[1][1:].upper()], mi,
add_duplicates=True) add_duplicates=True)
try: try:
os.remove(os.path.join(self.worker.path, fname)) os.remove(paths[0])
try: try:
self.worker.staging.remove(fname) self.worker.staging.remove(fname)
except KeyError: except KeyError:
@ -153,4 +201,7 @@ class AutoAdder(QObject):
if hasattr(gui, 'db_images'): if hasattr(gui, 'db_images'):
gui.db_images.reset() gui.db_images.reset()
if needs_rescan:
QTimer.singleShot(2000, self.dir_changed)

View File

@ -19,7 +19,7 @@ from calibre.utils.ipc.launch import Worker
class WorkerError(Exception): class WorkerError(Exception):
def __init__(self, msg, orig_tb=''): def __init__(self, msg, orig_tb=''):
Exception.__init__(self, msg) Exception.__init__(self, msg)
self.org_tb = orig_tb self.orig_tb = orig_tb
class ConnectedWorker(Thread): class ConnectedWorker(Thread):