diff --git a/CHANGELOG.md b/CHANGELOG.md index f5a244c3..f051cdb2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ 本项目所有重要变更记录于此。格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),遵循 [语义化版本](https://semver.org/lang/zh-CN/)。 +## [Unreleased] + +### Fixed + +- **快手 ASR 接口下线后用户看到无信息量的「未知错误」**:快手官方已永久关闭 `ai.kuaishou.com/api/effects/subtitle_generate`(返回 `code=501, msg=效果subtitle_generate禁用`),旧实现默默地把这条 501 错误冒泡出去,前端只能展示「快手 API 返回错误: 未知错误」之类的笼统提示,用户无从得知是该接口本身已下线。`KuaishouTranscriber._submit` 现在直接在入口处抛 `RuntimeError` 并提示用户切换到 bcut 或 Faster Whisper,避免无谓的上传带宽消耗。 + ## [2.4.4] - 2026-06-23 ### Security diff --git a/backend/app/transcriber/kuaishou.py b/backend/app/transcriber/kuaishou.py index e2846b1f..6c1fe11b 100644 --- a/backend/app/transcriber/kuaishou.py +++ b/backend/app/transcriber/kuaishou.py @@ -26,39 +26,12 @@ def _load_file(self, file_path: str) -> bytes: def _submit(self, file_path: str) -> dict: """提交识别请求""" - try: - file_binary = self._load_file(file_path) - - payload = { - "typeId": "1" - } - - # 使用文件名作为上传文件名 - file_name = os.path.basename(file_path) - files = [('file', (file_name, file_binary, 'audio/mpeg'))] - - logger.info(f"开始向快手API提交请求,文件: {file_name}") - response = requests.post(self.API_URL, data=payload, files=files, timeout=300) - response.raise_for_status() # 检查HTTP错误 - - result = response.json() - print('result',result) - # 检查快手API返回是否包含错误 - if "data" not in result or result.get("code", 0) != 0: - error_msg = f"快手API返回错误: {result.get('message', '未知错误')}" - logger.error(error_msg) - raise Exception(error_msg) - - return result - - except requests.exceptions.RequestException as e: - error_msg = f"快手ASR请求网络错误: {str(e)}" - logger.error(error_msg) - raise - except Exception as e: - error_msg = f"快手ASR请求处理错误: {str(e)}" - logger.error(error_msg) - raise + # 快手 ASR API 已于 2025 年永久关停 (code=501, msg="效果subtitle_generate禁用"), + # 请使用 bcut(B站必剪)或 fast-whisper(本地)替代。 + raise RuntimeError( + "快手 ASR 转写接口已永久关停(快手官方已禁用 subtitle_generate API)。" + "请在「设置 → 音频转写配置」中将转写引擎切换为「必剪(bcut)」或「Faster Whisper(本地)」。" + ) @timeit def transcript(self, file_path: str) -> TranscriptResult: diff --git a/backend/tests/test_kuaishou_disabled.py b/backend/tests/test_kuaishou_disabled.py new file mode 100644 index 00000000..4ca744a5 --- /dev/null +++ b/backend/tests/test_kuaishou_disabled.py @@ -0,0 +1,49 @@ +""" +Coverage for the Kuaishou ASR disabled-state guard. + +Background: 快手 (Kuaishou) 关闭了对外开放的 subtitle_generate 语音识别接口, +任何调用都会立刻返回 ``{"code": 501, "msg": "效果subtitle_generate禁用"}``。 +旧实现默默地把这条错误抛出去,前端没有针对性的提示,用户只能从 +"未知错误" 里猜原因。 + +本次改动在 KuaishouTranscriber._submit 开头直接抛带操作指引的 +``RuntimeError``,而不是花时间把文件 POST 上去再被 501 拒掉。 +""" +import pytest + +from app.transcriber import kuaishou as kuaishou_module +from app.transcriber.kuaishou import KuaishouTranscriber + + +def test_kuaishou_submit_raises_clear_error(tmp_path): + """_submit should raise immediately with a message that names the replacement engines.""" + f = tmp_path / "audio.mp3" + f.write_bytes(b"fake") + + t = KuaishouTranscriber() + with pytest.raises(RuntimeError) as exc: + t._submit(str(f)) + msg = str(exc.value) + # 用户需要能根据这条提示自助切换到 bcut / faster-whisper + assert "快手" in msg or "Kuaishou" in msg + assert "已" in msg or "关闭" in msg or "禁用" in msg + assert "bcut" in msg or "必剪" in msg + assert "whisper" in msg.lower() or "Faster Whisper" in msg + + +def test_kuaishou_submit_does_not_make_network_call(monkeypatch, tmp_path): + """短路上线后,不应再发起网络请求 (避免无谓的上传带宽消耗).""" + called = {"n": 0} + + def fake_post(*args, **kwargs): + called["n"] += 1 + raise AssertionError("submit() should not reach requests.post after the shutdown guard") + + monkeypatch.setattr(kuaishou_module.requests, "post", fake_post) + f = tmp_path / "audio.mp3" + f.write_bytes(b"fake") + + t = KuaishouTranscriber() + with pytest.raises(RuntimeError): + t._submit(str(f)) + assert called["n"] == 0