diff --git a/dsLightRag/JiMeng/Kit/JmImg2VideoUtil.py b/dsLightRag/JiMeng/Kit/JmImg2VideoUtil.py new file mode 100644 index 00000000..5edebe56 --- /dev/null +++ b/dsLightRag/JiMeng/Kit/JmImg2VideoUtil.py @@ -0,0 +1,182 @@ +import json +import time +import logging +from JiMeng.Kit.JmCommon import JmCommon +from JiMeng.Kit.JmErrorCode import JmErrorCode + +# 配置日志 +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') +logger = logging.getLogger('JmImg2Video') + +class JmImg2Video(JmCommon): + req_key = "jimeng_vgfm_i2v_l20" + action = "CVSync2AsyncSubmitTask" + default_max_retries = 10000 # 最大查询次数 + default_retry_interval = 3000 # 查询间隔(毫秒) + + def __init__(self, max_retries=None, retry_interval=None): + """ + 初始化JmImg2Video实例 + + :param max_retries: 最大查询次数,默认使用类的默认值 + :param retry_interval: 查询间隔(毫秒),默认使用类的默认值 + """ + self.max_retries = max_retries if max_retries is not None else self.default_max_retries + self.retry_interval = retry_interval if retry_interval is not None else self.default_retry_interval + + def create_video_task(self, image_urls, prompt): + """ + 根据首帧图片和分镜头脚本生成视频任务 + + :param image_urls: 图片URL列表 + :param prompt: 提示词(分镜头脚本) + :return: 任务ID + :raises Exception: 任务创建失败时抛出异常 + """ + # 创建请求体 + req = { + "req_key": self.req_key, + "image_urls": image_urls, + "prompt": prompt + } + + try: + response_body = self.do_request( + method="POST", + query_list={}, + body=json.dumps(req).encode('utf-8'), + action=self.action + ) + result = json.loads(response_body) + code = result.get("code") + + if not JmErrorCode.is_success(code): + error_msg = f"提交任务失败: 错误码={code}, 错误信息={JmErrorCode.get_message_by_code(code)}" + logger.error(error_msg) + raise Exception(error_msg) + + # 获取任务ID + task_id = result.get("data", {}).get("task_id") + if not task_id: + error_msg = "提交任务成功但未返回任务ID" + logger.error(error_msg) + raise Exception(error_msg) + + logger.info(f"任务创建成功,任务ID: {task_id}") + return task_id + + except Exception as e: + logger.error(f"创建视频任务异常: {str(e)}", exc_info=True) + raise + + def query_task_status(self, task_id): + """ + 根据任务ID查询视频生成状态 + + :param task_id: 视频生成任务ID + :return: 字典,包含任务状态信息 + - status: 'completed' 表示完成, 'processing' 表示处理中, 'failed' 表示失败 + - video_url: 视频URL,如果任务完成 + - error_msg: 错误信息,如果任务失败 + """ + try: + result = self.query_task_result(task_id) + code = result.get("code") + + if not JmErrorCode.is_success(code): + error_msg = f"查询任务失败: 错误码={code}, 错误信息={JmErrorCode.get_message_by_code(code)}" + logger.error(error_msg) + return { + "status": "failed", + "error_msg": error_msg + } + + data = result.get("data", {}) + if data and data.get("video_url"): + video_url = data.get("video_url") + logger.info(f"任务已完成,视频地址: {video_url}") + return { + "status": "completed", + "video_url": video_url + } + else: + logger.info(f"任务处理中,任务ID: {task_id}") + return { + "status": "processing" + } + + except Exception as e: + logger.error(f"查询任务状态异常: {str(e)}", exc_info=True) + return { + "status": "failed", + "error_msg": str(e) + } + + def wait_for_task_completion(self, task_id, download_path=None): + """ + 等待任务完成并可选地下载视频 + + :param task_id: 视频生成任务ID + :param download_path: 视频下载路径,如果为None则不下载 + :return: 视频URL,如果任务成功完成 + :raises Exception: 任务失败或超时 + """ + query_retry_count = 0 + + while query_retry_count < self.max_retries: + status_info = self.query_task_status(task_id) + + if status_info["status"] == "completed": + video_url = status_info["video_url"] + if download_path: + logger.info(f"开始下载视频到: {download_path}") + self.download_file(video_url, download_path) + logger.info(f"视频已下载完成") + return video_url + elif status_info["status"] == "failed": + raise Exception(f"任务失败: {status_info.get('error_msg', '未知错误')}") + else: + logger.info(f"任务处理中,等待{self.retry_interval}毫秒后重试...") + time.sleep(self.retry_interval / 1000) + query_retry_count += 1 + + error_msg = f"任务查询超时,已达到最大查询次数: {self.max_retries}" + logger.error(error_msg) + raise Exception(error_msg) + + +def main(): + try: + # 示例用法 + image_urls = [ + "https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/Backup/Text2Img.jpg" + ] + prompt = """ + |镜号|运镜|画面内容| +| :---: | :---: | --- | +|1|固定镜头|穿着黄色安全帽、反光背心的工人背着小女孩,左手抱着小熊玩偶,两人脸上洋溢着幸福的笑容,背景是老旧居民楼和湿漉漉的路面。| +|2|推镜头|镜头从两人全身慢慢推近至两人上半身,聚焦两人亲密温馨的神态。| +|3|平移镜头|镜头从两人正面平移至侧面,展现工人的朴实与小女孩的可爱。| +|4|拉镜头|镜头逐渐拉远,展现两人在居民楼间道路上的整体场景。| +|5|固定镜头|定格在两人互动的画面,突出亲情的温暖氛围。| + """ + + # 创建实例 + img2video = JmImg2Video(max_retries=100, retry_interval=2000) + + # 创建视频任务 + task_id = img2video.create_video_task(image_urls, prompt) + print(f"任务ID: {task_id}") + + # 等待任务完成并下载视频 + mp4_file_name = "image2video.mp4" + save_video_path = fr"D:\dsWork\dsProject\dsLightRag\JiMeng\{mp4_file_name}" + video_url = img2video.wait_for_task_completion(task_id, save_video_path) + print(f"视频生成完成,地址: {video_url}") + + except Exception as e: + logger.error(f"程序执行异常: {str(e)}", exc_info=True) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/dsLightRag/JiMeng/Kit/__pycache__/JmImg2VideoUtil.cpython-310.pyc b/dsLightRag/JiMeng/Kit/__pycache__/JmImg2VideoUtil.cpython-310.pyc new file mode 100644 index 00000000..03eeb86e Binary files /dev/null and b/dsLightRag/JiMeng/Kit/__pycache__/JmImg2VideoUtil.cpython-310.pyc differ diff --git a/dsLightRag/JiMeng/T3_JmImg2Video.py b/dsLightRag/JiMeng/T3_JmImg2Video.py deleted file mode 100644 index 09a5a7c0..00000000 --- a/dsLightRag/JiMeng/T3_JmImg2Video.py +++ /dev/null @@ -1,107 +0,0 @@ -import json -import time -import logging -from JiMeng.Kit.JmCommon import JmCommon -from JiMeng.Kit.JmErrorCode import JmErrorCode - -# 配置日志 -logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') -logger = logging.getLogger('JmImg2Video') - -class JmImg2Video(JmCommon): - req_key = "jimeng_vgfm_i2v_l20" - action = "CVSync2AsyncSubmitTask" - - @staticmethod - def submit_image_to_video_task(image_urls, prompt): - """ - 提交图生视频任务(使用图片URL) - - :param image_urls: 图片URL列表 - :param prompt: 提示词 - :return: 任务结果 - :raises Exception: 异常信息 - """ - # 创建请求体 - req = { - "req_key": JmImg2Video.req_key, - "image_urls": image_urls, - "prompt": prompt - } - - response_body = JmCommon.do_request( - method="POST", - query_list={}, # 添加空字典作为query_list参数 - body=json.dumps(req).encode('utf-8'), - action=JmImg2Video.action - ) - return json.loads(response_body) - - -def main(): - try: - # 参考: https://www.volcengine.com/docs/85621/1544774 - image_urls = [ - "https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/Backup/Text2Img.jpg" - ] - prompt = """ - |镜号|运镜|画面内容| -| :---: | :---: | --- | -|1|固定镜头|穿着黄色安全帽、反光背心的工人背着小女孩,左手抱着小熊玩偶,两人脸上洋溢着幸福的笑容,背景是老旧居民楼和湿漉漉的路面。| -|2|推镜头|镜头从两人全身慢慢推近至两人上半身,聚焦两人亲密温馨的神态。| -|3|平移镜头|镜头从两人正面平移至侧面,展现工人的朴实与小女孩的可爱。| -|4|拉镜头|镜头逐渐拉远,展现两人在居民楼间道路上的整体场景。| -|5|固定镜头|定格在两人互动的画面,突出亲情的温暖氛围。| - """ - - mp4_file_name = "image2video.mp4" - submit_result = JmImg2Video.submit_image_to_video_task(image_urls, prompt) - logger.info(f"提交结果: {submit_result}") - code = submit_result.get("code") - if not JmErrorCode.is_success(code): - logger.error(f"提交任务失败: 错误码={code}, 错误信息={JmErrorCode.get_message_by_code(code)}") - return - - # 获取任务ID - task_id = submit_result.get("data", {}).get("task_id") - logger.info(f"任务ID: {task_id}") - - # 检查任务是不是已经结束 - query_retry_count = 0 - max_query_retries = 10000 # 最大查询次数 - query_retry_interval = 3000 # 查询间隔(毫秒) - - while query_retry_count < max_query_retries: - result = JmCommon.query_task_result(task_id) - logger.info(f"查询结果: {result}") - - code = result.get("code") - if not JmErrorCode.is_success(code): - logger.error(f"查询失败: 错误码={code}, 错误信息={JmErrorCode.get_message_by_code(code)}") - break - - data = result.get("data") - if data and data.get("video_url"): - video_url = data.get("video_url") - logger.info(f"视频地址: {video_url}") - - # 下载视频 - save_video_path = fr"D:\dsWork\dsProject\dsLightRag\JiMeng\{mp4_file_name}" - logger.info("开始下载视频...") - JmCommon.download_file(video_url, save_video_path) - logger.info(f"视频已下载到: {save_video_path}") - break - else: - logger.info(f"任务处理中,等待{query_retry_interval}毫秒后重试...") - time.sleep(query_retry_interval / 1000) - query_retry_count += 1 - - if query_retry_count >= max_query_retries: - logger.error(f"任务查询超时,已达到最大查询次数: {max_query_retries}") - - except Exception as e: - logger.error(f"程序执行异常: {str(e)}", exc_info=True) - - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/dsLightRag/Routes/JiMengRoute.py b/dsLightRag/Routes/JiMengRoute.py index 839af5cb..70e611bf 100644 --- a/dsLightRag/Routes/JiMengRoute.py +++ b/dsLightRag/Routes/JiMengRoute.py @@ -3,11 +3,10 @@ import logging import fastapi from fastapi import APIRouter from fastapi import HTTPException -from openai import AsyncOpenAI -from Config import Config -from JiMeng.Kit.JmTxt2ImgUtil import JmTxt2Img from JiMeng.Kit.FenJingTouGenerator import FenJingTouGenerator # 导入分镜头生成器 +from JiMeng.Kit.JmImg2VideoUtil import JmImg2Video # 导入视频生成工具 +from JiMeng.Kit.JmTxt2ImgUtil import JmTxt2Img # 创建路由路由器 router = APIRouter(prefix="/api/jimeng", tags=["即梦"]) @@ -15,12 +14,6 @@ router = APIRouter(prefix="/api/jimeng", tags=["即梦"]) # 配置日志 logger = logging.getLogger(__name__) -# 初始化异步 OpenAI 客户端 -client = AsyncOpenAI( - api_key=Config.ALY_LLM_API_KEY, - base_url=Config.ALY_LLM_BASE_URL -) - @router.post("/prompt_input") async def prompt_input(request: fastapi.Request): @@ -87,3 +80,68 @@ async def generate_fenjingtou(request: fastapi.Request): logger.error(f"分镜头脚本生成失败: {str(e)}") raise HTTPException(status_code=500, detail=f"分镜头脚本生成失败: {str(e)}") + +@router.post("/create_video_task") +async def create_video_task(request: fastapi.Request): + try: + data = await request.json() + image_url = data.get("image_url") + prompt = data.get("prompt") + + if not image_url: + raise HTTPException(status_code=400, detail="缺少图片地址参数") + if not prompt: + raise HTTPException(status_code=400, detail="缺少分镜头脚本参数") + + logger.info(f"收到视频任务创建请求,图片地址: {image_url},分镜头脚本: {prompt}") + + # 创建视频生成实例并调用方法 + img2video = JmImg2Video() + task_id = img2video.create_video_task([image_url], prompt) + + logger.info(f"视频任务创建成功,任务ID: {task_id}") + + return { + "code": 200, + "message": "成功", + "data": { + "task_id": task_id + } + } + except HTTPException as e: + logger.error(f"请求参数错误: {str(e.detail)}") + raise e + except Exception as e: + logger.error(f"视频任务创建失败: {str(e)}") + raise HTTPException(status_code=500, detail=f"视频任务创建失败: {str(e)}") + + +@router.post("/query_video_task") +async def query_video_task(request: fastapi.Request): + try: + data = await request.json() + task_id = data.get("task_id") + + if not task_id: + raise HTTPException(status_code=400, detail="缺少任务ID参数") + + logger.info(f"收到视频任务查询请求,任务ID: {task_id}") + + # 创建视频生成实例并调用方法 + img2video = JmImg2Video() + status_info = img2video.query_task_status(task_id) + + logger.info(f"视频任务查询成功,状态: {status_info['status']}") + + return { + "code": 200, + "message": "成功", + "data": status_info + } + except HTTPException as e: + logger.error(f"请求参数错误: {str(e.detail)}") + raise e + except Exception as e: + logger.error(f"视频任务查询失败: {str(e)}") + raise HTTPException(status_code=500, detail=f"视频任务查询失败: {str(e)}") + diff --git a/dsLightRag/Routes/__pycache__/JiMengRoute.cpython-310.pyc b/dsLightRag/Routes/__pycache__/JiMengRoute.cpython-310.pyc index 7cc3e6c6..c341e1cb 100644 Binary files a/dsLightRag/Routes/__pycache__/JiMengRoute.cpython-310.pyc and b/dsLightRag/Routes/__pycache__/JiMengRoute.cpython-310.pyc differ diff --git a/dsLightRag/static/JiMeng/image_preview.html b/dsLightRag/static/JiMeng/image_preview.html index 9c7f0352..072ed7a1 100644 --- a/dsLightRag/static/JiMeng/image_preview.html +++ b/dsLightRag/static/JiMeng/image_preview.html @@ -3,7 +3,7 @@
-