上传main

This commit is contained in:
HKXluo 2025-04-19 23:37:46 +08:00
parent 70da8b17a5
commit 967a3ad705
No known key found for this signature in database
GPG Key ID: 498DD8DAE5261F38
11 changed files with 302 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
.history
__pycache__/
app.log

117
ffmpeg_utils.py Normal file
View File

@ -0,0 +1,117 @@
import os
import uuid
import subprocess
import requests
import tempfile
import shutil
import logging
import threading
import time
from contextlib import contextmanager
UPLOAD_FOLDER = 'temp_files'
@contextmanager
def temp_directory(dir=None):
temp_dir = tempfile.mkdtemp(dir=dir)
try:
yield temp_dir
finally:
# 不再在这里自动删除临时目录
pass
def download_file(url, temp_dir):
try:
headers = {
'User-Agent': 'Mozilla/5.0',
'Accept': '*/*'
}
logging.info(f"开始从URL下载文件: {url}")
response = requests.get(url, headers=headers, stream=True, timeout=30)
response.raise_for_status()
file_path = os.path.join(temp_dir, 'input_audio')
with open(file_path, 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
if chunk:
f.write(chunk)
file_size = os.path.getsize(file_path)
if file_size < 1024:
error_msg = f"下载的文件太小(仅{file_size}字节),可能无效"
logging.error(error_msg)
raise Exception(error_msg)
logging.info(f"文件下载成功,保存到: {file_path} (大小: {file_size}字节)")
return file_path
except Exception as e:
logging.error(f"{url} 下载文件时出错: {e}")
raise
def execute_ffmpeg(input_path, output_path, ffmpeg_args):
try:
filtered_args = [arg for arg in ffmpeg_args if not (arg.lower() in ['-i', '-input'] or
(ffmpeg_args.index(arg) > 0 and ffmpeg_args[ffmpeg_args.index(arg) - 1].lower() == '-i'))]
cmd = ['lib/ffmpeg/bin/ffmpeg', '-i', input_path] + filtered_args + [output_path]
logging.info(f"执行FFmpeg命令: {' '.join(cmd)}")
result = subprocess.run(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True,
encoding='utf-8',
errors='replace'
)
if result.returncode != 0:
error_msg = f"FFmpeg处理失败返回码: {result.returncode}, 错误: {result.stderr}"
logging.error(error_msg)
raise Exception(error_msg)
logging.info("FFmpeg处理成功完成")
return True
except Exception as e:
logging.error(f"执行FFmpeg时出错: {e}")
raise
def process_ffmpeg(data, file_registry, file_lock):
try:
temp_dir = tempfile.mkdtemp(dir=UPLOAD_FOLDER)
logging.info(f"创建临时目录: {temp_dir}")
input_url = data.get('input_url')
ffmpeg_args = data.get('args', [])
input_path = download_file(input_url, temp_dir)
output_id = str(uuid.uuid4())[:8]
output_format = data.get('output_format', 'mp4')
output_filename = f"{output_id}.{output_format}"
output_path = os.path.join(temp_dir, output_filename)
execute_ffmpeg(input_path, output_path, ffmpeg_args)
with file_lock:
file_registry[output_id] = {
'path': os.path.abspath(output_path),
'filename': output_filename,
'last_access': time.time(),
'download_count': 0
}
logging.info(f"已注册新文件ID: {output_id}, 路径: {output_path}")
# 返回临时目录路径以便在主函数中删除
return {
'status': 'success',
'download_url': f"http://ffmpeg.liulikeji.cn/download/{output_id}/{output_filename}",
'file_id': output_id,
'temp_dir': temp_dir # 返回临时目录路径
}
except Exception as e:
logging.error(f"处理过程中出错: {e}")
return {'error': str(e)}

52
file_cleanup.py Normal file
View File

@ -0,0 +1,52 @@
import os
import shutil
import threading
import time
import logging
UPLOAD_FOLDER = 'temp_files'
CLEANUP_INTERVAL = 3600 # 清理临时文件的间隔(秒)
FILE_EXPIRY = 7200 # 文件过期时间(秒)
file_registry = {}
file_lock = threading.Lock()
def cleanup_temp_files():
current_time = time.time()
expired_folders = []
for folder_name in os.listdir(UPLOAD_FOLDER):
folder_path = os.path.join(UPLOAD_FOLDER, folder_name)
if not os.path.isdir(folder_path):
continue
try:
folder_mtime = os.path.getmtime(folder_path)
if current_time - folder_mtime > FILE_EXPIRY:
expired_folders.append(folder_path)
logging.info(f"检测到过期文件夹: {folder_path} (修改时间: {time.ctime(folder_mtime)})")
except Exception as e:
logging.error(f"获取文件夹 {folder_path} 信息失败: {e}")
for folder_path in expired_folders:
try:
shutil.rmtree(folder_path)
logging.info(f"已删除过期文件夹: {folder_path}")
except Exception as e:
logging.error(f"删除文件夹 {folder_path} 失败: {e}")
logging.info(f"清理完成,共删除 {len(expired_folders)} 个过期文件夹")
def start_cleanup_thread():
def cleanup_loop():
while True:
logging.info("开始执行定期清理任务...")
cleanup_temp_files()
time.sleep(CLEANUP_INTERVAL)
thread = threading.Thread(target=cleanup_loop)
thread.daemon = True
thread.start()
logging.info("已启动后台清理线程")

BIN
lib/ffmpeg/bin/ffmpeg Normal file

Binary file not shown.

BIN
lib/ffmpeg/bin/ffmpeg.exe Normal file

Binary file not shown.

BIN
lib/ffmpeg/bin/ffplay Normal file

Binary file not shown.

BIN
lib/ffmpeg/bin/ffplay.exe Normal file

Binary file not shown.

BIN
lib/ffmpeg/bin/ffprobe Normal file

Binary file not shown.

BIN
lib/ffmpeg/bin/ffprobe.exe Normal file

Binary file not shown.

26
logging_config.py Normal file
View File

@ -0,0 +1,26 @@
import logging
from logging.handlers import RotatingFileHandler
def setup_logging():
log_format = '%(asctime)s - %(levelname)s - %(message)s'
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
console_handler.setFormatter(logging.Formatter(log_format))
file_handler = RotatingFileHandler(
'app.log',
maxBytes=10 * 1024 * 1024,
backupCount=5,
encoding='utf-8'
)
file_handler.setLevel(logging.INFO)
file_handler.setFormatter(logging.Formatter(log_format))
root_logger = logging.getLogger()
root_logger.setLevel(logging.INFO)
root_logger.addHandler(console_handler)
root_logger.addHandler(file_handler)
werkzeug_logger = logging.getLogger('werkzeug')
werkzeug_logger.setLevel(logging.WARNING)

104
main.py
View File

@ -0,0 +1,104 @@
import logging
import json
from flask import Flask, request, jsonify
import threading
import queue
import time
from ffmpeg_utils import process_ffmpeg
from file_cleanup import start_cleanup_thread
app = Flask(__name__)
# 假设这些是主文件中已定义的变量
file_registry = {}
file_lock = threading.Lock()
#输入参数列表
enter_parameter_table = {
"ffmpeg": {
"input_url": str,
"output_format": str,
"args": list
}
}
# 检测输入参数是否符合要求
def validate_request(data,api_name):
for key in enter_parameter_table[api_name]:
if key not in data:
logging.warning(f"请求中没有提供{key}参数")
return jsonify({'error': f"未提供{key}参数"}), 400
if isinstance(data[key], enter_parameter_table[api_name][key]) == False:
logging.warning(f"请求中{key}参数类型错误,应为{enter_parameter_table[api_name][key]}")
return jsonify({'error': f"{key}参数类型错误,您输入为{type(data[key])},应为{enter_parameter_table[api_name][key]}" }), 400
return None, None
@app.route('/api/ffmpeg', methods=['POST'])
def ffmpeg_api():
logging.info("收到FFmpeg API请求")
data = request.get_json()
# 检测参数类型
error_response, status_code = validate_request(data,"ffmpeg")
if error_response: return error_response, status_code
# 创建处理进程
result_queue = queue.Queue()
def run_process(data, file_registry, file_lock, result_queue):
with app.app_context(): # 设置应用上下文
result = process_ffmpeg(data, file_registry, file_lock)
result_queue.put(result)
thread = threading.Thread(target=run_process, args=(data, file_registry, file_lock, result_queue))
thread.start()
# 等待处理结果
result = result_queue.get()
if 'error' in result:
logging.error(f"处理过程中出错: {result['error']}")
return jsonify({'status': 'error','error': result['error']}), 500
else:
logging.info(f"处理成功返回下载URL: {result['download_url']}")
return jsonify({
'status': 'success',
'download_url': result['download_url'],
'file_id': result['file_id']
}), 200
@app.route('/api/sanjuuni', methods=['POST'])
def sanjuuni_api():
return jsonify({'status': 'error','error': '暂未开放'}), 403
@app.route('/download/<file_id>/<filename>', methods=['GET'])
def download_file_endpoint(file_id, filename):
logging.info(f"收到文件下载请求 - 文件ID: {file_id}, 文件名: {filename}")
with file_lock:
if file_id not in file_registry:
logging.warning(f"文件ID: {file_id} 不存在")
return jsonify({'error': '文件不存在'}), 404
file_info = file_registry[file_id]
file_info['last_access'] = time.time()
file_info['download_count'] += 1
try:
with open(file_info['path'], 'rb') as f:
file_data = f.read()
return file_data, 200, {'Content-Disposition': f'attachment; filename={filename}'}
except Exception as e:
logging.error(f"下载文件时出错: {e}")
return jsonify({'status': 'error','error': str(e)}), 500
if __name__ == '__main__':
logging.basicConfig(level=logging.INFO)
start_cleanup_thread()
app.run(debug=True)