上传main
This commit is contained in:
parent
70da8b17a5
commit
967a3ad705
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
.history
|
||||
__pycache__/
|
||||
app.log
|
117
ffmpeg_utils.py
Normal file
117
ffmpeg_utils.py
Normal 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
52
file_cleanup.py
Normal 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
BIN
lib/ffmpeg/bin/ffmpeg
Normal file
Binary file not shown.
BIN
lib/ffmpeg/bin/ffmpeg.exe
Normal file
BIN
lib/ffmpeg/bin/ffmpeg.exe
Normal file
Binary file not shown.
BIN
lib/ffmpeg/bin/ffplay
Normal file
BIN
lib/ffmpeg/bin/ffplay
Normal file
Binary file not shown.
BIN
lib/ffmpeg/bin/ffplay.exe
Normal file
BIN
lib/ffmpeg/bin/ffplay.exe
Normal file
Binary file not shown.
BIN
lib/ffmpeg/bin/ffprobe
Normal file
BIN
lib/ffmpeg/bin/ffprobe
Normal file
Binary file not shown.
BIN
lib/ffmpeg/bin/ffprobe.exe
Normal file
BIN
lib/ffmpeg/bin/ffprobe.exe
Normal file
Binary file not shown.
26
logging_config.py
Normal file
26
logging_config.py
Normal 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
104
main.py
@ -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)
|
Loading…
x
Reference in New Issue
Block a user