luoluoluo22 commited on
Commit
c8e14c6
·
0 Parent(s):

Initial commit with all files for baidu_drive_api

Browse files
Files changed (8) hide show
  1. Dockerfile +24 -0
  2. README.md +93 -0
  3. app.py +16 -0
  4. baidu_drive_api.py +372 -0
  5. healthcheck.py +44 -0
  6. requirements.txt +6 -0
  7. start.sh +13 -0
  8. static/index.html +239 -0
Dockerfile ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.9-slim
2
+
3
+ WORKDIR /app
4
+
5
+ # 安装依赖
6
+ COPY requirements.txt .
7
+ RUN pip install --no-cache-dir -r requirements.txt
8
+
9
+ # 复制所有文件
10
+ COPY . .
11
+
12
+ # 设置环境变量
13
+ ENV FUNUTIL_LOG_DISABLE=1
14
+ ENV FUNUTIL_LOG_TO_FILE=0
15
+ ENV PORT=7860
16
+
17
+ # 暴露端口
18
+ EXPOSE 7860
19
+
20
+ # 设置启动脚本权限
21
+ RUN chmod +x start.sh
22
+
23
+ # 启动服务
24
+ CMD ["/bin/bash", "start.sh"]
README.md ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 百度网盘API服务
2
+
3
+ 这是一个基于Flask的百度网盘API服务,提供了百度网盘的主要功能的RESTful API接口。
4
+
5
+ 本服务支持两种模式:
6
+ 1. **真实模式**:如果环境中安装了fundrive[baidu]库,将使用真实的百度网盘API
7
+ 2. **模拟模式**:如果环境中没有安装fundrive[baidu]库,将使用模拟的API(仅用于演示)
8
+
9
+ ## 使用方法
10
+
11
+ 1. 在请求头中添加`X-Bduss`字段,值为百度网盘的BDUSS
12
+ 2. 调用相应的API端点
13
+
14
+ ## 获取BDUSS
15
+
16
+ BDUSS可以从浏览器Cookie中获取:
17
+ 1. 登录百度网盘网页版
18
+ 2. 打开浏览器开发者工具(F12)
19
+ 3. 切换到"应用"或"Application"选项卡
20
+ 4. 在左侧找到"Cookies",然后选择百度网盘的域名
21
+ 5. 在右侧找到名为"BDUSS"的Cookie,其值就是BDUSS
22
+
23
+ ## API端点
24
+
25
+ ### 1. 列出文件和目录
26
+
27
+ ```
28
+ GET /api/files?path=/
29
+ ```
30
+
31
+ ### 2. 上传文件
32
+
33
+ ```
34
+ POST /api/files
35
+ ```
36
+
37
+ ### 3. 获取文件的下载链接
38
+
39
+ ```
40
+ GET /api/files/<file_path>
41
+ ```
42
+
43
+ ### 4. 删除文件
44
+
45
+ ```
46
+ DELETE /api/files/<file_path>
47
+ ```
48
+
49
+ ### 5. 获取网盘配额信息
50
+
51
+ ```
52
+ GET /api/quota
53
+ ```
54
+
55
+ ## 示例
56
+
57
+ 使用curl获取根目录文件列表:
58
+
59
+ ```bash
60
+ curl -H "X-Bduss: YOUR_BDUSS_VALUE" https://your-space-name.hf.space/api/files
61
+ ```
62
+
63
+ 获取文件的下载链接:
64
+
65
+ ```bash
66
+ curl -H "X-Bduss: YOUR_BDUSS_VALUE" https://your-space-name.hf.space/api/files/test.txt
67
+ ```
68
+
69
+ ## 注意事项
70
+
71
+ 1. BDUSS是敏感信息,请妥善保管,不要泄露给他人
72
+ 2. 下载链接的有效期较短,请尽快使用
73
+ 3. 服务默认限制上传文件大小为1GB
74
+
75
+ ## 当前服务状态
76
+
77
+ 本服务在Hugging Face上运行,可能会使用模拟模式。如果您需要使用真实的百度网盘API,可以将代码下载到本地运行。
78
+
79
+ 您可以通过访问 `/health` 端点来检查服务是否正常运行,以及当前使用的是真实模式还是模拟模式。
80
+
81
+ ## 源代码
82
+
83
+ 完整源代码和详细文档可在GitHub上获取:[百度网盘API服务](https://github.com/your-username/baidu-drive-api)
84
+
85
+ ## 关于模拟模式
86
+
87
+ 如果服务在模拟模式下运行,将会返回模拟的数据,而不是真实的百度网盘数据。这种模式下:
88
+
89
+ 1. 列出文件和目录将返回空列表
90
+ 2. 上传文件将返回成功,但实际上没有上传
91
+ 3. 获取下载链接将返回一个假的链接
92
+ 4. 删除文件将返回成功,但实际上没有删除
93
+ 5. 获取配额信息将返回固定的数据
app.py ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ 百度网盘API服务 - Hugging Face启动脚本
6
+ """
7
+
8
+ # 导入应用程序
9
+ from baidu_drive_api import app
10
+
11
+ # 这个文件会被Hugging Face自动识别并运行
12
+ if __name__ == "__main__":
13
+ # 使用环境变量中的端口(如果有),否则使用7860(Hugging Face的默认端口)
14
+ import os
15
+ port = int(os.environ.get('PORT', 7860))
16
+ app.run(host='0.0.0.0', port=port)
baidu_drive_api.py ADDED
@@ -0,0 +1,372 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ 百度网盘HTTP API服务
6
+ 提供百度网盘功能的RESTful API接口
7
+ """
8
+
9
+ import os
10
+ import time
11
+ import json
12
+ import logging
13
+ import tempfile
14
+ import traceback
15
+ from flask import Flask, request, jsonify, send_file, Response, send_from_directory
16
+ from werkzeug.utils import secure_filename
17
+ from functools import wraps
18
+
19
+ # 设置HOME环境变量(如果不存在)
20
+ if 'HOME' not in os.environ:
21
+ os.environ['HOME'] = os.environ.get('USERPROFILE', '')
22
+ print(f"已设置HOME环境变量为: {os.environ['HOME']}")
23
+
24
+ # 禁用funutil库创建logsdir
25
+ # 使用猴子补丁来防止os.makedirs创建'logs'目录
26
+ original_makedirs = os.makedirs
27
+ def patched_makedirs(name, *args, **kwargs):
28
+ if name == 'logs' or name.startswith('logs/'):
29
+ print(f"[警告] 已拦截对{name}目录的创建尝试")
30
+ return
31
+ return original_makedirs(name, *args, **kwargs)
32
+ os.makedirs = patched_makedirs
33
+
34
+ # 拦截文件打开操作
35
+ original_open = open
36
+ def patched_open(file, *args, **kwargs):
37
+ if file.startswith('logs/') or file == 'logs':
38
+ print(f"[警告] 已拦截对{file}文件的打开尝试")
39
+ # 创建一个内存中的空文件对象
40
+ import io
41
+ return io.StringIO()
42
+ return original_open(file, *args, **kwargs)
43
+ open = patched_open
44
+
45
+ # 禁用日志文件写入
46
+ # 设置环境变量来尝试禁用日志文件
47
+ os.environ['FUNUTIL_LOG_DISABLE'] = '1'
48
+ os.environ['FUNUTIL_LOG_TO_FILE'] = '0'
49
+
50
+ # 导入百度网盘API
51
+ try:
52
+ print("尝试导入真实BaiDuDrive...")
53
+ from fundrive.drives.baidu.drive import BaiDuDrive
54
+ print("成功导入真实BaiDuDrive")
55
+ USE_REAL_API = True
56
+ except Exception as e:
57
+ print(f"导入真实BaiDuDrive失败: {e}")
58
+ print("将使用模拟的BaiDuDrive类")
59
+
60
+ # 模拟百度网盘API
61
+ class BaiDuDrive:
62
+ def __init__(self):
63
+ self.drive = self
64
+ self.bduss = None
65
+ self.ptoken = None
66
+ print("创建了模拟的BaiDuDrive实例")
67
+
68
+ def login(self, bduss=None):
69
+ self.bduss = bduss
70
+ self.ptoken = "fake_ptoken"
71
+ print(f"模拟登录成功,bduss: {bduss[:10] if bduss else None}...")
72
+ return True
73
+
74
+ def get_file_list(self, path="/"):
75
+ print(f"模拟获取文件列表,路径: {path}")
76
+ return []
77
+
78
+ def get_dir_list(self, path="/"):
79
+ print(f"模拟获取目录列表,路径: {path}")
80
+ return []
81
+
82
+ def upload_file(self, local_path, remote_path):
83
+ print(f"模拟上传文件,本地路径: {local_path},远程路径: {remote_path}")
84
+ return True
85
+
86
+ def delete(self, path):
87
+ print(f"模拟删除文件,路径: {path}")
88
+ return True
89
+
90
+ def get_quota(self):
91
+ print("模拟获取配额信息")
92
+ return {"total": 2199023255552, "used": 1073741824}
93
+
94
+ def download_link(self, path):
95
+ print(f"模拟获取下载链接,路径: {path}")
96
+ return "https://example.com/fake_download_link"
97
+
98
+ USE_REAL_API = False
99
+ print("成功创建模拟的BaiDuDrive类")
100
+
101
+ # 配置日志 - 只输出到控制台
102
+ logging.basicConfig(
103
+ level=logging.INFO,
104
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
105
+ handlers=[
106
+ logging.StreamHandler()
107
+ ]
108
+ )
109
+ logger = logging.getLogger(__name__)
110
+
111
+ app = Flask(__name__, static_folder='static')
112
+ app.config['MAX_CONTENT_LENGTH'] = 1024 * 1024 * 1024 # 限制上传文件大小为1GB
113
+ app.config['UPLOAD_FOLDER'] = tempfile.gettempdir() # 使用临时目录存储上传的文件
114
+
115
+ # 存储BDUSS和客户端实例的字典
116
+ clients = {}
117
+
118
+ # 身份验证装饰器
119
+ def require_bduss(f):
120
+ @wraps(f)
121
+ def decorated(*args, **kwargs):
122
+ bduss = request.headers.get('X-Bduss')
123
+ if not bduss:
124
+ return jsonify({"error": "未提供BDUSS,请在请求头中添加X-Bduss"}), 401
125
+
126
+ # 检查是否已有客户端实例
127
+ if bduss not in clients:
128
+ try:
129
+ client = BaiDuDrive()
130
+ login_result = client.login(bduss=bduss)
131
+ if not login_result:
132
+ return jsonify({"error": "BDUSS无效或已过期"}), 401
133
+ clients[bduss] = client
134
+ logger.info(f"创建新的客户端实例,BDUSS: {bduss[:10]}...")
135
+ except Exception as e:
136
+ logger.error(f"创建客户端实例失败: {str(e)}")
137
+ return jsonify({"error": f"创建客户端实例失败: {str(e)}"}), 500
138
+
139
+ # 将客户端实例传递给视图函数
140
+ return f(clients[bduss], *args, **kwargs)
141
+ return decorated
142
+
143
+ @app.route('/')
144
+ def index():
145
+ """API首页,显示简单的使用说明"""
146
+ # 如果存在static/index.html,则返回它
147
+ if os.path.exists(os.path.join(app.static_folder, 'index.html')):
148
+ return send_from_directory(app.static_folder, 'index.html')
149
+
150
+ # 否则返回JSON数据
151
+ return jsonify({
152
+ "name": "百度网盘API服务",
153
+ "version": "1.0.0",
154
+ "description": "提供百度网盘功能的RESTful API接口",
155
+ "endpoints": [
156
+ {"path": "/api/files", "method": "GET", "description": "列出文件和目录"},
157
+ {"path": "/api/files", "method": "POST", "description": "上传文件"},
158
+ {"path": "/api/files/<path:file_path>", "method": "GET", "description": "获取文件的下载链接"},
159
+ {"path": "/api/files/<path:file_path>", "method": "DELETE", "description": "删除文件"},
160
+ {"path": "/api/quota", "method": "GET", "description": "获取网盘配额信息"}
161
+ ],
162
+ "authentication": "在请求头中添加X-Bduss字段,值为百度网盘的BDUSS"
163
+ })
164
+
165
+ @app.route('/api/files')
166
+ @require_bduss
167
+ def list_files(client):
168
+ """列出文件和目录"""
169
+ path = request.args.get('path', '/')
170
+
171
+ try:
172
+ # 获取文件列表
173
+ file_list = client.get_file_list(path)
174
+ # 获取目录列表
175
+ dir_list = client.get_dir_list(path)
176
+
177
+ # 合并文件和目录列表
178
+ all_items = []
179
+
180
+ # 处理目录
181
+ for item in dir_list:
182
+ item_data = {
183
+ "name": item.name if hasattr(item, 'name') else "未知",
184
+ "path": item.path if hasattr(item, 'path') else path + "/" + item.name,
185
+ "type": "directory",
186
+ "size": item.size if hasattr(item, 'size') else 0,
187
+ "size_formatted": f"{item.size / (1024 * 1024):.2f} MB" if hasattr(item, 'size') else "0.00 MB"
188
+ }
189
+ all_items.append(item_data)
190
+
191
+ # 处理文件
192
+ for item in file_list:
193
+ item_data = {
194
+ "name": item.name if hasattr(item, 'name') else "未知",
195
+ "path": item.path if hasattr(item, 'path') else path + "/" + item.name,
196
+ "type": "file",
197
+ "size": item.size if hasattr(item, 'size') else 0,
198
+ "size_formatted": f"{item.size / (1024 * 1024):.2f} MB" if hasattr(item, 'size') else "0.00 MB"
199
+ }
200
+ all_items.append(item_data)
201
+
202
+ return jsonify({
203
+ "path": path,
204
+ "items": all_items,
205
+ "total": len(all_items)
206
+ })
207
+ except Exception as e:
208
+ logger.error(f"列出文件时出错: {str(e)}\n{traceback.format_exc()}")
209
+ return jsonify({"error": f"列出文件时出错: {str(e)}"}), 500
210
+
211
+ @app.route('/api/files', methods=['POST'])
212
+ @require_bduss
213
+ def upload_file(client):
214
+ """上传文件"""
215
+ if 'file' not in request.files:
216
+ return jsonify({"error": "未提供文件"}), 400
217
+
218
+ file = request.files['file']
219
+ if file.filename == '':
220
+ return jsonify({"error": "未选择文件"}), 400
221
+
222
+ remote_path = request.form.get('path', '/')
223
+ if not remote_path.endswith('/'):
224
+ remote_path += '/'
225
+
226
+ try:
227
+ # 保存上传的文件到临时目录
228
+ filename = secure_filename(file.filename)
229
+ temp_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
230
+ file.save(temp_path)
231
+
232
+ # 上传文件到百度网盘
233
+ remote_file_path = f"{remote_path}{filename}"
234
+ upload_result = client.upload_file(temp_path, remote_file_path)
235
+
236
+ # 删除临时文件
237
+ os.remove(temp_path)
238
+
239
+ if upload_result:
240
+ return jsonify({
241
+ "success": True,
242
+ "message": f"文件 {filename} 上传成功",
243
+ "path": remote_file_path
244
+ })
245
+ else:
246
+ return jsonify({
247
+ "success": False,
248
+ "error": f"文件 {filename} 上传失败"
249
+ }), 500
250
+ except Exception as e:
251
+ logger.error(f"上传文件时出错: {str(e)}\n{traceback.format_exc()}")
252
+ # 确保临时文件被删除
253
+ if os.path.exists(temp_path):
254
+ os.remove(temp_path)
255
+ return jsonify({"error": f"上传文件时出错: {str(e)}"}), 500
256
+
257
+ @app.route('/api/files/<path:file_path>')
258
+ @require_bduss
259
+ def get_download_link(client, file_path):
260
+ """获取文件的下载链接"""
261
+ try:
262
+ # 确保文件路径以/开头
263
+ if not file_path.startswith('/'):
264
+ file_path = '/' + file_path
265
+
266
+ # 获取文件名
267
+ filename = os.path.basename(file_path)
268
+
269
+ # 使用底层API获取下载链接
270
+ try:
271
+ download_link = client.drive.download_link(file_path)
272
+
273
+ # 生成完整的下载信息
274
+ headers = {
275
+ "User-Agent": "softxm;netdisk",
276
+ "Connection": "Keep-Alive",
277
+ "Cookie": f"BDUSS={client.drive.bduss};ptoken={client.drive.ptoken}",
278
+ }
279
+
280
+ return jsonify({
281
+ "success": True,
282
+ "filename": filename,
283
+ "download_link": download_link,
284
+ "headers": headers,
285
+ "message": "下载链接获取成功",
286
+ "note": "注意:下载链接有效期较短,请尽快使用"
287
+ })
288
+ except Exception as e:
289
+ return jsonify({"error": f"获取下载链接失败: {str(e)}"}), 500
290
+ except Exception as e:
291
+ logger.error(f"获取下载链接时出错: {str(e)}\n{traceback.format_exc()}")
292
+ return jsonify({"error": f"获取下载链接时出错: {str(e)}"}), 500
293
+
294
+ @app.route('/api/files/<path:file_path>', methods=['DELETE'])
295
+ @require_bduss
296
+ def delete_file(client, file_path):
297
+ """删除文件"""
298
+ try:
299
+ # 确保文件路径以/开头
300
+ if not file_path.startswith('/'):
301
+ file_path = '/' + file_path
302
+
303
+ # 删除文件
304
+ delete_result = client.delete(file_path)
305
+
306
+ if delete_result is None or delete_result:
307
+ return jsonify({
308
+ "success": True,
309
+ "message": f"文件 {file_path} 删除成功"
310
+ })
311
+ else:
312
+ return jsonify({
313
+ "success": False,
314
+ "error": f"文件 {file_path} 删除失败"
315
+ }), 500
316
+ except Exception as e:
317
+ logger.error(f"删除文件时出错: {str(e)}\n{traceback.format_exc()}")
318
+ return jsonify({"error": f"删除文件时出错: {str(e)}"}), 500
319
+
320
+ @app.route('/api/quota')
321
+ @require_bduss
322
+ def get_quota(client):
323
+ """获取网盘配额信息"""
324
+ try:
325
+ # 获取配额信息
326
+ quota_info = client.get_quota()
327
+
328
+ if quota_info:
329
+ total_space = quota_info.get('total', 0) / (1024 * 1024 * 1024) # 转换为GB
330
+ used_space = quota_info.get('used', 0) / (1024 * 1024 * 1024) # 转换为GB
331
+
332
+ return jsonify({
333
+ "total": quota_info.get('total', 0),
334
+ "used": quota_info.get('used', 0),
335
+ "free": quota_info.get('total', 0) - quota_info.get('used', 0),
336
+ "total_formatted": f"{total_space:.2f} GB",
337
+ "used_formatted": f"{used_space:.2f} GB",
338
+ "free_formatted": f"{total_space - used_space:.2f} GB"
339
+ })
340
+ else:
341
+ return jsonify({"error": "获取配额信息失败"}), 500
342
+ except Exception as e:
343
+ logger.error(f"获取配额信息时出错: {str(e)}\n{traceback.format_exc()}")
344
+ return jsonify({"error": f"获取配额信息时出错: {str(e)}"}), 500
345
+
346
+ @app.errorhandler(404)
347
+ def not_found(error):
348
+ return jsonify({"error": "资源不存在"}), 404
349
+
350
+ @app.errorhandler(500)
351
+ def server_error(error):
352
+ return jsonify({"error": "服务器内部错误"}), 500
353
+
354
+ @app.errorhandler(413)
355
+ def request_entity_too_large(error):
356
+ return jsonify({"error": "上传的文件太大"}), 413
357
+
358
+ # 添加健康检查端点
359
+ @app.route('/health')
360
+ def health_check():
361
+ mode = "real" if 'USE_REAL_API' in globals() and USE_REAL_API else "mock"
362
+ return jsonify({
363
+ "status": "ok",
364
+ "message": "Service is running",
365
+ "mode": mode,
366
+ "description": "使用真实百度网盘API" if mode == "real" else "使用模拟百度网盘API(仅用于演示)"
367
+ })
368
+
369
+ if __name__ == '__main__':
370
+ # 使用环境变量中的端口(如果有),否则使用7860(Hugging Face的默认端口)
371
+ port = int(os.environ.get('PORT', 7860))
372
+ app.run(host='0.0.0.0', port=port, debug=True)
healthcheck.py ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ 百度网盘API服务健康检查脚本
6
+ 用于Hugging Face检查服务是否正常运行
7
+ """
8
+
9
+ import os
10
+ import sys
11
+ import time
12
+ import requests
13
+
14
+ def check_health():
15
+ """检查服务是否正常运行"""
16
+ port = os.environ.get('PORT', 7860)
17
+ url = f"http://localhost:{port}/health"
18
+
19
+ # 尝试连接服务
20
+ max_retries = 10
21
+ retry_interval = 3 # 秒
22
+
23
+ for i in range(max_retries):
24
+ try:
25
+ response = requests.get(url, timeout=5)
26
+ if response.status_code == 200:
27
+ data = response.json()
28
+ if data.get('status') == 'ok':
29
+ print(f"服务正常运行: {data}")
30
+ return True
31
+ print(f"服务返回异常状态码: {response.status_code}")
32
+ except Exception as e:
33
+ print(f"尝试 {i+1}/{max_retries} 连接服务失败: {e}")
34
+
35
+ if i < max_retries - 1:
36
+ print(f"等待 {retry_interval} 秒后重试...")
37
+ time.sleep(retry_interval)
38
+
39
+ print("健康检查失败,服务可能未正常运行")
40
+ return False
41
+
42
+ if __name__ == "__main__":
43
+ success = check_health()
44
+ sys.exit(0 if success else 1)
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ flask==2.0.1
2
+ requests==2.26.0
3
+ werkzeug==2.0.1
4
+ gunicorn==20.1.0
5
+ python-dotenv==0.19.1
6
+ fundrive[baidu]
start.sh ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ # 设置环境变量
4
+ export FUNUTIL_LOG_DISABLE=1
5
+ export FUNUTIL_LOG_TO_FILE=0
6
+ export PORT=7860
7
+
8
+ # 创建一个空的logs目录,防止fundrive库尝试创建它时失败
9
+ mkdir -p logs
10
+ touch logs/.gitignore
11
+
12
+ # 启动服务
13
+ gunicorn --bind 0.0.0.0:7860 --timeout 120 --workers 1 baidu_drive_api:app
static/index.html ADDED
@@ -0,0 +1,239 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>百度网盘API服务</title>
7
+ <style>
8
+ body {
9
+ font-family: Arial, sans-serif;
10
+ line-height: 1.6;
11
+ max-width: 800px;
12
+ margin: 0 auto;
13
+ padding: 20px;
14
+ color: #333;
15
+ }
16
+ h1, h2, h3 {
17
+ color: #1a73e8;
18
+ }
19
+ code {
20
+ background-color: #f5f5f5;
21
+ padding: 2px 5px;
22
+ border-radius: 3px;
23
+ font-family: monospace;
24
+ }
25
+ pre {
26
+ background-color: #f5f5f5;
27
+ padding: 10px;
28
+ border-radius: 5px;
29
+ overflow-x: auto;
30
+ }
31
+ .endpoint {
32
+ margin-bottom: 30px;
33
+ border-left: 3px solid #1a73e8;
34
+ padding-left: 15px;
35
+ }
36
+ .method {
37
+ display: inline-block;
38
+ padding: 3px 8px;
39
+ border-radius: 3px;
40
+ color: white;
41
+ font-weight: bold;
42
+ margin-right: 10px;
43
+ }
44
+ .get {
45
+ background-color: #4CAF50;
46
+ }
47
+ .post {
48
+ background-color: #2196F3;
49
+ }
50
+ .delete {
51
+ background-color: #F44336;
52
+ }
53
+ .status {
54
+ margin-top: 20px;
55
+ padding: 15px;
56
+ border-radius: 5px;
57
+ background-color: #f8f9fa;
58
+ border: 1px solid #ddd;
59
+ }
60
+ .status.loading {
61
+ background-color: #fff3cd;
62
+ border-color: #ffeeba;
63
+ }
64
+ .status.success {
65
+ background-color: #d4edda;
66
+ border-color: #c3e6cb;
67
+ }
68
+ .status.error {
69
+ background-color: #f8d7da;
70
+ border-color: #f5c6cb;
71
+ }
72
+ button {
73
+ background-color: #1a73e8;
74
+ color: white;
75
+ border: none;
76
+ padding: 8px 15px;
77
+ border-radius: 4px;
78
+ cursor: pointer;
79
+ font-size: 14px;
80
+ }
81
+ button:hover {
82
+ background-color: #0d62c9;
83
+ }
84
+ input[type="text"] {
85
+ padding: 8px;
86
+ border: 1px solid #ddd;
87
+ border-radius: 4px;
88
+ width: 300px;
89
+ font-size: 14px;
90
+ }
91
+ </style>
92
+ </head>
93
+ <body>
94
+ <h1>百度网盘API服务</h1>
95
+
96
+ <div class="status" id="serviceStatus">
97
+ <p>正在检查服务状态...</p>
98
+ </div>
99
+
100
+ <p>这是一个基于Flask的百度网盘API服务,提供了百度网盘的主要功能的RESTful API接口。</p>
101
+
102
+ <h2>使用方法</h2>
103
+ <p>1. 在请求头中添加<code>X-Bduss</code>字段,值为百度网盘的BDUSS</p>
104
+ <p>2. 调用相应的API端点</p>
105
+
106
+ <h2>获取BDUSS</h2>
107
+ <p>BDUSS可以从浏览器Cookie中获取:</p>
108
+ <ol>
109
+ <li>登录百度网盘网页版</li>
110
+ <li>打开浏览器开发者工具(F12)</li>
111
+ <li>切换到"应用"或"Application"选项卡</li>
112
+ <li>在左侧找到"Cookies",然后选择百度网盘的域名</li>
113
+ <li>在右侧找到名为"BDUSS"的Cookie,其值就是BDUSS</li>
114
+ </ol>
115
+
116
+ <h2>API端点</h2>
117
+
118
+ <div class="endpoint">
119
+ <h3><span class="method get">GET</span> /api/files</h3>
120
+ <p>列出指定路径下的文件和目录</p>
121
+ <p><strong>参数:</strong> <code>path</code> - 要列出内容的路径,默认为根目录(/)</p>
122
+ <pre><code>curl -H "X-Bduss: YOUR_BDUSS_VALUE" https://your-space-name.hf.space/api/files?path=/</code></pre>
123
+ </div>
124
+
125
+ <div class="endpoint">
126
+ <h3><span class="method post">POST</span> /api/files</h3>
127
+ <p>上传文件到指定路径</p>
128
+ <p><strong>参数:</strong></p>
129
+ <ul>
130
+ <li><code>file</code> - 要上传的文件(multipart/form-data)</li>
131
+ <li><code>path</code> - 远程目录路径,默认为根目录(/)</li>
132
+ </ul>
133
+ <pre><code>curl -H "X-Bduss: YOUR_BDUSS_VALUE" -F "file=@local_file.txt" -F "path=/" https://your-space-name.hf.space/api/files</code></pre>
134
+ </div>
135
+
136
+ <div class="endpoint">
137
+ <h3><span class="method get">GET</span> /api/files/&lt;file_path&gt;</h3>
138
+ <p>获取文件的下载链接</p>
139
+ <pre><code>curl -H "X-Bduss: YOUR_BDUSS_VALUE" https://your-space-name.hf.space/api/files/test.txt</code></pre>
140
+ </div>
141
+
142
+ <div class="endpoint">
143
+ <h3><span class="method delete">DELETE</span> /api/files/&lt;file_path&gt;</h3>
144
+ <p>删除指定路径的文件</p>
145
+ <pre><code>curl -X DELETE -H "X-Bduss: YOUR_BDUSS_VALUE" https://your-space-name.hf.space/api/files/test.txt</code></pre>
146
+ </div>
147
+
148
+ <div class="endpoint">
149
+ <h3><span class="method get">GET</span> /api/quota</h3>
150
+ <p>获取网盘配额信息</p>
151
+ <pre><code>curl -H "X-Bduss: YOUR_BDUSS_VALUE" https://your-space-name.hf.space/api/quota</code></pre>
152
+ </div>
153
+
154
+ <h2>测试API</h2>
155
+ <p>输入您的BDUSS值,然后点击按钮测试API:</p>
156
+ <div>
157
+ <input type="text" id="bdussInput" placeholder="输入您的BDUSS值">
158
+ <button onclick="testAPI()">测试API</button>
159
+ </div>
160
+ <div class="status" id="testResult" style="display: none;"></div>
161
+
162
+ <h2>注意事项</h2>
163
+ <ol>
164
+ <li>BDUSS是敏感信息,请妥善保管,不要泄露给他人</li>
165
+ <li>下载链接的有效期较短,请尽快使用</li>
166
+ <li>服务默认限制上传文件大小为1GB</li>
167
+ </ol>
168
+
169
+ <script>
170
+ // 检查服务状态
171
+ async function checkServiceStatus() {
172
+ const statusDiv = document.getElementById('serviceStatus');
173
+ statusDiv.className = 'status loading';
174
+ statusDiv.innerHTML = '<p>正在检查服务状态...</p>';
175
+
176
+ try {
177
+ const response = await fetch('/health');
178
+ const data = await response.json();
179
+
180
+ if (data.status === 'ok') {
181
+ statusDiv.className = 'status success';
182
+ statusDiv.innerHTML = `
183
+ <p><strong>服务状态:</strong> 正常运行</p>
184
+ <p><strong>运行模式:</strong> ${data.mode === 'real' ? '真实模式' : '模拟模式'}</p>
185
+ <p><strong>描述:</strong> ${data.description}</p>
186
+ `;
187
+ } else {
188
+ statusDiv.className = 'status error';
189
+ statusDiv.innerHTML = '<p><strong>服务状态:</strong> 异常</p>';
190
+ }
191
+ } catch (error) {
192
+ statusDiv.className = 'status error';
193
+ statusDiv.innerHTML = `<p><strong>服务状态:</strong> 无法连接到服务 (${error.message})</p>`;
194
+ }
195
+ }
196
+
197
+ // 测试API
198
+ async function testAPI() {
199
+ const bduss = document.getElementById('bdussInput').value.trim();
200
+ const resultDiv = document.getElementById('testResult');
201
+
202
+ if (!bduss) {
203
+ resultDiv.className = 'status error';
204
+ resultDiv.style.display = 'block';
205
+ resultDiv.innerHTML = '<p>请输入BDUSS值</p>';
206
+ return;
207
+ }
208
+
209
+ resultDiv.className = 'status loading';
210
+ resultDiv.style.display = 'block';
211
+ resultDiv.innerHTML = '<p>正在测试API...</p>';
212
+
213
+ try {
214
+ const response = await fetch('/api/files', {
215
+ headers: {
216
+ 'X-Bduss': bduss
217
+ }
218
+ });
219
+
220
+ const data = await response.json();
221
+
222
+ resultDiv.className = 'status success';
223
+ resultDiv.innerHTML = `
224
+ <p><strong>API测试结果:</strong> 成功</p>
225
+ <p><strong>路径:</strong> ${data.path}</p>
226
+ <p><strong>文件/文件夹数量:</strong> ${data.total}</p>
227
+ <pre>${JSON.stringify(data, null, 2)}</pre>
228
+ `;
229
+ } catch (error) {
230
+ resultDiv.className = 'status error';
231
+ resultDiv.innerHTML = `<p><strong>API测试失败:</strong> ${error.message}</p>`;
232
+ }
233
+ }
234
+
235
+ // 页面加载时检查服务状态
236
+ window.onload = checkServiceStatus;
237
+ </script>
238
+ </body>
239
+ </html>