Make thumbnail generation atomic and warm it on import
- write thumbnails to a .part file and os.replace() them, so concurrent requests never read a half-written JPEG - a stale thumbnail plus a vanished source no longer raises through the request (getmtime on a missing file returned 500); it falls back cleanly - ensure_thumbnail(item) warms the preview when a file is indexed, keeping image decoding out of the request path
This commit is contained in:
@@ -120,6 +120,27 @@ class MediaCacheTests(TestCase):
|
||||
self.client.get(url, self.signed("thumbnail"))
|
||||
self.assertEqual(thumb.stat().st_mtime_ns, before)
|
||||
|
||||
def test_ensure_thumbnail_reuses_the_cache(self):
|
||||
first = services.ensure_thumbnail(self.item)
|
||||
self.assertIsNotNone(first)
|
||||
self.assertTrue(first.exists())
|
||||
mtime = first.stat().st_mtime_ns
|
||||
second = services.ensure_thumbnail(self.item)
|
||||
self.assertEqual(second, first)
|
||||
self.assertEqual(second.stat().st_mtime_ns, mtime)
|
||||
|
||||
def test_thumbnail_of_a_missing_source_does_not_error(self):
|
||||
"""Regression: getmtime() on a vanished source used to raise a 500."""
|
||||
thumbs = self._media / "thumbs"
|
||||
thumbs.mkdir(parents=True, exist_ok=True)
|
||||
(thumbs / f"{self.item.md5}.jpg").write_bytes(b"stale")
|
||||
Path(self.item.locations.first().path).unlink()
|
||||
self.assertIsNone(services.ensure_thumbnail(self.item))
|
||||
response = self.client.get(
|
||||
f"/api/files/J-{self.item.id}/thumbnail/", self.signed("thumbnail")
|
||||
)
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
def test_staged_files_cache_briefly(self):
|
||||
temp = TempUpload.objects.create(
|
||||
user=self.user,
|
||||
|
||||
Reference in New Issue
Block a user