diff --git a/alist_sync/config.py b/alist_sync/config.py index 3648d95..1fc7290 100644 --- a/alist_sync/config.py +++ b/alist_sync/config.py @@ -1,4 +1,165 @@ +import builtins +import json +import os +from datetime import datetime from pathlib import Path +from functools import cached_property +from typing import Optional -cache_dir = Path(__file__).parent / ".cache" # 缓存目录 -cache_dir.mkdir(exist_ok=True, parents=True) +from alist_sdk import AlistPathType +from pydantic import Field, BaseModel +from pymongo.database import Database + + +def create_config(): + """创建配置文件""" + if hasattr(builtins, "sync_config"): + return builtins.sync_config + + config_file = os.getenv( + "ALIST_SYNC_CONFIG", Path(__file__).parent.parent / "config.yaml" + ) + + sync_config = Config.load_from_yaml(config_file) + setattr(builtins, "sync_config", sync_config) + return sync_config + + +class AlistServer(BaseModel): + """""" + + base_url: str = "http://localhost:5244" + username: Optional[str] = "" + password: Optional[str] = "" + token: Optional[str] = None + has_opt: Optional[bool] = False + + max_connect: int = 30 # 最大同时连接数 + storage_config: Optional[Path] = None + + # httpx 的参数 + verify: Optional[bool] = True + headers: Optional[dict] = None + + def dump_for_sdk(self): + return self.model_dump(exclude={"storage_config"}) + + def storages(self) -> list[dict]: + """返回给定的 storage_config 中包含的storages""" + + def is_storage(_st): + if not isinstance(_st, dict): + return False + if "mount_path" in _st and "driver" in _st: + return True + return False + + if not self.storage_config or self.storage_config == Path(): + return [] + if not self.storage_config.exists(): + raise FileNotFoundError(f"找不到文件:{self.storage_config}") + + _load_storages = json.load(self.storage_config.open()) + if isinstance(_load_storages, list): + _load_storages = [_s for _s in _load_storages if is_storage(_s)] + if _load_storages: + return _load_storages + raise KeyError() + + if isinstance(_load_storages, dict): + if "storages" in _load_storages: + _load_storages = [ + _s for _s in _load_storages["storages"] if is_storage(_s) + ] + if _load_storages: + return _load_storages + raise KeyError() + if is_storage(_load_storages): + return [ + _load_storages, + ] + raise KeyError("给定的") + + +class SyncGroup(BaseModel): + name: str + type: str + group: list[AlistPathType] = Field(min_length=2) + + +class Config(BaseModel): + """配置""" + + _id: str = "alist-sync-config" + + cache__dir: Path = Field( + default=os.getenv("ALIST_SYNC_CACHE_DIR") or Path(__file__).parent / ".cache", + alias="cache_dir", + ) + + daemon: bool = os.getenv("ALIST_SYNC_DAEMON", "false").lower() in ( + "true", + "1", + "yes", + "on", + "y", + "t", + "1", + ) + + mongodb_uri: str | None = os.getenv("ALIST_SYNC_MONGODB_URI", None) + + alist_servers: list[AlistServer] = [] + sync_groups: list[SyncGroup] = [] + + create_time: datetime = datetime.now() + + @cached_property + def cache_dir(self) -> Path: + self.cache__dir.mkdir(exist_ok=True, parents=True) + return self.cache__dir + + @cached_property + def mongodb(self) -> "Database": + from pymongo import MongoClient + from pymongo.server_api import ServerApi + + db = MongoClient( + self.mongodb_uri, server_api=ServerApi("1") + ).get_default_database() + if db is None: + raise ValueError("连接数据库失败") + + return db + + @classmethod + def load_from_yaml(cls, file: Path) -> "Config": + from yaml import safe_load + + return cls.model_validate(safe_load(file.open("rb"))) + + def dump_to_yaml(self, file: Path): + from yaml import safe_dump + + return safe_dump(self.model_dump(mode="json"), file.open("wb")) + + def load_from_mongo(self, uri: str = None): + from pymongo import MongoClient + + col = MongoClient(uri).get_default_database()["config"] + data = col.find_one({"_id": self._id}) + return self.model_validate(data) + + def dump_to_mongodb(self): + return self.mongodb["config"].update_one( + {"_id": self._id}, + {"$set": self.model_dump(mode="json")}, + True, + ) + + +if __name__ == "__main__": + config = create_config() + print(config) + print(config.cache_dir) + print(config.mongodb) diff --git a/alist_sync/d_checker.py b/alist_sync/d_checker.py new file mode 100644 index 0000000..5344e82 --- /dev/null +++ b/alist_sync/d_checker.py @@ -0,0 +1,8 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +""" +@File Name : d_checker.py +@Author : LeeCQ +@Date-Time : 2024/2/25 21:17 + +""" diff --git a/alist_sync/downloader.py b/alist_sync/downloader.py new file mode 100644 index 0000000..553a5d5 --- /dev/null +++ b/alist_sync/downloader.py @@ -0,0 +1,8 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +""" +@File Name : downloader.py +@Author : LeeCQ +@Date-Time : 2024/2/25 21:17 + +""" diff --git a/alist_sync/models.py b/alist_sync/models.py index 6846163..2b07496 100644 --- a/alist_sync/models.py +++ b/alist_sync/models.py @@ -1,17 +1,17 @@ -import datetime -import json -from pathlib import PurePosixPath, Path -from typing import Optional +# 用于定义模型 +from pathlib import Path from pydantic import BaseModel as _BaseModel +from alist_sync.config import create_config + +config = create_config() + + __all__ = [ "BaseModel", - "AlistServer", ] -from alist_sync.config import cache_dir - class BaseModel(_BaseModel): """基础模型""" @@ -26,79 +26,10 @@ def from_json_file(cls, file: Path): @classmethod def from_cache(cls): class_name = cls.__name__ - file = cache_dir.joinpath(f"{class_name}.json") + file = config.cache_dir.joinpath(f"{class_name}.json") return cls.from_json_file(file) def save_to_cache(self): class_name = self.__class__.__name__ - file = cache_dir.joinpath(f"{class_name}.json") + file = config.cache_dir.joinpath(f"{class_name}.json") file.write_text(self.model_dump_json(indent=2), encoding="utf-8") - - -class AlistServer(BaseModel): - """""" - - base_url: str = "http://localhost:5244" - username: Optional[str] = "" - password: Optional[str] = "" - token: Optional[str] = None - has_opt: Optional[bool] = False - - max_connect: int = 30 # 最大同时连接数 - storage_config: Optional[Path] = None - - # httpx 的参数 - verify: Optional[bool] = True - headers: Optional[dict] = None - - def dump_for_sdk(self): - return self.model_dump(exclude={"storage_config"}) - - def storages(self) -> list[dict]: - """返回给定的 storage_config 中包含的storages""" - - def is_storage(_st): - if not isinstance(_st, dict): - return False - if "mount_path" in _st and "driver" in _st: - return True - return False - - if not self.storage_config or self.storage_config == Path(): - return [] - if not self.storage_config.exists(): - raise FileNotFoundError(f"找不到文件:{self.storage_config}") - - _load_storages = json.load(self.storage_config.open()) - if isinstance(_load_storages, list): - _load_storages = [_s for _s in _load_storages if is_storage(_s)] - if _load_storages: - return _load_storages - raise KeyError() - - if isinstance(_load_storages, dict): - if "storages" in _load_storages: - _load_storages = [ - _s for _s in _load_storages["storages"] if is_storage(_s) - ] - if _load_storages: - return _load_storages - raise KeyError() - if is_storage(_load_storages): - return [ - _load_storages, - ] - raise KeyError("给定的") - - -class Config(BaseModel): - """配置""" - - name: str - alist_info: AlistServer - config_dir: PurePosixPath - cache_dir: Path - sync_group: list[str] - - update_time: datetime.datetime - create_time: datetime.datetime diff --git a/config.yaml b/config.yaml new file mode 100644 index 0000000..2872313 --- /dev/null +++ b/config.yaml @@ -0,0 +1,29 @@ +mongodb_uri: "mongodb+srv://${username}:${password}@${host}/alist_sync?retryWrites=true&w=majority&appName=A1" +cache_dir: ./.cache +daemon: false + +alist_servers: + - url: http://localhost:5244/ + username: "admin" + password: "admin" + verify_ssl: false + - url: http://localhost:5245/ + username: "admin" + password: "admin" + + +sync_groups: + - name: "sync1" + # 同步类型,一共5种: + # 1 copy:如果目标目录中已经存在该文件,则跳过 + # 忽略存在与目标目录中但不存在于源目录中的文件 + # 2 mirror: 如果目标目录中已经存在该文件,则跳过 + # 删除存在于目录目录但不存在于源目录中的文件 + # 3 sync: 如果目标目录中已经存在该文件,则跳过 + # 删除存在于目录目录但不存在于源目录中的文件 + # 4 sync-incr: 基于文件的修改时间,只同步源目录中修改时间晚于目标目录的文件 + type: "copy" + group: + - "http://localhost:5244/test1" + - "http://localhost:5245/test2" +