Files
autoclip/backend/tasks/upload.py

202 lines
8.1 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
投稿相关Celery任务
"""
import logging
from pathlib import Path
from sqlalchemy.orm import Session
from ..core.celery_app import celery_app
from ..core.database import SessionLocal
from ..services.bilibili_service import BilibiliUploadService
from ..core.path_utils import get_project_output_directory
logger = logging.getLogger(__name__)
@celery_app.task(bind=True, name='backend.tasks.upload.upload_clip_task')
def upload_clip_task(self, record_id: str, clip_id: str):
"""上传切片任务"""
db = SessionLocal()
try:
logger.info(f"开始上传切片: record_id={record_id}, clip_id={clip_id}")
# 获取投稿服务
upload_service = BilibiliUploadService(db)
# 转换record_id为整数类型
try:
record_id_int = int(record_id)
except ValueError:
raise ValueError(f"无效的record_id格式: {record_id}")
# 获取投稿记录
upload_record = upload_service.get_upload_record_by_id(record_id_int)
if not upload_record:
raise ValueError(f"投稿记录不存在: {record_id}")
# 构建视频文件路径
project_output_dir = get_project_output_directory(str(upload_record.project_id))
logger.info(f"项目输出目录: {project_output_dir}")
# 获取clip信息以匹配正确的文件名
from ..models.clip import Clip
clip = db.query(Clip).filter(Clip.id == clip_id).first()
if not clip:
raise ValueError(f"切片记录不存在: {clip_id}")
clip_title = clip.title or clip.generated_title or ""
logger.info(f"切片标题: {clip_title}")
# 尝试多种可能的文件命名模式
possible_paths = [
project_output_dir / "clips" / f"{clip_id}.mp4", # 标准命名
project_output_dir / "clips" / f"{clip_id}_clip_{clip_id}.mp4", # 带clip后缀的命名
project_output_dir / "clips" / f"{clip_id}_clip.mp4", # 简化clip后缀
]
# 如果clip有标题尝试通过标题匹配
if clip_title:
# 清理标题中的特殊字符,用于文件名匹配
import re
clean_title = re.sub(r'[<>:"/\\|?*]', '', clip_title)
possible_paths.extend([
project_output_dir / "clips" / f"{clean_title}.mp4",
project_output_dir / "clips" / f"*{clean_title}*.mp4",
])
logger.info(f"尝试查找文件,可能的路径: {[str(p) for p in possible_paths]}")
# 查找视频文件
video_path = None
for path in possible_paths:
if path.exists():
video_path = path
logger.info(f"找到视频文件: {video_path}")
break
if not video_path:
# 如果标准路径都没找到尝试在clips目录下查找所有mp4文件
clips_dir = project_output_dir / "clips"
if clips_dir.exists():
mp4_files = list(clips_dir.glob("*.mp4"))
logger.info(f"clips目录下的所有mp4文件: {[str(f) for f in mp4_files]}")
# 如果只有一个mp4文件就使用它
if len(mp4_files) == 1:
video_path = mp4_files[0]
logger.info(f"使用唯一的mp4文件: {video_path}")
else:
# 尝试通过标题匹配文件名
if clip_title:
for mp4_file in mp4_files:
# 检查文件名是否包含标题的关键词
if any(keyword in mp4_file.name for keyword in clip_title.split()[:3]): # 使用标题的前3个词
video_path = mp4_file
logger.info(f"通过标题匹配找到: {video_path}")
break
# 如果还是没找到尝试通过clip_id匹配
if not video_path:
for mp4_file in mp4_files:
if clip_id in mp4_file.name:
video_path = mp4_file
logger.info(f"通过clip_id匹配找到: {video_path}")
break
if not video_path:
raise FileNotFoundError(f"未找到切片视频文件: {clip_id}")
# 检查文件大小
file_size = video_path.stat().st_size
logger.info(f"视频文件大小: {file_size} bytes")
if file_size == 0:
raise ValueError("视频文件为空")
# 执行上传
logger.info(f"开始上传视频: {video_path}")
success = upload_service.upload_clip_sync(record_id_int, str(video_path))
if success:
logger.info(f"切片上传成功: {clip_id}")
upload_service.update_upload_status(record_id_int, "success")
else:
logger.error(f"切片上传失败: {clip_id}")
upload_service.update_upload_status(record_id_int, "failed", "上传失败")
except Exception as e:
logger.error(f"上传切片任务失败: {str(e)}")
upload_service.update_upload_status(record_id_int, "failed", str(e))
raise
finally:
db.close()
@celery_app.task(bind=True, name='backend.tasks.upload.upload_project_task')
def upload_project_task(self, record_id: str, clip_ids: list):
"""上传项目任务"""
db = SessionLocal()
try:
logger.info(f"开始上传项目: record_id={record_id}, clip_ids={clip_ids}")
# 获取投稿服务
upload_service = BilibiliUploadService(db)
# 转换record_id为整数类型
try:
record_id_int = int(record_id)
except ValueError:
raise ValueError(f"无效的record_id格式: {record_id}")
# 获取投稿记录
upload_record = upload_service.get_upload_record_by_id(record_id_int)
if not upload_record:
raise ValueError(f"投稿记录不存在: {record_id}")
# 构建视频文件路径
project_output_dir = get_project_output_directory(str(upload_record.project_id))
logger.info(f"项目输出目录: {project_output_dir}")
# 查找所有切片文件
clips_dir = project_output_dir / "clips"
if not clips_dir.exists():
raise FileNotFoundError(f"clips目录不存在: {clips_dir}")
# 获取所有mp4文件
mp4_files = list(clips_dir.glob("*.mp4"))
logger.info(f"找到的mp4文件: {[str(f) for f in mp4_files]}")
if not mp4_files:
raise FileNotFoundError("未找到任何mp4文件")
# 如果只有一个文件,直接上传
if len(mp4_files) == 1:
video_path = mp4_files[0]
logger.info(f"单个文件上传: {video_path}")
# 检查文件大小
file_size = video_path.stat().st_size
if file_size == 0:
raise ValueError("视频文件为空")
# 执行上传
success = upload_service.upload_clip(record_id_int, str(video_path))
if success:
logger.info("项目上传成功")
upload_service.update_upload_status(record_id_int, "success")
else:
logger.error("项目上传失败")
upload_service.update_upload_status(record_id_int, "failed", "上传失败")
else:
# 多个文件的情况,这里可以扩展为合并上传或分别上传
logger.warning(f"发现多个视频文件,当前只支持单个文件上传: {len(mp4_files)}")
upload_service.update_upload_status(record_id_int, "failed", "暂不支持多文件上传")
except Exception as e:
logger.error(f"上传项目任务失败: {str(e)}")
upload_service.update_upload_status(record_id_int, "failed", str(e))
raise
finally:
db.close()