mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-08-11 00:25:36 +08:00
feat(storages): 新增 AList 存储类型 (#6245)
This commit is contained in:
@@ -1274,9 +1274,17 @@ def cached(region: Optional[str] = None, maxsize: Optional[int] = 1024, ttl: Opt
|
||||
cache_key, cached_value, cache_region
|
||||
)
|
||||
|
||||
async def cache_delete(*args, **kwargs) -> None:
|
||||
"""
|
||||
删除当前参数对应的缓存。
|
||||
"""
|
||||
cache_key = __get_cache_key(args, kwargs)
|
||||
await cache_backend.delete(cache_key, region=cache_region)
|
||||
|
||||
async_wrapper.cache_region = cache_region
|
||||
async_wrapper.cache_clear = cache_clear
|
||||
async_wrapper.cache_exists = cache_exists
|
||||
async_wrapper.cache_delete = cache_delete
|
||||
return async_wrapper
|
||||
else:
|
||||
# 同步函数使用同步缓存后端
|
||||
@@ -1317,9 +1325,17 @@ def cached(region: Optional[str] = None, maxsize: Optional[int] = 1024, ttl: Opt
|
||||
cache_key, cached_value, cache_region
|
||||
)
|
||||
|
||||
def cache_delete(*args, **kwargs) -> None:
|
||||
"""
|
||||
删除当前参数对应的缓存。
|
||||
"""
|
||||
cache_key = __get_cache_key(args, kwargs)
|
||||
cache_backend.delete(cache_key, region=cache_region)
|
||||
|
||||
wrapper.cache_region = cache_region
|
||||
wrapper.cache_clear = cache_clear
|
||||
wrapper.cache_exists = cache_exists
|
||||
wrapper.cache_delete = cache_delete
|
||||
return wrapper
|
||||
|
||||
return decorator
|
||||
|
||||
@@ -47,7 +47,10 @@ class Alist(StorageBase, metaclass=WeakSingleton):
|
||||
"""
|
||||
初始化
|
||||
"""
|
||||
self.__generate_token.cache_clear() # noqa
|
||||
conf = self.get_conf()
|
||||
self.__login_token.cache_delete( # noqa
|
||||
self, self.__get_base_url, conf.get("username"), conf.get("password")
|
||||
)
|
||||
|
||||
def _delay_get_item(
|
||||
self, path: Path, /, refresh: bool = False
|
||||
@@ -117,22 +120,32 @@ class Alist(StorageBase, metaclass=WeakSingleton):
|
||||
"""
|
||||
return self.__generate_token()
|
||||
|
||||
@cached(maxsize=1, ttl=60 * 60 * 24 * 2 - 60 * 5, skip_empty=True)
|
||||
def __generate_token(self) -> str:
|
||||
"""
|
||||
如果设置永久令牌则返回永久令牌,否则使用账号密码生成一个临时 token
|
||||
缓存2天,提前5分钟更新
|
||||
"""
|
||||
conf = self.get_conf()
|
||||
token = conf.get("token")
|
||||
if token:
|
||||
return str(token)
|
||||
return self.__login_token(
|
||||
self.__get_base_url, conf.get("username"), conf.get("password")
|
||||
)
|
||||
|
||||
@cached(maxsize=8, ttl=60 * 60 * 24 * 2 - 60 * 5, skip_empty=True)
|
||||
def __login_token(
|
||||
self, base_url: str, username: Optional[str], password: Optional[str]
|
||||
) -> str:
|
||||
"""
|
||||
使用账号密码生成一个临时 token
|
||||
缓存2天,提前5分钟更新
|
||||
"""
|
||||
resp = RequestUtils(headers={"Content-Type": "application/json"}).post_res(
|
||||
self.__get_api_url("/api/auth/login"),
|
||||
UrlUtils.adapt_request_url(base_url, "/api/auth/login"),
|
||||
data=json.dumps(
|
||||
{
|
||||
"username": conf.get("username"),
|
||||
"password": conf.get("password"),
|
||||
"username": username,
|
||||
"password": password,
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
12
app/modules/filemanager/storages/alistgo.py
Normal file
12
app/modules/filemanager/storages/alistgo.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from app.modules.filemanager.storages.alist import Alist
|
||||
from app.schemas.types import StorageSchema
|
||||
|
||||
|
||||
class AlistGo(Alist):
|
||||
"""
|
||||
AList相关操作
|
||||
|
||||
API 文档:https://docs.alistgo.com/
|
||||
"""
|
||||
|
||||
schema = StorageSchema.AlistGo
|
||||
@@ -413,6 +413,7 @@ class StorageSchema(Enum):
|
||||
U115 = "u115"
|
||||
Rclone = "rclone"
|
||||
Alist = "alist"
|
||||
AlistGo = "alistgo"
|
||||
SMB = "smb"
|
||||
|
||||
|
||||
|
||||
94
tests/test_alistgo_storage.py
Normal file
94
tests/test_alistgo_storage.py
Normal file
@@ -0,0 +1,94 @@
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from app.modules.filemanager.storages import alist as alist_module
|
||||
from app.modules.filemanager.storages.alist import Alist
|
||||
from app.modules.filemanager.storages.alistgo import AlistGo
|
||||
from app.schemas.types import StorageSchema
|
||||
|
||||
|
||||
class _FakeResponse:
|
||||
def __init__(self, payload: dict, status_code: int = 200):
|
||||
self._payload = payload
|
||||
self.status_code = status_code
|
||||
|
||||
def json(self):
|
||||
return self._payload
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def clear_token_cache():
|
||||
Alist._Alist__login_token.cache_clear() # noqa
|
||||
yield
|
||||
Alist._Alist__login_token.cache_clear() # noqa
|
||||
|
||||
|
||||
def test_alistgo_schema_registered():
|
||||
assert AlistGo.schema == StorageSchema.AlistGo
|
||||
assert StorageSchema.AlistGo.value == "alistgo"
|
||||
|
||||
|
||||
def test_alistgo_singleton_isolated_from_alist():
|
||||
alist = Alist()
|
||||
alistgo = AlistGo()
|
||||
assert alistgo is not alist
|
||||
assert isinstance(alistgo, Alist)
|
||||
|
||||
|
||||
def test_alistgo_token_isolated_from_alist(clear_token_cache):
|
||||
def _conf(storage):
|
||||
return {
|
||||
"url": f"http://{storage.schema.value}.test",
|
||||
"username": "user",
|
||||
"password": "pass",
|
||||
}
|
||||
|
||||
responses = [
|
||||
_FakeResponse({"code": 200, "message": "success", "data": {"token": "token-alist"}}),
|
||||
_FakeResponse({"code": 200, "message": "success", "data": {"token": "token-alistgo"}}),
|
||||
]
|
||||
request_utils = MagicMock()
|
||||
request_utils.post_res.side_effect = responses
|
||||
|
||||
alist = Alist()
|
||||
alistgo = AlistGo()
|
||||
with patch.object(Alist, "get_conf", _conf):
|
||||
with patch.object(alist_module, "RequestUtils", return_value=request_utils):
|
||||
assert alist._Alist__generate_token() == "token-alist" # noqa
|
||||
assert alistgo._Alist__generate_token() == "token-alistgo" # noqa
|
||||
assert alist._Alist__generate_token() == "token-alist" # noqa
|
||||
assert alistgo._Alist__generate_token() == "token-alistgo" # noqa
|
||||
|
||||
assert request_utils.post_res.call_count == 2
|
||||
|
||||
|
||||
def test_init_storage_keeps_other_storage_token(clear_token_cache):
|
||||
def _conf(storage):
|
||||
return {
|
||||
"url": f"http://{storage.schema.value}.test",
|
||||
"username": "user",
|
||||
"password": "pass",
|
||||
}
|
||||
|
||||
responses = [
|
||||
_FakeResponse({"code": 200, "message": "success", "data": {"token": "token-alist"}}),
|
||||
_FakeResponse({"code": 200, "message": "success", "data": {"token": "token-alistgo"}}),
|
||||
_FakeResponse({"code": 200, "message": "success", "data": {"token": "token-alistgo-new"}}),
|
||||
]
|
||||
request_utils = MagicMock()
|
||||
request_utils.post_res.side_effect = responses
|
||||
|
||||
alist = Alist()
|
||||
alistgo = AlistGo()
|
||||
with patch.object(Alist, "get_conf", _conf):
|
||||
with patch.object(alist_module, "RequestUtils", return_value=request_utils):
|
||||
assert alist._Alist__generate_token() == "token-alist" # noqa
|
||||
assert alistgo._Alist__generate_token() == "token-alistgo" # noqa
|
||||
|
||||
alistgo.init_storage()
|
||||
|
||||
assert alist._Alist__generate_token() == "token-alist" # noqa
|
||||
assert alistgo._Alist__generate_token() == "token-alistgo-new" # noqa
|
||||
|
||||
assert request_utils.post_res.call_count == 3
|
||||
Reference in New Issue
Block a user