133 lines
4.3 KiB
Python
133 lines
4.3 KiB
Python
|
"""Redis客户端管理"""
|
|||
|
from typing import Optional
|
|||
|
import redis
|
|||
|
from redis.connection import ConnectionPool
|
|||
|
from config import REDIS_CONFIG, RedisKeys
|
|||
|
|
|||
|
class RedisClient:
|
|||
|
"""Redis客户端管理器,使用连接池"""
|
|||
|
_pools: dict[int, ConnectionPool] = {}
|
|||
|
_instance: Optional['RedisClient'] = None
|
|||
|
|
|||
|
def __new__(cls) -> 'RedisClient':
|
|||
|
"""确保单例"""
|
|||
|
if cls._instance is None:
|
|||
|
cls._instance = super().__new__(cls)
|
|||
|
return cls._instance
|
|||
|
|
|||
|
def __init__(self):
|
|||
|
"""初始化客户端管理器"""
|
|||
|
if not hasattr(self, '_initialized'):
|
|||
|
self._initialized = True
|
|||
|
self._current_db = 0
|
|||
|
self._redis: Optional[redis.Redis] = None
|
|||
|
self._init_connection()
|
|||
|
|
|||
|
def _get_pool(self, db: int) -> ConnectionPool:
|
|||
|
"""获取指定数据库的连接池"""
|
|||
|
if db not in self._pools:
|
|||
|
self._pools[db] = redis.ConnectionPool(
|
|||
|
host=REDIS_CONFIG.host,
|
|||
|
port=REDIS_CONFIG.port,
|
|||
|
db=db,
|
|||
|
max_connections=REDIS_CONFIG.max_connections,
|
|||
|
decode_responses=True
|
|||
|
)
|
|||
|
return self._pools[db]
|
|||
|
|
|||
|
def _init_connection(self) -> None:
|
|||
|
"""初始化当前数据库的连接"""
|
|||
|
self._redis = redis.Redis(
|
|||
|
connection_pool=self._get_pool(self._current_db)
|
|||
|
)
|
|||
|
|
|||
|
def select_db(self, db: int) -> bool:
|
|||
|
"""
|
|||
|
切换到指定数据库
|
|||
|
|
|||
|
参数:
|
|||
|
db: 数据库编号
|
|||
|
|
|||
|
返回:
|
|||
|
bool: 成功返回True,失败返回False
|
|||
|
"""
|
|||
|
min_db, max_db = REDIS_CONFIG.db_range
|
|||
|
if not min_db <= db <= max_db:
|
|||
|
return False
|
|||
|
|
|||
|
if db != self._current_db:
|
|||
|
self._current_db = db
|
|||
|
self._init_connection()
|
|||
|
return True
|
|||
|
|
|||
|
@property
|
|||
|
def client(self) -> redis.Redis:
|
|||
|
"""获取当前Redis客户端"""
|
|||
|
return self._redis
|
|||
|
|
|||
|
def get_cookie(self) -> Optional[str]:
|
|||
|
"""获取存储的Pixiv cookie"""
|
|||
|
return self._redis.get(RedisKeys.COOKIE)
|
|||
|
|
|||
|
def set_cookie(self, cookie: str) -> None:
|
|||
|
"""存储Pixiv cookie"""
|
|||
|
self._redis.set(RedisKeys.COOKIE, cookie)
|
|||
|
|
|||
|
def is_image_downloaded(self, pid: str, page: int) -> bool:
|
|||
|
"""检查特定图片页是否已下载"""
|
|||
|
key = RedisKeys.DOWNLOADED_IMAGE.format(pid=pid, page=page)
|
|||
|
return self._redis.get(key) == 'true'
|
|||
|
|
|||
|
def mark_image_downloaded(self, pid: str, page: int) -> None:
|
|||
|
"""标记特定图片页为已下载"""
|
|||
|
key = RedisKeys.DOWNLOADED_IMAGE.format(pid=pid, page=page)
|
|||
|
self._redis.set(key, 'true')
|
|||
|
|
|||
|
def is_work_complete(self, pid: str) -> bool:
|
|||
|
"""检查作品是否已完全下载"""
|
|||
|
key = RedisKeys.DOWNLOADED_WORK.format(pid=pid)
|
|||
|
return self._redis.get(key) == 'complete'
|
|||
|
|
|||
|
def mark_work_complete(self, pid: str) -> None:
|
|||
|
"""标记作品为已完全下载"""
|
|||
|
key = RedisKeys.DOWNLOADED_WORK.format(pid=pid)
|
|||
|
self._redis.set(key, 'complete')
|
|||
|
|
|||
|
def get_total_pages(self, pid: str) -> Optional[int]:
|
|||
|
"""获取作品总页数"""
|
|||
|
key = RedisKeys.TOTAL_PAGES.format(pid=pid)
|
|||
|
value = self._redis.get(key)
|
|||
|
return int(value) if value else None
|
|||
|
|
|||
|
def set_total_pages(self, pid: str, total: int) -> None:
|
|||
|
"""设置作品总页数"""
|
|||
|
key = RedisKeys.TOTAL_PAGES.format(pid=pid)
|
|||
|
self._redis.set(key, str(total))
|
|||
|
|
|||
|
def store_user_id(self, illust_id: str, user_id: str) -> None:
|
|||
|
"""存储作品作者ID"""
|
|||
|
key = RedisKeys.USER_ID.format(illust_id=illust_id)
|
|||
|
self._redis.set(key, user_id)
|
|||
|
|
|||
|
def get_db_stats(self) -> tuple[int, list[str]]:
|
|||
|
"""
|
|||
|
获取当前数据库统计信息
|
|||
|
|
|||
|
返回:
|
|||
|
tuple: (作品数量, 作品ID列表)
|
|||
|
"""
|
|||
|
pattern = RedisKeys.DOWNLOADED_IMAGE.format(pid='*', page='0')
|
|||
|
work_keys = self._redis.keys(pattern)
|
|||
|
work_ids = [key.split(':')[1].split('_')[0] for key in work_keys]
|
|||
|
return len(work_ids), work_ids
|
|||
|
|
|||
|
def clear_db(self) -> None:
|
|||
|
"""清空当前数据库"""
|
|||
|
self._redis.flushdb()
|
|||
|
|
|||
|
def close(self) -> None:
|
|||
|
"""关闭所有连接池"""
|
|||
|
for pool in self._pools.values():
|
|||
|
pool.disconnect()
|
|||
|
self._pools.clear()
|