mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-06-23 15:30:45 -04:00
Preserve notes resource mtimes when importing notes db
This commit is contained in:
parent
53f20a2ce6
commit
1e9a543e54
@ -989,8 +989,8 @@ class DB:
|
|||||||
id_val = self.tables[field].id_map[item_id]
|
id_val = self.tables[field].id_map[item_id]
|
||||||
return self.notes.unretire(self.conn, field, item_id, id_val)
|
return self.notes.unretire(self.conn, field, item_id, id_val)
|
||||||
|
|
||||||
def add_notes_resource(self, path_or_stream, name) -> int:
|
def add_notes_resource(self, path_or_stream, name, mtime=None) -> int:
|
||||||
return self.notes.add_resource(self.conn, path_or_stream, name)
|
return self.notes.add_resource(self.conn, path_or_stream, name, mtime=mtime)
|
||||||
|
|
||||||
def get_notes_resource(self, resource_hash) -> Optional[dict]:
|
def get_notes_resource(self, resource_hash) -> Optional[dict]:
|
||||||
return self.notes.get_resource_data(self.conn, resource_hash)
|
return self.notes.get_resource_data(self.conn, resource_hash)
|
||||||
|
@ -20,7 +20,7 @@ from functools import partial, wraps
|
|||||||
from io import DEFAULT_BUFFER_SIZE, BytesIO
|
from io import DEFAULT_BUFFER_SIZE, BytesIO
|
||||||
from queue import Queue
|
from queue import Queue
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
from time import monotonic, sleep, time
|
from time import mktime, monotonic, sleep, time
|
||||||
from typing import NamedTuple, Optional, Tuple
|
from typing import NamedTuple, Optional, Tuple
|
||||||
|
|
||||||
from calibre import as_unicode, detect_ncpus, isbytestring
|
from calibre import as_unicode, detect_ncpus, isbytestring
|
||||||
@ -724,9 +724,9 @@ class Cache:
|
|||||||
return self.backend.set_notes_for(field, item_id, doc, searchable_text, resource_hashes, remove_unused_resources)
|
return self.backend.set_notes_for(field, item_id, doc, searchable_text, resource_hashes, remove_unused_resources)
|
||||||
|
|
||||||
@write_api
|
@write_api
|
||||||
def add_notes_resource(self, path_or_stream_or_data, name: str) -> int:
|
def add_notes_resource(self, path_or_stream_or_data, name: str, mtime: float = None) -> int:
|
||||||
' Add the specified resource so it can be referenced by notes and return its content hash '
|
' Add the specified resource so it can be referenced by notes and return its content hash '
|
||||||
return self.backend.add_notes_resource(path_or_stream_or_data, name)
|
return self.backend.add_notes_resource(path_or_stream_or_data, name, mtime)
|
||||||
|
|
||||||
@read_api
|
@read_api
|
||||||
def get_notes_resource(self, resource_hash) -> Optional[dict]:
|
def get_notes_resource(self, resource_hash) -> Optional[dict]:
|
||||||
@ -3460,7 +3460,10 @@ def import_library(library_key, importer, library_path, progress=None, abort=Non
|
|||||||
with closing(importer.start_file(metadata['notes.db'], 'notes.db for ' + library_path)) as stream:
|
with closing(importer.start_file(metadata['notes.db'], 'notes.db for ' + library_path)) as stream:
|
||||||
stream.check_hash = False
|
stream.check_hash = False
|
||||||
with zipfile.ZipFile(stream) as zf:
|
with zipfile.ZipFile(stream) as zf:
|
||||||
zf.extractall(notes_dir)
|
for zi in zf.infolist():
|
||||||
|
tpath = zf._extract_member(zi, notes_dir, None)
|
||||||
|
date_time = mktime(zi.date_time + (0, 0, -1))
|
||||||
|
os.utime(tpath, (date_time, date_time))
|
||||||
if abort is not None and abort.is_set():
|
if abort is not None and abort.is_set():
|
||||||
return
|
return
|
||||||
cache = Cache(DB(library_path, load_user_formatter_functions=False))
|
cache = Cache(DB(library_path, load_user_formatter_functions=False))
|
||||||
|
@ -306,7 +306,7 @@ class Notes:
|
|||||||
for path in items[:extra]:
|
for path in items[:extra]:
|
||||||
remove_with_retry(path, is_dir=True)
|
remove_with_retry(path, is_dir=True)
|
||||||
|
|
||||||
def add_resource(self, conn, path_or_stream_or_data, name, update_name=True):
|
def add_resource(self, conn, path_or_stream_or_data, name, update_name=True, mtime=None):
|
||||||
if isinstance(path_or_stream_or_data, bytes):
|
if isinstance(path_or_stream_or_data, bytes):
|
||||||
data = path_or_stream_or_data
|
data = path_or_stream_or_data
|
||||||
elif isinstance(path_or_stream_or_data, str):
|
elif isinstance(path_or_stream_or_data, str):
|
||||||
@ -332,6 +332,9 @@ class Notes:
|
|||||||
f = open(path, 'wb')
|
f = open(path, 'wb')
|
||||||
with f:
|
with f:
|
||||||
f.write(data)
|
f.write(data)
|
||||||
|
if mtime is not None:
|
||||||
|
os.utime(f.name, (mtime, mtime))
|
||||||
|
|
||||||
name = sanitize_file_name(name)
|
name = sanitize_file_name(name)
|
||||||
base_name, ext = os.path.splitext(name)
|
base_name, ext = os.path.splitext(name)
|
||||||
c = 0
|
c = 0
|
||||||
|
@ -264,8 +264,8 @@ class FilesystemTest(BaseTest):
|
|||||||
bookdir = os.path.dirname(ic.format_abspath(1, '__COVER_INTERNAL__'))
|
bookdir = os.path.dirname(ic.format_abspath(1, '__COVER_INTERNAL__'))
|
||||||
self.assertEqual('exf', open(os.path.join(bookdir, 'exf')).read())
|
self.assertEqual('exf', open(os.path.join(bookdir, 'exf')).read())
|
||||||
self.assertEqual('recurse', open(os.path.join(bookdir, 'sub', 'recurse')).read())
|
self.assertEqual('recurse', open(os.path.join(bookdir, 'sub', 'recurse')).read())
|
||||||
r1 = cache.add_notes_resource(b'res1', 'res.jpg')
|
r1 = cache.add_notes_resource(b'res1', 'res.jpg', mtime=time.time()-113)
|
||||||
r2 = cache.add_notes_resource(b'res2', 'res.jpg')
|
r2 = cache.add_notes_resource(b'res2', 'res.jpg', mtime=time.time()-1115)
|
||||||
cache.set_notes_for('authors', 2, 'some notes', resource_hashes=(r1, r2))
|
cache.set_notes_for('authors', 2, 'some notes', resource_hashes=(r1, r2))
|
||||||
cache.add_format(1, 'TXT', BytesIO(b'testing exim'))
|
cache.add_format(1, 'TXT', BytesIO(b'testing exim'))
|
||||||
cache.fts_indexing_sleep_time = 0.001
|
cache.fts_indexing_sleep_time = 0.001
|
||||||
@ -285,7 +285,10 @@ class FilesystemTest(BaseTest):
|
|||||||
ic = import_library('l', importer, idir)
|
ic = import_library('l', importer, idir)
|
||||||
self.assertEqual(ic.fts_search('exim')[0]['id'], 1)
|
self.assertEqual(ic.fts_search('exim')[0]['id'], 1)
|
||||||
self.assertEqual(cache.notes_for('authors', 2), ic.notes_for('authors', 2))
|
self.assertEqual(cache.notes_for('authors', 2), ic.notes_for('authors', 2))
|
||||||
self.assertEqual(cache.get_notes_resource(r1), ic.get_notes_resource(r1))
|
a, b = cache.get_notes_resource(r1), ic.get_notes_resource(r1)
|
||||||
|
at, bt, = a.pop('mtime'), b.pop('mtime')
|
||||||
|
self.assertEqual(a, b)
|
||||||
|
self.assertLess(abs(at-bt), 2)
|
||||||
|
|
||||||
def test_find_books_in_directory(self):
|
def test_find_books_in_directory(self):
|
||||||
from calibre.db.adding import find_books_in_directory, compile_rule
|
from calibre.db.adding import find_books_in_directory, compile_rule
|
||||||
|
@ -151,6 +151,10 @@ def test_cache_api(self: 'NotesTest'):
|
|||||||
self.assertGreater(note_id, 0)
|
self.assertGreater(note_id, 0)
|
||||||
self.assertIn('<p>test simple exim <img', cache.notes_for('authors', author_id))
|
self.assertIn('<p>test simple exim <img', cache.notes_for('authors', author_id))
|
||||||
res2 = tuple(cache.get_notes_resource(x) for x in cache.notes_resources_used_by('authors', author_id))
|
res2 = tuple(cache.get_notes_resource(x) for x in cache.notes_resources_used_by('authors', author_id))
|
||||||
|
for x in res:
|
||||||
|
del x['mtime']
|
||||||
|
for x in res2:
|
||||||
|
del x['mtime']
|
||||||
self.ae(sorted(res, key=itemgetter('name')), sorted(res2, key=itemgetter('name')))
|
self.ae(sorted(res, key=itemgetter('name')), sorted(res2, key=itemgetter('name')))
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user