Files
cista-storage/cista/util/lrucache.py
T
LeoVasanko 8c93a4f2b5 Add OnlyOffice-based preview for office documents
Replace Aspose.Words with OnlyOffice Document Server for generating
bitmap previews of office documents (Word, Excel, PowerPoint, etc.).

Backend:
- Add cista/onlyoffice.py conversion client
- Convert office docs directly to PNG via OnlyOffice, then AVIF via pyvips
- Make office previews optional based on OnlyOffice availability
- Remove Aspose.Words dependency and all related code
- Add spreadsheet and presentation format support

Frontend:
- Mark office files as previewable in Document.ts
- Add office extensions to MediaPreview.vue preview list
- Fix pre-existing @ts-ignore in HeaderMain.vue

Tests:
- Fix test_lrucache.py parameter name (open -> opener)

Also run ruff format across the codebase to satisfy linter checks.
2026-04-26 06:59:01 +00:00

72 lines
2.1 KiB
Python

from collections.abc import Callable
from time import monotonic
class LRUCache:
"""
LRUCache is a least-recently-used (LRU) cache with expiry time.
Attributes:
opener (callable): Function to open a new handle.
capacity (int): Max number of items in the cache.
maxage (float): Max age for items in cache in seconds.
cache (list): Internal list storing the cache items.
"""
def __init__(self, opener: Callable, *, capacity: int, maxage: float):
"""
Initialize LRUCache.
Args:
opener (callable): Function to open a new handle.
capacity (int): Maximum capacity of the cache.
maxage (float): Max age for items in cache in seconds.
"""
self.opener = opener
self.capacity = capacity
self.maxage = maxage
self.cache = [] # Each item is a tuple: (key, handle, timestamp), recent items first
def __contains__(self, key):
"""Check if key is in cache."""
return any(rec[0] == key for rec in self.cache)
def __getitem__(self, key):
"""
Retrieve an item by its key.
Args:
key: The key to retrieve.
Returns:
The corresponding item's handle.
"""
# Take from cache or open a new one
for i, (k, f, _ts) in enumerate(self.cache): # noqa: B007
if k == key:
self.cache.pop(i)
break
else:
f = self.opener(key)
# Add/restore to end of cache
self.cache.insert(0, (key, f, monotonic()))
self.expire_items()
return f
def expire_items(self):
"""
Expire items that are either too old or exceed cache capacity.
"""
ts = monotonic() - self.maxage
while len(self.cache) > self.capacity or (
self.cache and self.cache[-1][2] < ts
):
self.cache.pop()[1].close()
def close(self):
"""
Close the cache and remove all items.
"""
self.capacity = 0
self.expire_items()