返回 VideoClaw
files.py
1 import logging
2 import os
3 import shutil
4 import time
5 from typing import Optional
6
7 from fastapi import APIRouter, File, HTTPException, UploadFile
8
9 from config import settings
10 from models.file_reader import FileReader
11
12 logger = logging.getLogger(__name__)
13 router = APIRouter(tags=["Files"])
14
15
16 def _path_size(path: str) -> int:
17 if os.path.isfile(path) or os.path.islink(path):
18 return os.path.getsize(path)
19 total = 0
20 for root, dirs, files in os.walk(path):
21 for name in files:
22 item = os.path.join(root, name)
23 try:
24 total += os.path.getsize(item)
25 except OSError:
26 continue
27 for name in dirs:
28 item = os.path.join(root, name)
29 if os.path.islink(item):
30 try:
31 total += os.path.getsize(item)
32 except OSError:
33 continue
34 return total
35
36
37 @router.post("/api/upload_file")
38 async def upload_file(file: UploadFile = File(...)):
39 allowed_exts = [".docx", ".doc", ".txt", ".md", ".pdf"]
40 filename = file.filename
41 ext = os.path.splitext(filename)[1].lower()
42 if ext not in allowed_exts:
43 raise HTTPException(status_code=400, detail=f"仅支持 {', '.join(allowed_exts)} 格式的文件")
44
45 os.makedirs(settings.TEMP_DIR, exist_ok=True)
46 safe_filename = f"{int(time.time())}_{filename}"
47 file_path = os.path.join(settings.TEMP_DIR, safe_filename)
48 try:
49 with open(file_path, "wb") as buffer:
50 shutil.copyfileobj(file.file, buffer)
51 except Exception as e:
52 logger.error(f"保存上传文件失败: {e}")
53 raise HTTPException(status_code=500, detail=f"文件保存失败: {str(e)}")
54
55 return {"filename": filename, "file_path": safe_filename}
56
57
58 @router.post("/api/upload_media")
59 async def upload_media(file: UploadFile = File(...)):
60 allowed_exts = [
61 ".jpg", ".jpeg", ".png", ".webp", ".bmp",
62 ".mp4", ".mov", ".avi", ".mkv", ".webm",
63 ]
64 filename = file.filename or "upload"
65 ext = os.path.splitext(filename)[1].lower()
66 if ext not in allowed_exts:
67 raise HTTPException(status_code=400, detail=f"仅支持 {', '.join(allowed_exts)} 格式的媒体文件")
68
69 os.makedirs(settings.TEMP_DIR, exist_ok=True)
70 safe_filename = f"{int(time.time())}_{filename}"
71 file_path = os.path.join(settings.TEMP_DIR, safe_filename)
72 try:
73 with open(file_path, "wb") as buffer:
74 shutil.copyfileobj(file.file, buffer)
75 except Exception as e:
76 logger.error(f"保存上传媒体失败: {e}")
77 raise HTTPException(status_code=500, detail=f"媒体保存失败: {str(e)}")
78
79 return {
80 "filename": filename,
81 "file_path": file_path,
82 }
83
84
85 @router.delete("/api/cache/temp")
86 async def clear_temp_cache():
87 os.makedirs(settings.TEMP_DIR, exist_ok=True)
88 deleted = 0
89 freed_bytes = 0
90 errors = []
91 for entry in os.scandir(settings.TEMP_DIR):
92 try:
93 freed_bytes += _path_size(entry.path)
94 if entry.is_dir(follow_symlinks=False):
95 shutil.rmtree(entry.path)
96 else:
97 os.remove(entry.path)
98 deleted += 1
99 except Exception as exc:
100 logger.warning("Failed to delete temp cache item: %s", entry.path, exc_info=True)
101 errors.append({"path": entry.name, "error": str(exc)})
102 return {
103 "status": "ok",
104 "deleted": deleted,
105 "freed_bytes": freed_bytes,
106 "freed_mb": round(freed_bytes / 1024 / 1024, 2),
107 "errors": errors,
108 }
109
110
111 def merge_uploaded_file_into_idea(idea: str, file_path: Optional[str]) -> str:
112 if not file_path:
113 return idea
114
115 full_path = os.path.join(settings.TEMP_DIR, file_path)
116 if not os.path.exists(full_path):
117 logger.warning(f"上传的文件未找到: {full_path}")
118 return idea
119
120 content = FileReader.extract_text(full_path)
121 if content:
122 original_filename = "_".join(file_path.split("_")[1:])
123 prompt_fragment = FileReader.format_as_prompt(original_filename, content)
124 idea = f"{idea}\n\n{prompt_fragment}"
125 logger.info(f"成功处理上传文件: {full_path}")
126 logger.debug(f"文件内容预览:\n{content[:500]}")
127 return idea
128
128 lines PYTHON