mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-05 07:27:15 +08:00
优化缓存机制,支持Redis和本地缓存的切换
This commit is contained in:
@@ -54,6 +54,7 @@ class ChainBase(metaclass=ABCMeta):
|
|||||||
try:
|
try:
|
||||||
self._redis_helper = RedisHelper(redis_url=settings.CACHE_BACKEND_URL)
|
self._redis_helper = RedisHelper(redis_url=settings.CACHE_BACKEND_URL)
|
||||||
except RuntimeError as e:
|
except RuntimeError as e:
|
||||||
|
self._redis_helper = None
|
||||||
logger.warning(f"Redis缓存初始化失败,将使用本地缓存: {e}")
|
logger.warning(f"Redis缓存初始化失败,将使用本地缓存: {e}")
|
||||||
|
|
||||||
def load_cache(self, filename: str) -> Any:
|
def load_cache(self, filename: str) -> Any:
|
||||||
|
|||||||
@@ -9,9 +9,10 @@ from typing import Optional
|
|||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
from app.core.meta import MetaBase
|
from app.core.meta import MetaBase
|
||||||
from app.core.metainfo import MetaInfo
|
from app.core.metainfo import MetaInfo
|
||||||
|
from app.helper.redis import RedisHelper
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.utils.singleton import WeakSingleton
|
|
||||||
from app.schemas.types import MediaType
|
from app.schemas.types import MediaType
|
||||||
|
from app.utils.singleton import WeakSingleton
|
||||||
|
|
||||||
lock = RLock()
|
lock = RLock()
|
||||||
|
|
||||||
@@ -30,18 +31,35 @@ class DoubanCache(metaclass=WeakSingleton):
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
# TMDB缓存过期
|
# TMDB缓存过期
|
||||||
_tmdb_cache_expire: bool = True
|
_douban_cache_expire: bool = True
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
# 初始化Redis缓存助手
|
||||||
|
self._redis_helper = None
|
||||||
|
if settings.CACHE_BACKEND_TYPE == "redis":
|
||||||
|
try:
|
||||||
|
self._redis_helper = RedisHelper(redis_url=settings.CACHE_BACKEND_URL)
|
||||||
|
except RuntimeError as e:
|
||||||
|
logger.warning(f"豆瓣缓存Redis初始化失败,将使用本地缓存: {e}")
|
||||||
|
self._redis_helper = None
|
||||||
|
# 加载本地缓存数据
|
||||||
self._meta_path = settings.TEMP_PATH / "__douban_cache__"
|
self._meta_path = settings.TEMP_PATH / "__douban_cache__"
|
||||||
self._meta_data = self.__load(self._meta_path)
|
if not self._redis_helper:
|
||||||
|
self._meta_data = self.__load(self._meta_path)
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
"""
|
"""
|
||||||
清空所有TMDB缓存
|
清空所有豆瓣缓存
|
||||||
"""
|
"""
|
||||||
with lock:
|
with lock:
|
||||||
self._meta_data = {}
|
self._meta_data = {}
|
||||||
|
# 如果Redis可用,同时清理Redis缓存
|
||||||
|
if self._redis_helper:
|
||||||
|
try:
|
||||||
|
self._redis_helper.clear(region="douban_cache")
|
||||||
|
logger.debug("已清理豆瓣Redis缓存")
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"清理豆瓣Redis缓存失败: {e}")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __get_key(meta: MetaBase) -> str:
|
def __get_key(meta: MetaBase) -> str:
|
||||||
@@ -56,16 +74,28 @@ class DoubanCache(metaclass=WeakSingleton):
|
|||||||
根据KEY值获取缓存值
|
根据KEY值获取缓存值
|
||||||
"""
|
"""
|
||||||
key = self.__get_key(meta)
|
key = self.__get_key(meta)
|
||||||
with lock:
|
|
||||||
info: dict = self._meta_data.get(key)
|
if self._redis_helper:
|
||||||
if info:
|
# 如果Redis可用,从Redis读取
|
||||||
expire = info.get(CACHE_EXPIRE_TIMESTAMP_STR)
|
try:
|
||||||
if not expire or int(time.time()) < expire:
|
redis_data = self._redis_helper.get(key, region="douban_cache")
|
||||||
info[CACHE_EXPIRE_TIMESTAMP_STR] = int(time.time()) + EXPIRE_TIMESTAMP
|
return redis_data or {}
|
||||||
self._meta_data[key] = info
|
except Exception as e:
|
||||||
elif expire and self._tmdb_cache_expire:
|
logger.warning(f"从Redis获取豆瓣缓存失败: {e}")
|
||||||
self.delete(key)
|
else:
|
||||||
return info or {}
|
# Redis不可用时,从内存缓存读取
|
||||||
|
with lock:
|
||||||
|
info: dict = self._meta_data.get(key)
|
||||||
|
if info:
|
||||||
|
# 检查过期时间
|
||||||
|
expire = info.get(CACHE_EXPIRE_TIMESTAMP_STR)
|
||||||
|
if not expire or int(time.time()) < expire:
|
||||||
|
info[CACHE_EXPIRE_TIMESTAMP_STR] = int(time.time()) + EXPIRE_TIMESTAMP
|
||||||
|
self._meta_data[key] = info
|
||||||
|
elif expire and self._douban_cache_expire:
|
||||||
|
self.delete(key)
|
||||||
|
return info or {}
|
||||||
|
return {}
|
||||||
|
|
||||||
def delete(self, key: str) -> dict:
|
def delete(self, key: str) -> dict:
|
||||||
"""
|
"""
|
||||||
@@ -73,39 +103,44 @@ class DoubanCache(metaclass=WeakSingleton):
|
|||||||
@param key: 缓存key
|
@param key: 缓存key
|
||||||
@return: 被删除的缓存内容
|
@return: 被删除的缓存内容
|
||||||
"""
|
"""
|
||||||
with lock:
|
if self._redis_helper:
|
||||||
return self._meta_data.pop(key, {})
|
# 如果Redis可用,删除Redis缓存
|
||||||
|
try:
|
||||||
def delete_by_doubanid(self, doubanid: str) -> None:
|
self._redis_helper.delete(key, region="douban_cache")
|
||||||
"""
|
return {}
|
||||||
清空对应豆瓣ID的所有缓存记录,以强制更新TMDB中最新的数据
|
except Exception as e:
|
||||||
"""
|
logger.warning(f"删除豆瓣Redis缓存失败: {e}")
|
||||||
for key in list(self._meta_data):
|
return {}
|
||||||
if self._meta_data.get(key, {}).get("id") == doubanid:
|
else:
|
||||||
with lock:
|
# Redis不可用时,删除内存缓存
|
||||||
self._meta_data.pop(key)
|
with lock:
|
||||||
|
return self._meta_data.pop(key, {})
|
||||||
def delete_unknown(self) -> None:
|
|
||||||
"""
|
|
||||||
清除未识别的缓存记录,以便重新搜索TMDB
|
|
||||||
"""
|
|
||||||
for key in list(self._meta_data):
|
|
||||||
if self._meta_data.get(key, {}).get("id") == "0":
|
|
||||||
with lock:
|
|
||||||
self._meta_data.pop(key)
|
|
||||||
|
|
||||||
def modify(self, key: str, title: str) -> dict:
|
def modify(self, key: str, title: str) -> dict:
|
||||||
"""
|
"""
|
||||||
删除缓存信息
|
修改缓存信息
|
||||||
@param key: 缓存key
|
@param key: 缓存key
|
||||||
@param title: 标题
|
@param title: 标题
|
||||||
@return: 被修改后缓存内容
|
@return: 被修改后缓存内容
|
||||||
"""
|
"""
|
||||||
with lock:
|
if self._redis_helper:
|
||||||
if self._meta_data.get(key):
|
# 如果Redis可用,修改Redis缓存
|
||||||
self._meta_data[key]['title'] = title
|
try:
|
||||||
self._meta_data[key][CACHE_EXPIRE_TIMESTAMP_STR] = int(time.time()) + EXPIRE_TIMESTAMP
|
redis_data = self._redis_helper.get(key, region="douban_cache")
|
||||||
return self._meta_data.get(key)
|
if redis_data:
|
||||||
|
redis_data['title'] = title
|
||||||
|
self._redis_helper.set(key, redis_data, ttl=EXPIRE_TIMESTAMP, region="douban_cache")
|
||||||
|
return redis_data
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"修改豆瓣Redis缓存失败: {e}")
|
||||||
|
return {}
|
||||||
|
else:
|
||||||
|
# Redis不可用时,修改内存缓存
|
||||||
|
with lock:
|
||||||
|
if self._meta_data.get(key):
|
||||||
|
self._meta_data[key]['title'] = title
|
||||||
|
self._meta_data[key][CACHE_EXPIRE_TIMESTAMP_STR] = int(time.time()) + EXPIRE_TIMESTAMP
|
||||||
|
return self._meta_data.get(key)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __load(path: Path) -> dict:
|
def __load(path: Path) -> dict:
|
||||||
@@ -126,31 +161,47 @@ class DoubanCache(metaclass=WeakSingleton):
|
|||||||
"""
|
"""
|
||||||
新增或更新缓存条目
|
新增或更新缓存条目
|
||||||
"""
|
"""
|
||||||
with lock:
|
if info:
|
||||||
if info:
|
# 缓存标题
|
||||||
# 缓存标题
|
cache_title = info.get("title")
|
||||||
cache_title = info.get("title")
|
# 缓存年份
|
||||||
# 缓存年份
|
cache_year = info.get('year')
|
||||||
cache_year = info.get('year')
|
# 类型
|
||||||
# 类型
|
if isinstance(info.get('media_type'), MediaType):
|
||||||
if isinstance(info.get('media_type'), MediaType):
|
mtype = info.get('media_type')
|
||||||
mtype = info.get('media_type')
|
elif info.get("type"):
|
||||||
elif info.get("type"):
|
mtype = MediaType.MOVIE if info.get("type") == "movie" else MediaType.TV
|
||||||
mtype = MediaType.MOVIE if info.get("type") == "movie" else MediaType.TV
|
else:
|
||||||
|
meta = MetaInfo(cache_title)
|
||||||
|
if meta.begin_season:
|
||||||
|
mtype = MediaType.TV
|
||||||
else:
|
else:
|
||||||
meta = MetaInfo(cache_title)
|
mtype = MediaType.MOVIE
|
||||||
if meta.begin_season:
|
# 海报
|
||||||
mtype = MediaType.TV
|
poster_path = info.get("pic", {}).get("large")
|
||||||
else:
|
if not poster_path and info.get("cover_url"):
|
||||||
mtype = MediaType.MOVIE
|
poster_path = info.get("cover_url")
|
||||||
# 海报
|
if not poster_path and info.get("cover"):
|
||||||
poster_path = info.get("pic", {}).get("large")
|
poster_path = info.get("cover").get("url")
|
||||||
if not poster_path and info.get("cover_url"):
|
|
||||||
poster_path = info.get("cover_url")
|
|
||||||
if not poster_path and info.get("cover"):
|
|
||||||
poster_path = info.get("cover").get("url")
|
|
||||||
|
|
||||||
self._meta_data[self.__get_key(meta)] = {
|
if self._redis_helper:
|
||||||
|
# 如果Redis可用,保存到Redis
|
||||||
|
cache_data = {
|
||||||
|
"id": info.get("id"),
|
||||||
|
"type": mtype,
|
||||||
|
"year": cache_year,
|
||||||
|
"title": cache_title,
|
||||||
|
"poster_path": poster_path
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
self._redis_helper.set(self.__get_key(meta), cache_data, ttl=EXPIRE_TIMESTAMP,
|
||||||
|
region="douban_cache")
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"保存豆瓣缓存到Redis失败: {e}")
|
||||||
|
else:
|
||||||
|
# Redis不可用时,保存到内存缓存
|
||||||
|
with lock:
|
||||||
|
cache_data = {
|
||||||
"id": info.get("id"),
|
"id": info.get("id"),
|
||||||
"type": mtype,
|
"type": mtype,
|
||||||
"year": cache_year,
|
"year": cache_year,
|
||||||
@@ -158,15 +209,29 @@ class DoubanCache(metaclass=WeakSingleton):
|
|||||||
"poster_path": poster_path,
|
"poster_path": poster_path,
|
||||||
CACHE_EXPIRE_TIMESTAMP_STR: int(time.time()) + EXPIRE_TIMESTAMP
|
CACHE_EXPIRE_TIMESTAMP_STR: int(time.time()) + EXPIRE_TIMESTAMP
|
||||||
}
|
}
|
||||||
elif info is not None:
|
self._meta_data[self.__get_key(meta)] = cache_data
|
||||||
# None时不缓存,此时代表网络错误,允许重复请求
|
|
||||||
self._meta_data[self.__get_key(meta)] = {'id': "0"}
|
elif info is not None:
|
||||||
|
# None时不缓存,此时代表网络错误,允许重复请求
|
||||||
|
if self._redis_helper:
|
||||||
|
try:
|
||||||
|
self._redis_helper.set(self.__get_key(meta), {'id': "0"}, ttl=EXPIRE_TIMESTAMP,
|
||||||
|
region="douban_cache")
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"保存豆瓣缓存到Redis失败: {e}")
|
||||||
|
else:
|
||||||
|
with lock:
|
||||||
|
self._meta_data[self.__get_key(meta)] = {'id': "0"}
|
||||||
|
|
||||||
def save(self, force: Optional[bool] = False) -> None:
|
def save(self, force: Optional[bool] = False) -> None:
|
||||||
"""
|
"""
|
||||||
保存缓存数据到文件
|
保存缓存数据到文件
|
||||||
"""
|
"""
|
||||||
|
# 如果Redis可用,不需要保存到本地文件
|
||||||
|
if self._redis_helper:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Redis不可用时,保存到本地文件
|
||||||
meta_data = self.__load(self._meta_path)
|
meta_data = self.__load(self._meta_path)
|
||||||
new_meta_data = {k: v for k, v in self._meta_data.items() if v.get("id")}
|
new_meta_data = {k: v for k, v in self._meta_data.items() if v.get("id")}
|
||||||
|
|
||||||
@@ -176,7 +241,7 @@ class DoubanCache(metaclass=WeakSingleton):
|
|||||||
return
|
return
|
||||||
|
|
||||||
with open(self._meta_path, 'wb') as f:
|
with open(self._meta_path, 'wb') as f:
|
||||||
pickle.dump(new_meta_data, f, pickle.HIGHEST_PROTOCOL) # noqa
|
pickle.dump(new_meta_data, f, pickle.HIGHEST_PROTOCOL) # noqa
|
||||||
|
|
||||||
def _random_sample(self, new_meta_data: dict) -> bool:
|
def _random_sample(self, new_meta_data: dict) -> bool:
|
||||||
"""
|
"""
|
||||||
@@ -193,7 +258,7 @@ class DoubanCache(metaclass=WeakSingleton):
|
|||||||
info[CACHE_EXPIRE_TIMESTAMP_STR] = int(time.time()) + EXPIRE_TIMESTAMP
|
info[CACHE_EXPIRE_TIMESTAMP_STR] = int(time.time()) + EXPIRE_TIMESTAMP
|
||||||
elif int(time.time()) >= expire:
|
elif int(time.time()) >= expire:
|
||||||
ret = True
|
ret = True
|
||||||
if self._tmdb_cache_expire:
|
if self._douban_cache_expire:
|
||||||
new_meta_data.pop(k)
|
new_meta_data.pop(k)
|
||||||
else:
|
else:
|
||||||
count = 0
|
count = 0
|
||||||
@@ -206,30 +271,12 @@ class DoubanCache(metaclass=WeakSingleton):
|
|||||||
info[CACHE_EXPIRE_TIMESTAMP_STR] = int(time.time()) + EXPIRE_TIMESTAMP
|
info[CACHE_EXPIRE_TIMESTAMP_STR] = int(time.time()) + EXPIRE_TIMESTAMP
|
||||||
elif int(time.time()) >= expire:
|
elif int(time.time()) >= expire:
|
||||||
ret = True
|
ret = True
|
||||||
if self._tmdb_cache_expire:
|
if self._douban_cache_expire:
|
||||||
new_meta_data.pop(k)
|
new_meta_data.pop(k)
|
||||||
count += 1
|
count += 1
|
||||||
if count >= 5:
|
if count >= 5:
|
||||||
ret |= self._random_sample(new_meta_data)
|
ret |= self._random_sample(new_meta_data)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def get_title(self, key: str) -> Optional[str]:
|
|
||||||
"""
|
|
||||||
获取缓存的标题
|
|
||||||
"""
|
|
||||||
cache_media_info = self._meta_data.get(key)
|
|
||||||
if not cache_media_info or not cache_media_info.get("id"):
|
|
||||||
return None
|
|
||||||
return cache_media_info.get("title")
|
|
||||||
|
|
||||||
def set_title(self, key: str, cn_title: str) -> None:
|
|
||||||
"""
|
|
||||||
重新设置缓存标题
|
|
||||||
"""
|
|
||||||
cache_media_info = self._meta_data.get(key)
|
|
||||||
if not cache_media_info:
|
|
||||||
return
|
|
||||||
self._meta_data[key]['title'] = cn_title
|
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
self.save()
|
self.save()
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ import time
|
|||||||
import traceback
|
import traceback
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from threading import RLock
|
from threading import RLock
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
from app.core.meta import MetaBase
|
from app.core.meta import MetaBase
|
||||||
|
from app.helper.redis import RedisHelper
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.utils.singleton import WeakSingleton
|
|
||||||
from app.schemas.types import MediaType
|
from app.schemas.types import MediaType
|
||||||
|
from app.utils.singleton import WeakSingleton
|
||||||
|
|
||||||
lock = RLock()
|
lock = RLock()
|
||||||
|
|
||||||
@@ -32,8 +32,19 @@ class TmdbCache(metaclass=WeakSingleton):
|
|||||||
_tmdb_cache_expire: bool = True
|
_tmdb_cache_expire: bool = True
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
# 初始化Redis缓存助手
|
||||||
|
self._redis_helper = None
|
||||||
|
if settings.CACHE_BACKEND_TYPE == "redis":
|
||||||
|
try:
|
||||||
|
self._redis_helper = RedisHelper(redis_url=settings.CACHE_BACKEND_URL)
|
||||||
|
except RuntimeError as e:
|
||||||
|
logger.warning(f"TMDB缓存Redis初始化失败,将使用本地缓存: {e}")
|
||||||
|
self._redis_helper = None
|
||||||
|
|
||||||
|
# 加载缓存数据
|
||||||
self._meta_path = settings.TEMP_PATH / "__tmdb_cache__"
|
self._meta_path = settings.TEMP_PATH / "__tmdb_cache__"
|
||||||
self._meta_data = self.__load(self._meta_path)
|
if not self._redis_helper:
|
||||||
|
self._meta_data = self.__load(self._meta_path)
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
"""
|
"""
|
||||||
@@ -41,6 +52,13 @@ class TmdbCache(metaclass=WeakSingleton):
|
|||||||
"""
|
"""
|
||||||
with lock:
|
with lock:
|
||||||
self._meta_data = {}
|
self._meta_data = {}
|
||||||
|
# 如果Redis可用,同时清理Redis缓存
|
||||||
|
if self._redis_helper:
|
||||||
|
try:
|
||||||
|
self._redis_helper.clear(region="tmdb_cache")
|
||||||
|
logger.debug("已清理TMDB Redis缓存")
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"清理TMDB Redis缓存失败: {e}")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __get_key(meta: MetaBase) -> str:
|
def __get_key(meta: MetaBase) -> str:
|
||||||
@@ -54,16 +72,28 @@ class TmdbCache(metaclass=WeakSingleton):
|
|||||||
根据KEY值获取缓存值
|
根据KEY值获取缓存值
|
||||||
"""
|
"""
|
||||||
key = self.__get_key(meta)
|
key = self.__get_key(meta)
|
||||||
with lock:
|
|
||||||
info: dict = self._meta_data.get(key)
|
if self._redis_helper:
|
||||||
if info:
|
# 如果Redis可用,从Redis读取
|
||||||
expire = info.get(CACHE_EXPIRE_TIMESTAMP_STR)
|
try:
|
||||||
if not expire or int(time.time()) < expire:
|
redis_data = self._redis_helper.get(key, region="tmdb_cache")
|
||||||
info[CACHE_EXPIRE_TIMESTAMP_STR] = int(time.time()) + EXPIRE_TIMESTAMP
|
return redis_data or {}
|
||||||
self._meta_data[key] = info
|
except Exception as e:
|
||||||
elif expire and self._tmdb_cache_expire:
|
logger.warning(f"从Redis获取TMDB缓存失败: {e}")
|
||||||
self.delete(key)
|
else:
|
||||||
return info or {}
|
# Redis不可用时,从内存缓存读取
|
||||||
|
with lock:
|
||||||
|
info: dict = self._meta_data.get(key)
|
||||||
|
if info:
|
||||||
|
# 检查过期时间
|
||||||
|
expire = info.get(CACHE_EXPIRE_TIMESTAMP_STR)
|
||||||
|
if not expire or int(time.time()) < expire:
|
||||||
|
info[CACHE_EXPIRE_TIMESTAMP_STR] = int(time.time()) + EXPIRE_TIMESTAMP
|
||||||
|
self._meta_data[key] = info
|
||||||
|
elif expire and self._tmdb_cache_expire:
|
||||||
|
self.delete(key)
|
||||||
|
return info or {}
|
||||||
|
return {}
|
||||||
|
|
||||||
def delete(self, key: str) -> dict:
|
def delete(self, key: str) -> dict:
|
||||||
"""
|
"""
|
||||||
@@ -71,39 +101,44 @@ class TmdbCache(metaclass=WeakSingleton):
|
|||||||
@param key: 缓存key
|
@param key: 缓存key
|
||||||
@return: 被删除的缓存内容
|
@return: 被删除的缓存内容
|
||||||
"""
|
"""
|
||||||
with lock:
|
if self._redis_helper:
|
||||||
return self._meta_data.pop(key, {})
|
# 如果Redis可用,删除Redis缓存
|
||||||
|
try:
|
||||||
def delete_by_tmdbid(self, tmdbid: int) -> None:
|
self._redis_helper.delete(key, region="tmdb_cache")
|
||||||
"""
|
return {}
|
||||||
清空对应TMDBID的所有缓存记录,以强制更新TMDB中最新的数据
|
except Exception as e:
|
||||||
"""
|
logger.warning(f"删除TMDB Redis缓存失败: {e}")
|
||||||
for key in list(self._meta_data):
|
return {}
|
||||||
if self._meta_data.get(key, {}).get("id") == tmdbid:
|
else:
|
||||||
with lock:
|
# Redis不可用时,删除内存缓存
|
||||||
self._meta_data.pop(key)
|
with lock:
|
||||||
|
return self._meta_data.pop(key, {})
|
||||||
def delete_unknown(self) -> None:
|
|
||||||
"""
|
|
||||||
清除未识别的缓存记录,以便重新搜索TMDB
|
|
||||||
"""
|
|
||||||
for key in list(self._meta_data):
|
|
||||||
if self._meta_data.get(key, {}).get("id") == 0:
|
|
||||||
with lock:
|
|
||||||
self._meta_data.pop(key)
|
|
||||||
|
|
||||||
def modify(self, key: str, title: str) -> dict:
|
def modify(self, key: str, title: str) -> dict:
|
||||||
"""
|
"""
|
||||||
删除缓存信息
|
修改缓存信息
|
||||||
@param key: 缓存key
|
@param key: 缓存key
|
||||||
@param title: 标题
|
@param title: 标题
|
||||||
@return: 被修改后缓存内容
|
@return: 被修改后缓存内容
|
||||||
"""
|
"""
|
||||||
with lock:
|
if self._redis_helper:
|
||||||
if self._meta_data.get(key):
|
# 如果Redis可用,修改Redis缓存
|
||||||
self._meta_data[key]['title'] = title
|
try:
|
||||||
self._meta_data[key][CACHE_EXPIRE_TIMESTAMP_STR] = int(time.time()) + EXPIRE_TIMESTAMP
|
redis_data = self._redis_helper.get(key, region="tmdb_cache")
|
||||||
return self._meta_data.get(key)
|
if redis_data:
|
||||||
|
redis_data['title'] = title
|
||||||
|
self._redis_helper.set(key, redis_data, ttl=EXPIRE_TIMESTAMP, region="tmdb_cache")
|
||||||
|
return redis_data
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"修改TMDB Redis缓存失败: {e}")
|
||||||
|
return {}
|
||||||
|
else:
|
||||||
|
# Redis不可用时,修改内存缓存
|
||||||
|
with lock:
|
||||||
|
if self._meta_data.get(key):
|
||||||
|
self._meta_data[key]['title'] = title
|
||||||
|
self._meta_data[key][CACHE_EXPIRE_TIMESTAMP_STR] = int(time.time()) + EXPIRE_TIMESTAMP
|
||||||
|
return self._meta_data.get(key)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __load(path: Path) -> dict:
|
def __load(path: Path) -> dict:
|
||||||
@@ -115,43 +150,72 @@ class TmdbCache(metaclass=WeakSingleton):
|
|||||||
with open(path, 'rb') as f:
|
with open(path, 'rb') as f:
|
||||||
data = pickle.load(f)
|
data = pickle.load(f)
|
||||||
return data
|
return data
|
||||||
return {}
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f'加载缓存失败:{str(e)} - {traceback.format_exc()}')
|
logger.error(f'加载缓存失败:{str(e)} - {traceback.format_exc()}')
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def update(self, meta: MetaBase, info: dict) -> None:
|
def update(self, meta: MetaBase, info: dict) -> None:
|
||||||
"""
|
"""
|
||||||
新增或更新缓存条目
|
新增或更新缓存条目
|
||||||
"""
|
"""
|
||||||
with lock:
|
if info:
|
||||||
if info:
|
# 缓存标题
|
||||||
# 缓存标题
|
cache_title = info.get("title") \
|
||||||
cache_title = info.get("title") \
|
if info.get("media_type") == MediaType.MOVIE else info.get("name")
|
||||||
if info.get("media_type") == MediaType.MOVIE else info.get("name")
|
# 缓存年份
|
||||||
# 缓存年份
|
cache_year = info.get('release_date') \
|
||||||
cache_year = info.get('release_date') \
|
if info.get("media_type") == MediaType.MOVIE else info.get('first_air_date')
|
||||||
if info.get("media_type") == MediaType.MOVIE else info.get('first_air_date')
|
if cache_year:
|
||||||
if cache_year:
|
cache_year = cache_year[:4]
|
||||||
cache_year = cache_year[:4]
|
|
||||||
self._meta_data[self.__get_key(meta)] = {
|
if self._redis_helper:
|
||||||
|
# 如果Redis可用,保存到Redis
|
||||||
|
cache_data = {
|
||||||
"id": info.get("id"),
|
"id": info.get("id"),
|
||||||
"type": info.get("media_type"),
|
"type": info.get("media_type"),
|
||||||
"year": cache_year,
|
"year": cache_year,
|
||||||
"title": cache_title,
|
"title": cache_title,
|
||||||
"poster_path": info.get("poster_path"),
|
"poster_path": info.get("poster_path"),
|
||||||
"backdrop_path": info.get("backdrop_path"),
|
"backdrop_path": info.get("backdrop_path")
|
||||||
CACHE_EXPIRE_TIMESTAMP_STR: int(time.time()) + EXPIRE_TIMESTAMP
|
|
||||||
}
|
}
|
||||||
elif info is not None:
|
try:
|
||||||
# None时不缓存,此时代表网络错误,允许重复请求
|
self._redis_helper.set(self.__get_key(meta), cache_data, ttl=EXPIRE_TIMESTAMP, region="tmdb_cache")
|
||||||
self._meta_data[self.__get_key(meta)] = {'id': 0}
|
except Exception as e:
|
||||||
|
logger.warning(f"保存TMDB缓存到Redis失败: {e}")
|
||||||
|
else:
|
||||||
|
# Redis不可用时,保存到内存缓存
|
||||||
|
with lock:
|
||||||
|
cache_data = {
|
||||||
|
"id": info.get("id"),
|
||||||
|
"type": info.get("media_type"),
|
||||||
|
"year": cache_year,
|
||||||
|
"title": cache_title,
|
||||||
|
"poster_path": info.get("poster_path"),
|
||||||
|
"backdrop_path": info.get("backdrop_path"),
|
||||||
|
CACHE_EXPIRE_TIMESTAMP_STR: int(time.time()) + EXPIRE_TIMESTAMP
|
||||||
|
}
|
||||||
|
self._meta_data[self.__get_key(meta)] = cache_data
|
||||||
|
|
||||||
|
elif info is not None:
|
||||||
|
# None时不缓存,此时代表网络错误,允许重复请求
|
||||||
|
if self._redis_helper:
|
||||||
|
try:
|
||||||
|
self._redis_helper.set(self.__get_key(meta), {'id': 0}, ttl=EXPIRE_TIMESTAMP, region="tmdb_cache")
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"保存TMDB缓存到Redis失败: {e}")
|
||||||
|
else:
|
||||||
|
with lock:
|
||||||
|
self._meta_data[self.__get_key(meta)] = {'id': 0}
|
||||||
|
|
||||||
def save(self, force: bool = False) -> None:
|
def save(self, force: bool = False) -> None:
|
||||||
"""
|
"""
|
||||||
保存缓存数据到文件
|
保存缓存数据到文件
|
||||||
"""
|
"""
|
||||||
|
# 如果Redis可用,不需要保存到本地文件
|
||||||
|
if self._redis_helper:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Redis不可用时,保存到本地文件
|
||||||
meta_data = self.__load(self._meta_path)
|
meta_data = self.__load(self._meta_path)
|
||||||
new_meta_data = {k: v for k, v in self._meta_data.items() if v.get("id")}
|
new_meta_data = {k: v for k, v in self._meta_data.items() if v.get("id")}
|
||||||
|
|
||||||
@@ -198,23 +262,5 @@ class TmdbCache(metaclass=WeakSingleton):
|
|||||||
ret |= self._random_sample(new_meta_data)
|
ret |= self._random_sample(new_meta_data)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def get_title(self, key: str) -> Optional[str]:
|
|
||||||
"""
|
|
||||||
获取缓存的标题
|
|
||||||
"""
|
|
||||||
cache_media_info = self._meta_data.get(key)
|
|
||||||
if not cache_media_info or not cache_media_info.get("id"):
|
|
||||||
return None
|
|
||||||
return cache_media_info.get("title")
|
|
||||||
|
|
||||||
def set_title(self, key: str, cn_title: str) -> None:
|
|
||||||
"""
|
|
||||||
重新设置缓存标题
|
|
||||||
"""
|
|
||||||
cache_media_info = self._meta_data.get(key)
|
|
||||||
if not cache_media_info:
|
|
||||||
return
|
|
||||||
self._meta_data[key]['title'] = cn_title
|
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
self.save()
|
self.save()
|
||||||
|
|||||||
Reference in New Issue
Block a user