small-packages/luci-app-netspeedtest/root/usr/bin/netspeedtest.sh
2026-05-10 09:48:30 +08:00

338 lines
8.8 KiB
Bash

#!/bin/bash
# Copyright (C) 2019-2026 sirpdboy
# Fixed version - correct image URL handling
RESULT_FILE='/tmp/netspeedtest_result'
LOG_FILE='/tmp/netspeedtest.log'
LOCK_FILE='/var/run/netspeedtest.lock'
PID_FILE='/var/run/netspeedtest.pid'
TIMEOUT=120
# 默认设置
PREFERRED_VERSION="auto"
SELECTED_SERVER="auto"
# 日志函数
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"
}
# 检查进程锁
check_running() {
if [ -f "$PID_FILE" ]; then
local old_pid=$(cat "$PID_FILE" 2>/dev/null)
if kill -0 "$old_pid" 2>/dev/null; then
log "Another speedtest is already running (PID: $old_pid)"
echo "Testing" > "$RESULT_FILE"
exit 1
else
rm -f "$PID_FILE"
fi
fi
exec 200>"$LOCK_FILE"
if ! flock -n 200; then
log "Another speedtest is already running (flock)"
echo "Testing" > "$RESULT_FILE"
exit 1
fi
echo $$ > "$PID_FILE"
}
# 清理函数
cleanup() {
rm -f "$PID_FILE"
flock -u 200 2>/dev/null
exec 200>&-
}
trap cleanup EXIT INT TERM
# 检测可用的speedtest版本
detect_speedtest() {
local preferred="$1"
local available_versions=""
# 检查Ookla官方版
if [ -f '/usr/bin/ookla-speedtest' ] && [ -x '/usr/bin/ookla-speedtest' ]; then
available_versions="ookla:/usr/bin/ookla-speedtest"
fi
# 检查Python版
if [ -f '/usr/bin/speedtest' ] && [ -x '/usr/bin/speedtest' ]; then
if [ -n "$available_versions" ]; then
available_versions="$available_versions|python:/usr/bin/speedtest"
else
available_versions="python:/usr/bin/speedtest"
fi
fi
# 如果没有找到任何版本
if [ -z "$available_versions" ]; then
log "No speedtest versions found"
return 1
fi
# 如果指定了偏好版本
if [ "$preferred" != "auto" ]; then
IFS='|' read -ra versions <<< "$available_versions"
for ver in "${versions[@]}"; do
if [[ "$ver" == "$preferred:"* ]]; then
echo "$ver"
return 0
fi
done
fi
echo "$available_versions" | cut -d'|' -f1
return 0
}
# 运行Ookla版测试
run_ookla_test() {
local speedtest_bin=$1
local server_id=$2
local cmd="$speedtest_bin --accept-gdpr --accept-license --progress=no"
[ -n "$server_id" ] && [ "$server_id" != "auto" ] && cmd="$cmd --server-id=$server_id"
# 使用临时文件存储输出
local tmp_output=$(mktemp)
if command -v timeout >/dev/null 2>&1; then
timeout $TIMEOUT $cmd > "$tmp_output" 2>&1
local exit_code=$?
if [ $exit_code -eq 124 ]; then
log "Error: Test timed out"
rm -f "$tmp_output"
return 1
fi
else
$cmd > "$tmp_output" 2>&1
fi
cat "$tmp_output"
rm -f "$tmp_output"
return 0
}
# 运行Python版测试
run_python_test() {
local speedtest_bin=$1
local server_id=$2
local cmd="$speedtest_bin --share --simple"
if [ -n "$server_id" ] && [ "$server_id" != "auto" ]; then
cmd="$speedtest_bin --server $server_id --share --simple"
fi
local tmp_output=$(mktemp)
if command -v timeout >/dev/null 2>&1; then
timeout 90 $cmd > "$tmp_output" 2>&1
local exit_code=$?
if [ $exit_code -eq 124 ]; then
log "Error: Python test timed out"
# 尝试使用常用服务器
timeout 90 $speedtest_bin --server 59016 --share --simple > "$tmp_output" 2>&1
if [ $? -eq 124 ]; then
log "Error: Second attempt also timed out"
rm -f "$tmp_output"
return 1
fi
fi
else
$cmd > "$tmp_output" 2>&1
fi
cat "$tmp_output"
rm -f "$tmp_output"
return 0
}
# 解析Python版结果
parse_python_result() {
local result="$1"
# 检查是否包含错误信息
if echo "$result" | grep -q -i "error\|failed\|cannot\|timeout"; then
return 1
fi
# 提取分享链接
local share_url=$(echo "$result" | grep -o 'http://www.speedtest.net/result/[0-9]*\.png' | head -1)
if [ -n "$share_url" ]; then
echo "$share_url"
return 0
fi
# 提取速度数据
local download=$(echo "$result" | grep -i "download" | head -1)
local upload=$(echo "$result" | grep -i "upload" | head -1)
local latency=$(echo "$result" | grep -i "latency" | head -1)
if [ -n "$download" ] && [ -n "$upload" ]; then
{
echo "$download"
echo "$upload"
[ -n "$latency" ] && echo "$latency"
}
return 0
fi
return 1
}
parse_ookla_result() {
local result="$1"
# 提取结果URL
local result_url=$(echo "$result" | grep -i "result url" | grep -o 'https\?://[^ ]*' | head -1)
if [ -n "$result_url" ]; then
echo "$result_url.png"
return 0
fi
local download=$(echo "$result" | grep -i "download" | grep -o "[0-9.]\+ [Mk]bits" | head -1)
local upload=$(echo "$result" | grep -i "upload" | grep -o "[0-9.]\+ [Mk]bits" | head -1)
local latency=$(echo "$result" | grep -i "latency" | grep -o "[0-9.]\+ ms" | head -1)
if [ -n "$download" ] && [ -n "$upload" ]; then
{
echo "Download: $download"
echo "Upload: $upload"
[ -n "$latency" ] && echo "Latency: $latency"
}
return 0
fi
return 1
}
# 主测试函数
main_test() {
log "=== Speedtest started (PID: $$) ==="
# 检测可用的speedtest版本
local version_info=$(detect_speedtest "$PREFERRED_VERSION")
if [ -z "$version_info" ]; then
log "Error: No speedtest version found"
echo "Test failed" > "$RESULT_FILE"
exit 1
fi
local version_type=${version_info%:*}
local speedtest_bin=${version_info#*:}
log "Using $version_type version: $speedtest_bin"
TEST_SUCCESS=0
# 运行测试
case $version_type in
ookla)
RUNTEST=$(run_ookla_test "$speedtest_bin" "$SELECTED_SERVER")
;;
python)
RUNTEST=$(run_python_test "$speedtest_bin" "$SELECTED_SERVER")
;;
esac
echo "$RUNTEST" >> "$LOG_FILE"
# 解析结果
case $version_type in
ookla) RESULT=$(parse_ookla_result "$RUNTEST") ;;
python) RESULT=$(parse_python_result "$RUNTEST") ;;
esac
if [ -n "$RESULT" ]; then
echo "$RESULT" > "$RESULT_FILE"
TEST_SUCCESS=1
log "Test successful"
fi
# 如果失败,尝试自动选择
if [ $TEST_SUCCESS -eq 0 ] && [ "$SELECTED_SERVER" != "auto" ]; then
log "Test failed with selected server, trying automatic server selection"
SELECTED_SERVER="auto"
case $version_type in
ookla)
RUNTEST=$(run_ookla_test "$speedtest_bin" "auto")
;;
python)
RUNTEST=$(run_python_test "$speedtest_bin" "auto")
;;
esac
echo "$RUNTEST" >> "$LOG_FILE"
case $version_type in
ookla) RESULT=$(parse_ookla_result "$RUNTEST") ;;
python) RESULT=$(parse_python_result "$RUNTEST") ;;
esac
if [ -n "$RESULT" ]; then
echo "$RESULT" > "$RESULT_FILE"
log "Test successful with automatic server selection"
TEST_SUCCESS=1
fi
fi
# 最终检查
if [ $TEST_SUCCESS -eq 0 ]; then
echo "Test failed" > "$RESULT_FILE"
log "All tests failed"
fi
log "=== Speedtest completed (PID: $$) ==="
echo "" >> "$LOG_FILE"
}
# 显示帮助信息
show_help() {
echo "Usage: $0 [OPTIONS]"
echo "Options:"
echo " --version VERSION Preferred version (ookla/python/auto)"
echo " --server SERVER_ID Server ID to test with"
echo " --help Show this help message"
}
# 命令行参数处理
while [ $# -gt 0 ]; do
case "$1" in
--version)
if [ -n "$2" ]; then
PREFERRED_VERSION="$2"
shift 2
else
echo "Error: --version requires an argument"
exit 1
fi
;;
--server)
if [ -n "$2" ]; then
SELECTED_SERVER="$2"
shift 2
else
echo "Error: --server requires an argument"
exit 1
fi
;;
--help)
show_help
exit 0
;;
*)
echo "Unknown option: $1"
show_help
exit 1
;;
esac
done
# 运行测试
check_running
main_test
exit 0