mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-06 16:07:01 +08:00
fix cache
This commit is contained in:
@@ -73,8 +73,9 @@ async def fetch_image(
|
|||||||
# 没有文件类型,则添加后缀,在恶意文件类型和实际需求下的折衷选择
|
# 没有文件类型,则添加后缀,在恶意文件类型和实际需求下的折衷选择
|
||||||
cache_path = cache_path.with_suffix(".jpg")
|
cache_path = cache_path.with_suffix(".jpg")
|
||||||
|
|
||||||
# 缓存对像
|
# 缓存对像,缓存过期时间为全局图片缓存天数
|
||||||
cache_backend = get_async_file_cache_backend(base=settings.CACHE_PATH)
|
cache_backend = get_async_file_cache_backend(base=settings.CACHE_PATH,
|
||||||
|
ttl=settings.GLOBAL_IMAGE_CACHE_DAYS * 24 * 3600)
|
||||||
|
|
||||||
if use_cache:
|
if use_cache:
|
||||||
content = await cache_backend.get(cache_path.as_posix(), region="images")
|
content = await cache_backend.get(cache_path.as_posix(), region="images")
|
||||||
|
|||||||
@@ -110,8 +110,9 @@ class RecommendChain(ChainBase, metaclass=Singleton):
|
|||||||
if not cache_path.suffix:
|
if not cache_path.suffix:
|
||||||
cache_path = cache_path.with_suffix(".jpg")
|
cache_path = cache_path.with_suffix(".jpg")
|
||||||
|
|
||||||
# 获取缓存后端
|
# 获取缓存后端,并设置缓存时间为全局配置的缓存天数
|
||||||
cache_backend = get_file_cache_backend(base=settings.CACHE_PATH)
|
cache_backend = get_file_cache_backend(base=settings.CACHE_PATH,
|
||||||
|
ttl=settings.GLOBAL_IMAGE_CACHE_DAYS * 24 * 3600)
|
||||||
|
|
||||||
# 本地存在缓存图片,则直接跳过
|
# 本地存在缓存图片,则直接跳过
|
||||||
if cache_backend.get(cache_path.as_posix(), region="images"):
|
if cache_backend.get(cache_path.as_posix(), region="images"):
|
||||||
|
|||||||
+29
-23
@@ -5,7 +5,7 @@ import threading
|
|||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, Optional
|
from typing import Any, Dict, Optional, Generator, AsyncGenerator, Tuple
|
||||||
|
|
||||||
import aiofiles
|
import aiofiles
|
||||||
import aioshutil
|
import aioshutil
|
||||||
@@ -84,7 +84,7 @@ class CacheBackend(ABC):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def items(self, region: Optional[str] = DEFAULT_CACHE_REGION) -> Dict[str, Any]:
|
def items(self, region: Optional[str] = DEFAULT_CACHE_REGION) -> Generator[Tuple[str, Any], None, None]:
|
||||||
"""
|
"""
|
||||||
获取指定区域的所有缓存项
|
获取指定区域的所有缓存项
|
||||||
|
|
||||||
@@ -197,7 +197,7 @@ class AsyncCacheBackend(ABC):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
async def items(self, region: Optional[str] = DEFAULT_CACHE_REGION) -> Dict[str, Any]:
|
async def items(self, region: Optional[str] = DEFAULT_CACHE_REGION) -> AsyncGenerator[Tuple[str, Any], None]:
|
||||||
"""
|
"""
|
||||||
获取指定区域的所有缓存项
|
获取指定区域的所有缓存项
|
||||||
|
|
||||||
@@ -352,7 +352,7 @@ class CacheToolsBackend(CacheBackend):
|
|||||||
region_cache.clear()
|
region_cache.clear()
|
||||||
logger.info("Cleared all cache")
|
logger.info("Cleared all cache")
|
||||||
|
|
||||||
def items(self, region: Optional[str] = DEFAULT_CACHE_REGION) -> Dict[str, Any]:
|
def items(self, region: Optional[str] = DEFAULT_CACHE_REGION) -> Generator[Tuple[str, Any], None, None]:
|
||||||
"""
|
"""
|
||||||
获取指定区域的所有缓存项
|
获取指定区域的所有缓存项
|
||||||
|
|
||||||
@@ -361,8 +361,9 @@ class CacheToolsBackend(CacheBackend):
|
|||||||
"""
|
"""
|
||||||
region_cache = self.__get_region_cache(region)
|
region_cache = self.__get_region_cache(region)
|
||||||
if region_cache is None:
|
if region_cache is None:
|
||||||
return {}
|
yield from ()
|
||||||
return dict(region_cache.items())
|
for item in region_cache.items():
|
||||||
|
yield item
|
||||||
|
|
||||||
def close(self) -> None:
|
def close(self) -> None:
|
||||||
"""
|
"""
|
||||||
@@ -436,7 +437,7 @@ class RedisBackend(CacheBackend):
|
|||||||
"""
|
"""
|
||||||
self.redis_helper.clear(region=region)
|
self.redis_helper.clear(region=region)
|
||||||
|
|
||||||
def items(self, region: Optional[str] = DEFAULT_CACHE_REGION) -> Dict[str, Any]:
|
def items(self, region: Optional[str] = DEFAULT_CACHE_REGION) -> Generator[Tuple[Any, Any], None, None]:
|
||||||
"""
|
"""
|
||||||
获取指定区域的所有缓存项
|
获取指定区域的所有缓存项
|
||||||
|
|
||||||
@@ -517,14 +518,15 @@ class AsyncRedisBackend(AsyncCacheBackend):
|
|||||||
"""
|
"""
|
||||||
await self.redis_helper.clear(region=region)
|
await self.redis_helper.clear(region=region)
|
||||||
|
|
||||||
async def items(self, region: Optional[str] = DEFAULT_CACHE_REGION) -> Dict[str, Any]:
|
async def items(self, region: Optional[str] = DEFAULT_CACHE_REGION) -> AsyncGenerator[Tuple[str, Any], None]:
|
||||||
"""
|
"""
|
||||||
获取指定区域的所有缓存项
|
获取指定区域的所有缓存项
|
||||||
|
|
||||||
:param region: 缓存的区
|
:param region: 缓存的区
|
||||||
:return: 返回一个字典,包含所有缓存键值对
|
:return: 返回一个字典,包含所有缓存键值对
|
||||||
"""
|
"""
|
||||||
return await self.redis_helper.items(region=region)
|
async for item in self.redis_helper.items(region=region):
|
||||||
|
yield item
|
||||||
|
|
||||||
async def close(self) -> None:
|
async def close(self) -> None:
|
||||||
"""
|
"""
|
||||||
@@ -572,7 +574,7 @@ class FileBackend(CacheBackend):
|
|||||||
:param region: 缓存的区
|
:param region: 缓存的区
|
||||||
:return: 存在返回 True,否则返回 False
|
:return: 存在返回 True,否则返回 False
|
||||||
"""
|
"""
|
||||||
cache_path = self.base / key
|
cache_path = self.base / region / key
|
||||||
return cache_path.exists()
|
return cache_path.exists()
|
||||||
|
|
||||||
def get(self, key: str, region: Optional[str] = DEFAULT_CACHE_REGION) -> Optional[Any]:
|
def get(self, key: str, region: Optional[str] = DEFAULT_CACHE_REGION) -> Optional[Any]:
|
||||||
@@ -623,7 +625,7 @@ class FileBackend(CacheBackend):
|
|||||||
else:
|
else:
|
||||||
shutil.rmtree(item, ignore_errors=True)
|
shutil.rmtree(item, ignore_errors=True)
|
||||||
|
|
||||||
def items(self, region: Optional[str] = DEFAULT_CACHE_REGION) -> Dict[str, Any]:
|
def items(self, region: Optional[str] = DEFAULT_CACHE_REGION) -> Generator[Tuple[str, Any], None, None]:
|
||||||
"""
|
"""
|
||||||
获取指定区域的所有缓存项
|
获取指定区域的所有缓存项
|
||||||
|
|
||||||
@@ -632,11 +634,11 @@ class FileBackend(CacheBackend):
|
|||||||
"""
|
"""
|
||||||
cache_path = self.base / region
|
cache_path = self.base / region
|
||||||
if not cache_path.exists():
|
if not cache_path.exists():
|
||||||
return {}
|
yield from ()
|
||||||
for item in cache_path.iterdir():
|
for item in cache_path.iterdir():
|
||||||
if item.is_file():
|
if item.is_file():
|
||||||
with open(item, 'r') as f:
|
with open(item, 'r') as f:
|
||||||
yield f.read()
|
yield item.name, f.read()
|
||||||
|
|
||||||
def close(self) -> None:
|
def close(self) -> None:
|
||||||
"""
|
"""
|
||||||
@@ -722,20 +724,20 @@ class AsyncFileBackend(AsyncCacheBackend):
|
|||||||
# 清理指定缓存区
|
# 清理指定缓存区
|
||||||
cache_path = AsyncPath(self.base) / region
|
cache_path = AsyncPath(self.base) / region
|
||||||
if await cache_path.exists():
|
if await cache_path.exists():
|
||||||
for item in cache_path.iterdir():
|
async for item in cache_path.iterdir():
|
||||||
if await item.is_file():
|
if await item.is_file():
|
||||||
await item.unlink()
|
await item.unlink()
|
||||||
else:
|
else:
|
||||||
await aioshutil.rmtree(item, ignore_errors=True)
|
await aioshutil.rmtree(item, ignore_errors=True)
|
||||||
else:
|
else:
|
||||||
# 清除所有区域的缓存
|
# 清除所有区域的缓存
|
||||||
for item in AsyncPath(self.base).iterdir():
|
async for item in AsyncPath(self.base).iterdir():
|
||||||
if await item.is_file():
|
if await item.is_file():
|
||||||
await item.unlink()
|
await item.unlink()
|
||||||
else:
|
else:
|
||||||
await aioshutil.rmtree(item, ignore_errors=True)
|
await aioshutil.rmtree(item, ignore_errors=True)
|
||||||
|
|
||||||
async def items(self, region: Optional[str] = DEFAULT_CACHE_REGION) -> Dict[str, Any]:
|
async def items(self, region: Optional[str] = DEFAULT_CACHE_REGION) -> AsyncGenerator[Tuple[str, Any], None]:
|
||||||
"""
|
"""
|
||||||
获取指定区域的所有缓存项
|
获取指定区域的所有缓存项
|
||||||
|
|
||||||
@@ -744,11 +746,11 @@ class AsyncFileBackend(AsyncCacheBackend):
|
|||||||
"""
|
"""
|
||||||
cache_path = AsyncPath(self.base) / region
|
cache_path = AsyncPath(self.base) / region
|
||||||
if not await cache_path.exists():
|
if not await cache_path.exists():
|
||||||
yield None
|
yield "", None
|
||||||
for item in cache_path.iterdir():
|
async for item in cache_path.iterdir():
|
||||||
if await item.is_file():
|
if await item.is_file():
|
||||||
async with aiofiles.open(item, 'r') as f:
|
async with aiofiles.open(item, 'r') as f:
|
||||||
yield await f.read()
|
yield item.name, await f.read()
|
||||||
|
|
||||||
async def close(self) -> None:
|
async def close(self) -> None:
|
||||||
"""
|
"""
|
||||||
@@ -757,23 +759,27 @@ class AsyncFileBackend(AsyncCacheBackend):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def get_file_cache_backend(base: Path = settings.TEMP_PATH) -> CacheBackend:
|
def get_file_cache_backend(base: Path = settings.TEMP_PATH, ttl: Optional[int] = None) -> CacheBackend:
|
||||||
"""
|
"""
|
||||||
获取文件缓存后端实例(Redis或文件系统)
|
获取文件缓存后端实例(Redis或文件系统)
|
||||||
"""
|
"""
|
||||||
if settings.CACHE_BACKEND_TYPE == "redis":
|
if settings.CACHE_BACKEND_TYPE == "redis":
|
||||||
return RedisBackend()
|
# 如果使用 Redis,则设置缓存的存活时间为配置的天数转换为秒
|
||||||
|
return RedisBackend(ttl=ttl or settings.TEMP_FILE_DAYS * 24 * 3600)
|
||||||
else:
|
else:
|
||||||
|
# 如果使用文件系统,在停止服务时会自动清理过期文件
|
||||||
return FileBackend(base=base)
|
return FileBackend(base=base)
|
||||||
|
|
||||||
|
|
||||||
def get_async_file_cache_backend(base: Path = settings.TEMP_PATH) -> AsyncCacheBackend:
|
def get_async_file_cache_backend(base: Path = settings.TEMP_PATH, ttl: Optional[int] = None) -> AsyncCacheBackend:
|
||||||
"""
|
"""
|
||||||
获取文件异步缓存后端实例(Redis或文件系统)
|
获取文件异步缓存后端实例(Redis或文件系统)
|
||||||
"""
|
"""
|
||||||
if settings.CACHE_BACKEND_TYPE == "redis":
|
if settings.CACHE_BACKEND_TYPE == "redis":
|
||||||
return AsyncRedisBackend()
|
# 如果使用 Redis,则设置缓存的存活时间为配置的天数转换为秒
|
||||||
|
return AsyncRedisBackend(ttl=ttl or settings.TEMP_FILE_DAYS * 24 * 3600)
|
||||||
else:
|
else:
|
||||||
|
# 如果使用文件系统,在停止服务时会自动清理过期文件
|
||||||
return AsyncFileBackend(base=base)
|
return AsyncFileBackend(base=base)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+5
-5
@@ -1,6 +1,6 @@
|
|||||||
import json
|
import json
|
||||||
import pickle
|
import pickle
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional, Generator, Tuple, AsyncGenerator
|
||||||
from urllib.parse import quote
|
from urllib.parse import quote
|
||||||
|
|
||||||
import redis
|
import redis
|
||||||
@@ -245,7 +245,7 @@ class RedisHelper(metaclass=Singleton):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to clear cache, region: {region}, error: {e}")
|
logger.error(f"Failed to clear cache, region: {region}, error: {e}")
|
||||||
|
|
||||||
def items(self, region: Optional[str] = None):
|
def items(self, region: Optional[str] = None) -> Generator[Tuple[Any, Any], None, None]:
|
||||||
"""
|
"""
|
||||||
获取指定区域的所有缓存键值对
|
获取指定区域的所有缓存键值对
|
||||||
|
|
||||||
@@ -525,7 +525,7 @@ class AsyncRedisHelper(metaclass=Singleton):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to clear cache (async), region: {region}, error: {e}")
|
logger.error(f"Failed to clear cache (async), region: {region}, error: {e}")
|
||||||
|
|
||||||
async def items(self, region: Optional[str] = None):
|
async def items(self, region: Optional[str] = None) -> AsyncGenerator[Tuple[Any, Any], None]:
|
||||||
"""
|
"""
|
||||||
获取指定区域的所有缓存键值对
|
获取指定区域的所有缓存键值对
|
||||||
|
|
||||||
@@ -537,12 +537,12 @@ class AsyncRedisHelper(metaclass=Singleton):
|
|||||||
if region:
|
if region:
|
||||||
cache_region = self.get_region(quote(region))
|
cache_region = self.get_region(quote(region))
|
||||||
redis_key = f"{cache_region}:key:*"
|
redis_key = f"{cache_region}:key:*"
|
||||||
for key in self.client.scan_iter(redis_key):
|
async for key in self.client.scan_iter(redis_key):
|
||||||
value = await self.client.get(key)
|
value = await self.client.get(key)
|
||||||
if value is not None:
|
if value is not None:
|
||||||
yield key, self.deserialize(value)
|
yield key, self.deserialize(value)
|
||||||
else:
|
else:
|
||||||
for key in self.client.scan_iter("*"):
|
async for key in self.client.scan_iter("*"):
|
||||||
value = await self.client.get(key)
|
value = await self.client.get(key)
|
||||||
if value is not None:
|
if value is not None:
|
||||||
yield key, self.deserialize(value)
|
yield key, self.deserialize(value)
|
||||||
|
|||||||
Reference in New Issue
Block a user