(serverId?: number, retryCount: number = 0, useSystem: boolean = false)
| 635 | } |
| 636 | |
| 637 | async function runSpeedtest(serverId?: number, retryCount: number = 0, useSystem: boolean = false): Promise<SpeedtestResult> { |
| 638 | const MAX_RETRIES = 1; // 最多重试1次,避免无限循环 |
| 639 | |
| 640 | try { |
| 641 | // 如果要求使用系统 speedtest,则尝试系统可执行文件 |
| 642 | if (useSystem) { |
| 643 | return await runSystemSpeedtest(serverId, retryCount); |
| 644 | } |
| 645 | |
| 646 | // 检查并诊断内置可执行文件 |
| 647 | if (!fs.existsSync(SPEEDTEST_PATH)) { |
| 648 | console.log("Speedtest executable not found, downloading..."); |
| 649 | await downloadCli(); |
| 650 | } |
| 651 | |
| 652 | // 只在第一次尝试时进行诊断,避免重复诊断 |
| 653 | if (retryCount === 0) { |
| 654 | const diagnosis = await diagnoseSpeedtestExecutable(); |
| 655 | if (!diagnosis.canRun) { |
| 656 | console.log(`Speedtest executable issue detected: ${diagnosis.error}`); |
| 657 | if (diagnosis.needsReinstall) { |
| 658 | console.log("Attempting auto-fix..."); |
| 659 | await autoFixSpeedtest(); |
| 660 | } |
| 661 | } |
| 662 | } |
| 663 | |
| 664 | const serverArg = serverId ? ` -s ${serverId}` : ""; |
| 665 | const command = `"${SPEEDTEST_PATH}" --accept-license --accept-gdpr -f json${serverArg}`; |
| 666 | |
| 667 | const { stdout, stderr } = await execAsync(command, { |
| 668 | timeout: 120000 // 120秒超时 |
| 669 | }); |
| 670 | |
| 671 | if (stderr) { |
| 672 | console.log("Speedtest stderr:", stderr); |
| 673 | if (stderr.includes("NoServersException")) { |
| 674 | // 如果指定服务器不可用,尝试自动选择 |
| 675 | if (serverId) { |
| 676 | console.log(`Server ${serverId} not available, trying auto selection...`); |
| 677 | return await runSpeedtest(undefined, retryCount, useSystem); // 递归调用,不指定服务器ID,保持重试计数 |
| 678 | } |
| 679 | throw new Error("指定的服务器不可用,请尝试其他服务器或使用自动选择"); |
| 680 | } |
| 681 | if (stderr.includes("Timeout occurred")) { |
| 682 | throw new Error("网络连接超时,请检查网络状况或稍后重试"); |
| 683 | } |
| 684 | if (stderr.includes("Cannot read from socket")) { |
| 685 | throw new Error("网络连接中断,可能是网络不稳定或防火墙阻止"); |
| 686 | } |
| 687 | } |
| 688 | |
| 689 | // 尝试解析JSON结果,处理可能的部分失败情况 |
| 690 | let result: any; |
| 691 | try { |
| 692 | result = JSON.parse(stdout); |
| 693 | |
| 694 | // 检查JSON中是否包含错误信息 |
no test coverage detected