diff --git a/dsLightRag/B1_Mp4ToWav.py b/dsLightRag/B1_Mp4ToWav.py new file mode 100644 index 00000000..50b1e30b --- /dev/null +++ b/dsLightRag/B1_Mp4ToWav.py @@ -0,0 +1,49 @@ +import subprocess +from pathlib import Path + + +def convert_mp4_to_wav( + input_mp4: str, + ffmpeg_path: str = r"d:\ffmpeg\ffmpeg.exe", + output_path: str = None # 新增输出路径参数 +): + """ + 将指定MP4文件转换为WAV格式 + 参数: + input_mp4 - 输入的MP4文件路径 + ffmpeg_path - FFmpeg可执行文件路径 + output_path - 可选输出路径(可指定完整路径或目录) + """ + input_path = Path(input_mp4) + + # 处理输出路径逻辑 + if output_path: + output_path = Path(output_path) + if output_path.is_dir(): # 如果传入的是目录 + output_path = output_path / f"{input_path.stem}.wav" + output_path.parent.mkdir(parents=True, exist_ok=True) + else: + # 默认输出到wav目录 + output_dir = Path("wav") + output_dir.mkdir(exist_ok=True) + output_path = output_dir / f"{input_path.stem}.wav" + + cmd = [ + ffmpeg_path, + "-i", str(input_path), + "-acodec", "pcm_s16le", + "-ac", "1", + "-ar", "16000", + "-y", + str(output_path) # 使用处理后的输出路径 + ] + + try: + # 执行转换命令 + subprocess.run(cmd, check=True, capture_output=True) + print(f"转换成功:{input_path} -> {output_path}") + except subprocess.CalledProcessError as e: + print(f"转换失败:{e.stderr.decode('gbk')}") + except FileNotFoundError: + print(f"FFmpeg未找到,请确认路径是否正确:{ffmpeg_path}") + diff --git a/dsLightRag/B1_Start.py b/dsLightRag/B1_Start.py new file mode 100644 index 00000000..6a35f918 --- /dev/null +++ b/dsLightRag/B1_Start.py @@ -0,0 +1,21 @@ +from B1_Mp4ToWav import * +from B2_WavToText import * +from B3_TextSummarize import * + +if __name__ == '__main__': + # 1、根据云校的视频课程下载视频文件 + + # 2、调用ffmpeg将视频文件转成wav文件 + # convert_mp4_to_wav(r"D:\backup\七年级第三单元复习课.mp4", + # output_path=r"D:\backup\123.wav") + # 3、将转换完成的WAV上传到阿里云 + + # 4、调用阿里云的语音识别API,将wav文件转成文字 + #audio_url = "https://ylt.oss-cn-hangzhou.aliyuncs.com/HuangHai/123.wav" + #ShiBie(audio_url, Path("识别结果.txt")) + # 2.5元一小时,本音频文件45分钟,约2元 + + # 6、生成总结 + input_file = Path(r"D:\dsWork\QingLong\AI\音频文本.txt") + output_file = Path(r"D:\dsWork\QingLong\AI\分析结果.txt") + analyzer_action(input_file, output_file) diff --git a/dsLightRag/B2_WavToText.py b/dsLightRag/B2_WavToText.py new file mode 100644 index 00000000..b69f0f16 --- /dev/null +++ b/dsLightRag/B2_WavToText.py @@ -0,0 +1,189 @@ +# -*- coding: utf-8 -*- +import json +import time +from typing import Dict, Tuple +from pathlib import Path +from aliyunsdkcore.acs_exception.exceptions import ClientException, ServerException +from aliyunsdkcore.client import AcsClient +from aliyunsdkcore.request import CommonRequest +from Config import * +from Config.Config import ALY_AK, ALY_SK + +# 服务常量配置 +CONFIG = { + "REGION_ID": "cn-shanghai", + "PRODUCT": "nls-filetrans", + "DOMAIN": "filetrans.cn-shanghai.aliyuncs.com", + "API_VERSION": "2018-08-17", + "MAX_RETRIES": 20, + "POLL_INTERVAL": 10 +} + + +class TranscriptionError(Exception): + """自定义语音识别异常基类""" + pass + + +class APIConnectionError(TranscriptionError): + """API连接异常""" + pass + + +class TaskSubmissionError(TranscriptionError): + """任务提交异常""" + pass + + +class TaskTimeoutError(TranscriptionError): + """任务超时异常""" + pass + + +def submit_transcription_task(client: AcsClient, app_key: str, file_link: str) -> str: + """提交语音识别任务 + + :param client: 阿里云客户端实例 + :param app_key: 应用密钥 + :param file_link: 音频文件URL + :return: 任务ID + :raises TaskSubmissionError: 任务提交失败时抛出 + """ + task_config = { + "appkey": app_key, + "file_link": file_link, + "version": "4.0", + "enable_words": False + } + + request = CommonRequest() + request.set_domain(CONFIG["DOMAIN"]) + request.set_version(CONFIG["API_VERSION"]) + request.set_product(CONFIG["PRODUCT"]) + request.set_action_name("SubmitTask") + request.set_method('POST') + request.add_body_params("Task", json.dumps(task_config)) + + try: + response = client.do_action_with_exception(request) + response_data = json.loads(response) + + if response_data.get("StatusText") != "SUCCESS": + raise TaskSubmissionError(f"任务提交失败: {response_data.get('StatusText', '未知错误')}") + + return response_data["TaskId"] + + except (ServerException, ClientException) as e: + raise APIConnectionError(f"API连接异常: {str(e)}") from e + except KeyError as e: + raise TaskSubmissionError("响应缺少TaskId字段") from e + + +def poll_transcription_result(client: AcsClient, task_id: str) -> Dict: + """轮询识别任务结果 + + :param client: 阿里云客户端实例 + :param task_id: 任务ID + :return: 识别结果字典 + :raises TaskTimeoutError: 超时未完成时抛出 + """ + request = CommonRequest() + request.set_domain(CONFIG["DOMAIN"]) + request.set_version(CONFIG["API_VERSION"]) + request.set_product(CONFIG["PRODUCT"]) + request.set_action_name("GetTaskResult") + request.set_method('GET') + request.add_query_param("TaskId", task_id) + + retries = 0 + while retries < CONFIG["MAX_RETRIES"]: + try: + response = client.do_action_with_exception(request) + result = json.loads(response) + status = result.get("StatusText", "") + + if status == "SUCCESS": + return result.get("Result", {}) + if status in ("RUNNING", "QUEUEING"): + time.sleep(CONFIG["POLL_INTERVAL"]) + retries += 1 + else: + raise TaskSubmissionError(f"识别失败,状态: {status}") + + except (ServerException, ClientException) as e: + raise APIConnectionError(f"查询异常: {str(e)}") from e + + raise TaskTimeoutError(f"超过最大重试次数({CONFIG['MAX_RETRIES']}),任务未完成") + + +def save_transcription_result(result: Dict, output_path: Path) -> Path: + """保存识别结果到文件 + + :param result: 识别结果字典 + :param output_path: 输出文件路径 + :return: 实际保存路径 + :raises IOError: 文件保存失败时抛出 + """ + if not result.get('Sentences'): + raise ValueError("识别结果为空") + + try: + output_path.parent.mkdir(parents=True, exist_ok=True) + output_path.write_text(str(result['Sentences']), encoding='utf-8') + return output_path.resolve() + except (IOError, PermissionError) as e: + raise IOError(f"文件保存失败: {str(e)}") from e + + +def transcribe_audio_file( + access_key_id: str, + access_key_secret: str, + app_key: str, + audio_url: str, + output_path: Path = Path("识别结果.txt") +) -> Tuple[bool, Path, str]: + """语音识别主流程 + + :param access_key_id: 阿里云访问密钥ID + :param access_key_secret: 阿里云访问密钥 + :param app_key: 应用密钥 + :param audio_url: 音频文件URL + :param output_path: 输出文件路径 + :return: (成功标志, 输出路径, 错误信息) + """ + client = AcsClient(access_key_id, access_key_secret, CONFIG["REGION_ID"]) + + try: + task_id = submit_transcription_task(client, app_key, audio_url) + print(f"任务已提交,ID: {task_id}") + + result = poll_transcription_result(client, task_id) + saved_path = save_transcription_result(result, output_path) + + return True, saved_path, "" + + except TranscriptionError as e: + return False, Path(), str(e) + except Exception as e: + return False, Path(), f"未处理的异常: {str(e)}" + +def ShiBie(audio_url, output_path): + # 配置参数(应通过环境变量或配置文件获取) + CREDENTIALS = { + "access_key_id": ALY_AK, + "access_key_secret": ALY_SK, + "app_key": "OIpiw501l4o6MYEe", + "audio_url":audio_url, + "output_path": output_path + } + + # 执行识别 + success, path, error = transcribe_audio_file(**CREDENTIALS) + + if success: + print(f"✅ 识别成功,文件已保存至: {path}") + else: + print(f"❌ 识别失败: {error}") + exit(1) + + diff --git a/dsLightRag/B3_TextSummarize.py b/dsLightRag/B3_TextSummarize.py new file mode 100644 index 00000000..67213bac --- /dev/null +++ b/dsLightRag/B3_TextSummarize.py @@ -0,0 +1,173 @@ +# -*- coding: utf-8 -*- +from typing import Optional, Tuple, Iterator +from openai import OpenAI, APIError, APITimeoutError +import time +import httpx +from pathlib import Path +from Config.Config import LLM_API_KEY, LLM_BASE_URL, LLM_MODEL_NAME + + +class ContentAnalyzer: + """课程内容分析器(流式版本)""" + + def __init__( + self, + api_key: str = LLM_API_KEY, + base_url: str = LLM_BASE_URL, + model: str = LLM_MODEL_NAME, + max_retries: int = 10, + initial_timeout: int = 300 + ): + self._show_progress("🔧", "初始化分析器...", level=0) + self.client = OpenAI(api_key=api_key, base_url=base_url) + self.model = model + self.max_retries = max_retries + self.initial_timeout = initial_timeout + self._check_network() + self._show_progress("✅", "分析器准备就绪", level=0) + + def _show_progress(self, emoji: str, message: str, level: int = 1): + indent = " " * level + timestamp = time.strftime("%H:%M:%S") + print(f"{indent}{emoji} [{timestamp}] {message}") + + def _check_network(self): + try: + with httpx.Client(timeout=30) as client: + client.get("https://dashscope.aliyuncs.com") + self._show_progress("🌐", "网络连接正常", level=1) + except Exception as e: + self._show_progress("❌", f"网络异常: {str(e)}", level=1) + raise + + def _retry_delay(self, attempt: int) -> int: + """指数退避延迟""" + return min(2 ** attempt, 60) # 最大延迟60秒 + + def analyze_content_stream( + self, + content: str, + prompt_template: str = "帮我梳理:这节课分了几个部分,每部分的名称和开始的时间是多少:{}" + ) -> Iterator[Tuple[bool, str]]: + """流式分析内容""" + for attempt in range(self.max_retries + 1): + try: + current_timeout = self.initial_timeout + attempt * 5 + self._show_progress("⏱️", f"尝试 {attempt + 1}/{self.max_retries} (超时: {current_timeout}s)", level=2) + + full_prompt = prompt_template.format(content) + stream = self.client.chat.completions.create( + model=self.model, + messages=[{'role': 'user', 'content': full_prompt}], + timeout=current_timeout, + stream=True # 启用流式模式 + ) + + buffer = [] + for chunk in stream: + if chunk.choices and chunk.choices[0].delta.content: + content_chunk = chunk.choices[0].delta.content + buffer.append(content_chunk) + yield True, content_chunk # 实时返回每个片段 + + # 返回完整结果 + if buffer: + yield True, ''.join(buffer) + return + + except APITimeoutError as e: + if attempt < self.max_retries: + delay = self._retry_delay(attempt) + self._show_progress("⏳", f"{delay}s后重试...", level=2) + time.sleep(delay) + else: + yield False, f"API请求超时,已重试{self.max_retries}次" + return + + except APIError as e: + yield False, f"API错误: {str(e)}" + return + + except Exception as e: + yield False, f"未处理的异常: {str(e)}" + return + + def analyze_file( + self, + file_path: Path, + output_path: Optional[Path] = None, + encoding: str = 'utf-8' + ) -> Tuple[bool, str]: + """处理文件全流程(流式版本)""" + try: + self._show_progress("📂", f"开始处理文件: {file_path}", level=0) + + # 文件验证 + self._show_progress("🔍", "验证文件...", level=1) + if not file_path.exists(): + self._show_progress("❌", "文件不存在", level=2) + return False, f"文件不存在: {file_path}" + if file_path.stat().st_size > 10 * 1024 * 1024: + self._show_progress("⚠️", "注意:大文件可能影响处理速度", level=2) + + # 读取内容 + self._show_progress("📖", "读取文件内容...", level=1) + try: + content = file_path.read_text(encoding=encoding) + except UnicodeDecodeError: + self._show_progress("🔠", "解码失败,尝试GBK编码...", level=2) + content = file_path.read_text(encoding='gbk') + + # 流式分析 + self._show_progress("🧠", "开始流式分析...", level=1) + result_buffer = [] + has_error = False + error_msg = "" + + for status, chunk in self.analyze_content_stream(content): + if not status: + has_error = True + error_msg = chunk + break + print(chunk, end='', flush=True) # 实时输出 + result_buffer.append(chunk) + + if has_error: + self._show_progress("❌", f"分析失败: {error_msg}", level=1) + return False, error_msg + + final_result = ''.join(result_buffer) + + # 保存结果 + if output_path: + self._show_progress("💾", f"保存到: {output_path}", level=1) + try: + output_path.parent.mkdir(parents=True, exist_ok=True) + output_path.write_text(final_result, encoding=encoding) + self._show_progress("✅", "保存成功", level=2) + except Exception as e: + self._show_progress("❌", f"保存失败: {str(e)}", level=2) + return False, f"结果保存失败: {str(e)}" + + self._show_progress("🎉", "处理完成!", level=0) + return True, final_result + + except Exception as e: + self._show_progress("💣", f"严重错误: {str(e)}", level=1) + return False, f"文件处理失败: {str(e)}" + + +def analyzer_action(input_file, output_file): + print("\n" + "=" * 50) + print(" 🚀 长春云校视频课程智能打标记系统 ".center(50, "✨")) + print("=" * 50) + + analyzer = ContentAnalyzer(initial_timeout=300) + success, result = analyzer.analyze_file(Path(input_file), Path(output_file)) + + print("\n" + "=" * 50) + if success: + print("\n✅ 分析成功!结果已保存至:", output_file) + else: + print(f"\n❌ 分析失败:{result}") + print("=" * 50) \ No newline at end of file diff --git a/dsLightRag/Config/Config.py b/dsLightRag/Config/Config.py index 5b1a6daa..46f3b4d5 100644 --- a/dsLightRag/Config/Config.py +++ b/dsLightRag/Config/Config.py @@ -1,3 +1,7 @@ +# 阿里云的配置信息 +ALY_AK = 'LTAI5tE4tgpGcKWhbZg6C4bh' +ALY_SK = 'oizcTOZ8izbGUouboC00RcmGE8vBQ1' + # 大模型 【DeepSeek深度求索官方】 # LLM_API_KEY = "sk-44ae895eeb614aa1a9c6460579e322f1" # LLM_BASE_URL = "https://api.deepseek.com" diff --git a/dsLightRag/FlagSystem/视频课程打标记人工与大模型的对比.png b/dsLightRag/FlagSystem/视频课程打标记人工与大模型的对比.png new file mode 100644 index 00000000..b6151fb3 Binary files /dev/null and b/dsLightRag/FlagSystem/视频课程打标记人工与大模型的对比.png differ diff --git a/dsLightRag/FlagSystem/长春云校视频课程智能打标记点系统.txt b/dsLightRag/FlagSystem/长春云校视频课程智能打标记点系统.txt new file mode 100644 index 00000000..ab61451d --- /dev/null +++ b/dsLightRag/FlagSystem/长春云校视频课程智能打标记点系统.txt @@ -0,0 +1,82 @@ +# 视频课程 +# 七年级第三单元复习课 +https://yx.ccsjy.cn/ChangChunCloudSchool/index.html#/course-detail/99f50f9c61bd52f61d359e15e3903153 + + +D:\anaconda3\envs\py310\python.exe D:\dsWork\QingLong\AI\T3_TextSummarize.py + +================================================== +✨✨✨✨✨✨✨✨✨✨✨✨✨✨ 🚀 长春云校视频课程智能打标记系统 ✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨ +================================================== +🔧 [14:13:57] 初始化分析器... + 🌐 [14:13:58] 网络连接正常 +✅ [14:13:58] 分析器准备就绪 +📂 [14:13:58] 开始处理文件: D:\dsWork\QingLong\AI\音频文本.txt + 🔍 [14:13:58] 验证文件... + 📖 [14:13:58] 读取文件内容... + 🧠 [14:13:58] 开始分析... + ⏱️ [14:13:58] 尝试 1/10 (超时: 300s) + ✅ [14:16:57] 请求成功 + 💾 [14:16:57] 保存到: D:\dsWork\QingLong\AI\分析结果.txt + ✅ [14:16:57] 保存成功 +🎉 [14:16:57] 处理完成! + +================================================== +✅ 分析成功!结果如下: + +以下是本节课的结构梳理,按时间顺序分为5大部分: + +**1. 课程导入与目标说明(8,940-73,100 ms)** +- 单元复习导入(8,940-23,219) +- 单元目标说明(73,100-100,259) + 一、总结学习之道 + 二、积累成语运用 + 三、掌握默读技巧 + +**2. 任务一:温故知新(106,400-417,500 ms)** +- 核心内容: + - 四篇课文学习之道分析 + •《从百草园到三味书屋》 + •《往事依依》 + •《再塑生命的人》 + •《论语十二章》 + - 系统归纳表格梳理(366,160-417,500) + +**3. 任务二:成语积累(421,840-807,480 ms)** +- 活动一:成语展示(475,800-606,440) + - 课文成语解析 + - 论语演化成语 +- 活动二:成语运用(646,860-807,480) + - 语段填空练习 + - 片段写作训练 + +**4. 任务三:阅读方法(936,420-1,655,800 ms)** +- 默读技巧讲解(936,420-1,177,420) + - 三到原则(眼到/心到/手到) + - 关键信息捕捉 +- 朗读vs默读对比(1,188,780-1,405,180) + - 特点/作用/适用场景差异 +- 综合训练(1,407,180-1,655,800) + - 文章分析《读书声最美》 + - 阅读方法实践 + +**5. 总结与作业(1,625,580-1,959,179 ms)** +- 单元要点回顾(1,625,580-1,656,080) +- 拓展作业布置(1,656,166-1,959,179) + - 阅读《悬崖边的树》 + - 总结学习之道 + +**时间轴完整结构:** + +| 部分名称 | 开始时间(ms) | 主要内容 | +|------------------------|----------------|--------------------------------------------------------------------------| +| 课程导入与目标说明 | 8,940 | 单元概述、教学目标设定 | +| 任务一:温故知新 | 106,400 | 四篇课文学习之道深度解析 | +| 任务二:成语积累 | 421,840 | 成语知识系统梳理与运用实践 | +| 任务三:阅读方法 | 936,420 | 默读技巧训练/朗读与默读对比分析 | +| 总结与作业 | 1,625,580 | 单元知识结构化总结/拓展阅读《悬崖边的树》 | + +注:时间单位为毫秒(ms),完整课程时长约32分钟(1,959,179 ms ≈ 1,959秒) +================================================== + +进程已结束,退出代码为 0