File size: 6,156 Bytes
b6db694
 
27beae4
b6db694
454f1d5
27beae4
b6db694
 
 
27beae4
 
b6db694
 
 
27beae4
b6db694
cc826a1
b6db694
 
 
 
 
27beae4
b6db694
 
 
 
 
27beae4
 
b6db694
 
 
27beae4
 
 
 
 
 
 
 
b6db694
 
 
 
27beae4
b6db694
 
 
27beae4
b6db694
 
27beae4
b6db694
 
 
27beae4
b6db694
 
 
 
 
 
 
 
 
 
27beae4
b6db694
27beae4
b6db694
 
 
 
 
 
 
 
 
 
27beae4
b6db694
27beae4
b6db694
 
 
 
27beae4
b6db694
 
 
 
 
 
27beae4
b6db694
 
27beae4
 
 
 
 
 
 
 
b6db694
 
 
 
27beae4
b6db694
 
27beae4
 
 
 
 
b6db694
27beae4
 
 
b6db694
27beae4
b6db694
27beae4
b6db694
27beae4
b6db694
 
 
 
 
27beae4
b6db694
27beae4
b6db694
 
27beae4
b6db694
27beae4
b6db694
 
 
 
 
27beae4
b6db694
 
 
27beae4
b6db694
 
 
27beae4
b6db694
27beae4
b6db694
 
 
 
27beae4
 
 
 
 
 
cc826a1
27beae4
 
b6db694
27beae4
b6db694
 
 
 
 
27beae4
b6db694
 
 
 
 
 
27beae4
b6db694
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
import os
import asyncio
import logging
import time
import aiofiles
from datetime import datetime
from pathlib import Path
from typing import Dict, List, Optional

logger = logging.getLogger(__name__)


class FileCachePlugin:
    """文件缓存插件"""

    def __init__(self):
        self.name = "cache"
        self.enabled = False
        self.cache_dir = None
        self.temp_dir = None
        self.permanent_dir = None
        self.file_metadata = {}  # 存储文件元数据:{file_id: {"name": str, "type": str, "created": timestamp, "is_temp": bool}}

    def on_enable(self):
        """插件启用时调用"""
        self.enabled = True
        self._init_directories()
        self._load_metadata()
        logger.info(f"{self.name} 已启用")

    def on_disable(self):
        """插件禁用时调用"""
        self.enabled = False
        try:
            from . import api

            api.stop_cleanup_task()
        except Exception as e:
            logger.error(f"停止清理任务失败: {e}")
        logger.info(f"{self.name} 已禁用")

    def _init_directories(self):
        """初始化缓存目录"""
        plugin_dir = Path(__file__).parent / "cache"
        plugin_dir.mkdir(exist_ok=True)

        self.cache_dir = plugin_dir
        self.temp_dir = plugin_dir / "temp"
        self.permanent_dir = plugin_dir / "permanent"

        self.temp_dir.mkdir(exist_ok=True)
        self.permanent_dir.mkdir(exist_ok=True)

    def _load_metadata(self):
        """从缓存目录加载文件元数据"""
        self.file_metadata = {}

        # 加载临时文件
        if self.temp_dir.exists():
            for file_path in self.temp_dir.iterdir():
                if file_path.is_file():
                    file_id = file_path.stem
                    stat = file_path.stat()
                    self.file_metadata[file_id] = {
                        "name": file_path.name,
                        "type": "temp",
                        "created": stat.st_mtime,
                        "path": str(file_path),
                    }

        # 加载永久文件
        if self.permanent_dir.exists():
            for file_path in self.permanent_dir.iterdir():
                if file_path.is_file():
                    file_id = file_path.stem
                    stat = file_path.stat()
                    self.file_metadata[file_id] = {
                        "name": file_path.name,
                        "type": "permanent",
                        "created": stat.st_mtime,
                        "path": str(file_path),
                    }

    async def cleanup_expired_files(self):
        """清理超过8小时的临时文件"""
        current_time = time.time()
        expired_files = []

        for file_id, metadata in list(self.file_metadata.items()):
            if metadata["type"] == "temp":
                file_age = current_time - metadata["created"]
                # 8小时 = 28800秒
                if file_age > 28800:
                    expired_files.append(file_id)

        for file_id in expired_files:
            await self.delete_file(file_id)

    async def save_file(
        self,
        file_id: str,
        file_content: bytes,
        original_filename: str,
        is_temp: bool = True,
    ) -> bool:
        """保存文件到缓存目录"""
        try:
            target_dir = self.temp_dir if is_temp else self.permanent_dir
            file_path = target_dir / original_filename

            # 如果文件已存在,添加时间戳以避免重名
            if file_path.exists():
                name, suffix = (
                    original_filename.rsplit(".", 1)
                    if "." in original_filename
                    else (original_filename, "")
                )
                timestamp = int(time.time() * 1000)
                original_filename = (
                    f"{name}_{timestamp}.{suffix}" if suffix else f"{name}_{timestamp}"
                )
                file_path = target_dir / original_filename

            # 保存文件
            async with aiofiles.open(file_path, "wb") as f:
                await f.write(file_content)

            # 记录元数据
            self.file_metadata[file_id] = {
                "name": original_filename,
                "type": "temp" if is_temp else "permanent",
                "created": time.time(),
                "path": str(file_path),
            }

            return True
        except Exception as e:
            logger.error(f"保存文件失败: {e}")
            return False

    async def delete_file(self, file_id: str) -> bool:
        """删除文件"""
        try:
            if file_id not in self.file_metadata:
                return False

            file_path = Path(self.file_metadata[file_id]["path"])
            if file_path.exists():
                file_path.unlink()

            del self.file_metadata[file_id]
            return True
        except Exception as e:
            logger.error(f"删除文件失败: {e}")
            return False

    def get_file_list(self) -> List[Dict]:
        """获取文件列表"""
        file_list = []
        for file_id, metadata in self.file_metadata.items():
            file_list.append(
                {
                    "file_id": file_id,
                    "name": metadata["name"],
                    "type": metadata["type"],
                    "created": datetime.fromtimestamp(metadata["created"]).isoformat(),
                    "url": f"/plugins/cache/api/file/{file_id}",
                }
            )
        return file_list

    def get_file_path(self, file_id: str) -> Optional[str]:
        """获取文件路径"""
        if file_id in self.file_metadata:
            return self.file_metadata[file_id]["path"]
        return None

    def get_status(self) -> Dict:
        """获取插件状态"""
        return {
            "name": self.name,
            "enabled": self.enabled,
            "file_count": len(self.file_metadata),
            "cache_dir": str(self.cache_dir),
        }


# 创建全局实例
plugin = FileCachePlugin()